mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-22 01:15:54 +03:00
Merge tag 'v1.6.16' into sc
Change-Id: I690d21f0bac84dfed5d6f87e9c1aa30c78c8d346 Conflicts: dependencies_groups.gradle vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
This commit is contained in:
commit
abdc558c07
66 changed files with 566 additions and 2853 deletions
24
.github/workflows/sync-from-external-sources.yml
vendored
24
.github/workflows/sync-from-external-sources.yml
vendored
|
@ -58,27 +58,3 @@ jobs:
|
||||||
- Update SAS Strings from matrix-doc.
|
- Update SAS Strings from matrix-doc.
|
||||||
branch: sync-sas-strings
|
branch: sync-sas-strings
|
||||||
base: develop
|
base: develop
|
||||||
|
|
||||||
sync-analytics-plan:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
# Skip in forks
|
|
||||||
if: github.repository == 'element-hq/element-android'
|
|
||||||
# No concurrency required, runs every time on a schedule.
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Run analytics import script
|
|
||||||
run: ./tools/import_analytic_plan.sh
|
|
||||||
- name: Create Pull Request for analytics plan
|
|
||||||
uses: peter-evans/create-pull-request@v5
|
|
||||||
with:
|
|
||||||
commit-message: Sync analytics plan
|
|
||||||
title: Sync analytics plan
|
|
||||||
body: |
|
|
||||||
### Update analytics plan
|
|
||||||
Reviewers:
|
|
||||||
- [ ] Please remove usage of Event or Enum which may have been removed or updated
|
|
||||||
- [ ] please ensure new Events or new Enums are used to send analytics by pushing new commit(s) to this PR.
|
|
||||||
|
|
||||||
*Note*: Change are coming from [this project](https://github.com/matrix-org/matrix-analytics-events)
|
|
||||||
branch: sync-analytics-plan
|
|
||||||
base: develop
|
|
||||||
|
|
15
CHANGES.md
15
CHANGES.md
|
@ -1,3 +1,18 @@
|
||||||
|
Changes in Element v1.6.16 (2024-05-29)
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Bugfixes 🐛
|
||||||
|
----------
|
||||||
|
- Fix crash when accessing a local file and permission is revoked. ([#3616](https://github.com/element-hq/element-android/issues/3616))
|
||||||
|
- Fixes Element on Android 12+ being ineligible for URL deeplinks ([#5748](https://github.com/element-hq/element-android/issues/5748))
|
||||||
|
- Restore formatting when restoring a draft. Also keep formatting when switching composer mode. ([#7466](https://github.com/element-hq/element-android/issues/7466))
|
||||||
|
|
||||||
|
Other changes
|
||||||
|
-------------
|
||||||
|
- Update posthog sdk to 3.2.0 ([#8820](https://github.com/element-hq/element-android/issues/8820))
|
||||||
|
- Update Rust crypto SDK to version 0.4.1 ([#8838](https://github.com/element-hq/element-android/issues/8838))
|
||||||
|
|
||||||
|
|
||||||
Changes in Element v1.6.14 (2024-04-02)
|
Changes in Element v1.6.14 (2024-04-02)
|
||||||
=======================================
|
=======================================
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ ext.groups = [
|
||||||
'com.github.hyuwah',
|
'com.github.hyuwah',
|
||||||
'com.github.jetradarmobile',
|
'com.github.jetradarmobile',
|
||||||
'com.github.MatrixFrog',
|
'com.github.MatrixFrog',
|
||||||
|
'com.github.matrix-org',
|
||||||
'com.github.SchildiChat',
|
'com.github.SchildiChat',
|
||||||
'com.github.tapadoo',
|
'com.github.tapadoo',
|
||||||
'com.github.UnifiedPush',
|
'com.github.UnifiedPush',
|
||||||
|
@ -121,7 +122,7 @@ ext.groups = [
|
||||||
'com.parse.bolts',
|
'com.parse.bolts',
|
||||||
'com.pinterest',
|
'com.pinterest',
|
||||||
'com.pinterest.ktlint',
|
'com.pinterest.ktlint',
|
||||||
'com.posthog.android',
|
'com.posthog',
|
||||||
'com.squareup',
|
'com.squareup',
|
||||||
'com.squareup.curtains',
|
'com.squareup.curtains',
|
||||||
'com.squareup.duktape',
|
'com.squareup.duktape',
|
||||||
|
|
|
@ -17,7 +17,7 @@ We ask for the user to give consent before sending any analytics data.
|
||||||
|
|
||||||
The analytics plan is shared between all Element clients. To add an Event, please open a PR to this project: https://github.com/matrix-org/matrix-analytics-events
|
The analytics plan is shared between all Element clients. To add an Event, please open a PR to this project: https://github.com/matrix-org/matrix-analytics-events
|
||||||
|
|
||||||
Then, once the PR has been merged, you can run the tool `import_analytic_plan.sh` to import the plan to Element, and then you can use the new Event. Note that this tool is run by Github action once a week.
|
Then, once the PR has been merged, and the library is release, you can update the version of the library in the `build.gradle` file.
|
||||||
|
|
||||||
## Forks of Element
|
## Forks of Element
|
||||||
|
|
||||||
|
|
2
fastlane/metadata/android/en-US/changelogs/40106160.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40106160.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Main changes in this version: Bug fixes.
|
||||||
|
Full changelog: https://github.com/element-hq/element-android/releases
|
|
@ -31,7 +31,7 @@ class AudioPicker : Picker<MultiPickerAudioType>() {
|
||||||
* Returns selected audio files or empty list if user did not select any files.
|
* Returns selected audio files or empty list if user did not select any files.
|
||||||
*/
|
*/
|
||||||
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerAudioType> {
|
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerAudioType> {
|
||||||
return getSelectedUriList(data).mapNotNull { selectedUri ->
|
return getSelectedUriList(context, data).mapNotNull { selectedUri ->
|
||||||
selectedUri.toMultiPickerAudioType(context)
|
selectedUri.toMultiPickerAudioType(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ class FilePicker : Picker<MultiPickerBaseType>() {
|
||||||
* Returns selected files or empty list if user did not select any files.
|
* Returns selected files or empty list if user did not select any files.
|
||||||
*/
|
*/
|
||||||
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerBaseType> {
|
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerBaseType> {
|
||||||
return getSelectedUriList(data).mapNotNull { selectedUri ->
|
return getSelectedUriList(context, data).mapNotNull { selectedUri ->
|
||||||
val type = context.contentResolver.getType(selectedUri)
|
val type = context.contentResolver.getType(selectedUri)
|
||||||
|
|
||||||
when {
|
when {
|
||||||
|
|
|
@ -31,7 +31,7 @@ class ImagePicker : Picker<MultiPickerImageType>() {
|
||||||
* Returns selected image files or empty list if user did not select any files.
|
* Returns selected image files or empty list if user did not select any files.
|
||||||
*/
|
*/
|
||||||
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerImageType> {
|
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerImageType> {
|
||||||
return getSelectedUriList(data).mapNotNull { selectedUri ->
|
return getSelectedUriList(context, data).mapNotNull { selectedUri ->
|
||||||
selectedUri.toMultiPickerImageType(context)
|
selectedUri.toMultiPickerImageType(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ class MediaPicker : Picker<MultiPickerBaseMediaType>() {
|
||||||
* Returns selected image/video files or empty list if user did not select any files.
|
* Returns selected image/video files or empty list if user did not select any files.
|
||||||
*/
|
*/
|
||||||
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerBaseMediaType> {
|
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerBaseMediaType> {
|
||||||
return getSelectedUriList(data).mapNotNull { selectedUri ->
|
return getSelectedUriList(context, data).mapNotNull { selectedUri ->
|
||||||
val mimeType = context.contentResolver.getType(selectedUri)
|
val mimeType = context.contentResolver.getType(selectedUri)
|
||||||
|
|
||||||
if (mimeType.isMimeTypeVideo()) {
|
if (mimeType.isMimeTypeVideo()) {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package im.vector.lib.multipicker
|
package im.vector.lib.multipicker
|
||||||
|
|
||||||
|
import android.content.ComponentName
|
||||||
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
|
||||||
|
@ -58,7 +59,17 @@ abstract class Picker<T> {
|
||||||
uriList.forEach {
|
uriList.forEach {
|
||||||
for (resolveInfo in resInfoList) {
|
for (resolveInfo in resInfoList) {
|
||||||
val packageName: String = resolveInfo.activityInfo.packageName
|
val packageName: String = resolveInfo.activityInfo.packageName
|
||||||
|
|
||||||
|
// Replace implicit intent by an explicit to fix crash on some devices like Xiaomi.
|
||||||
|
// see https://juejin.cn/post/7031736325422186510
|
||||||
|
try {
|
||||||
context.grantUriPermission(packageName, it, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
context.grantUriPermission(packageName, it, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
data.action = null
|
||||||
|
data.component = ComponentName(packageName, resolveInfo.activityInfo.name)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return getSelectedFiles(context, data)
|
return getSelectedFiles(context, data)
|
||||||
|
@ -82,7 +93,7 @@ abstract class Picker<T> {
|
||||||
activityResultLauncher.launch(createIntent().apply { addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) })
|
activityResultLauncher.launch(createIntent().apply { addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) })
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun getSelectedUriList(data: Intent?): List<Uri> {
|
protected fun getSelectedUriList(context: Context, data: Intent?): List<Uri> {
|
||||||
val selectedUriList = mutableListOf<Uri>()
|
val selectedUriList = mutableListOf<Uri>()
|
||||||
val dataUri = data?.data
|
val dataUri = data?.data
|
||||||
val clipData = data?.clipData
|
val clipData = data?.clipData
|
||||||
|
@ -104,6 +115,6 @@ abstract class Picker<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return selectedUriList
|
return selectedUriList.onEach { context.grantUriPermission(context.applicationContext.packageName, it, Intent.FLAG_GRANT_READ_URI_PERMISSION) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ class VideoPicker : Picker<MultiPickerVideoType>() {
|
||||||
* Returns selected video files or empty list if user did not select any files.
|
* Returns selected video files or empty list if user did not select any files.
|
||||||
*/
|
*/
|
||||||
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerVideoType> {
|
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerVideoType> {
|
||||||
return getSelectedUriList(data).mapNotNull { selectedUri ->
|
return getSelectedUriList(context, data).mapNotNull { selectedUri ->
|
||||||
selectedUri.toMultiPickerVideoType(context)
|
selectedUri.toMultiPickerVideoType(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ android {
|
||||||
// that the app's state is completely cleared between tests.
|
// that the app's state is completely cleared between tests.
|
||||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||||
|
|
||||||
buildConfigField "String", "SDK_VERSION", "\"1.6.14\""
|
buildConfigField "String", "SDK_VERSION", "\"1.6.16\""
|
||||||
|
|
||||||
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
|
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
|
||||||
buildConfigField "String", "GIT_SDK_REVISION_UNIX_DATE", "\"${gitRevisionUnixDate()}\""
|
buildConfigField "String", "GIT_SDK_REVISION_UNIX_DATE", "\"${gitRevisionUnixDate()}\""
|
||||||
|
@ -215,7 +215,7 @@ dependencies {
|
||||||
|
|
||||||
implementation libs.google.phonenumber
|
implementation libs.google.phonenumber
|
||||||
|
|
||||||
implementation("org.matrix.rustcomponents:crypto-android:0.3.16")
|
implementation("org.matrix.rustcomponents:crypto-android:0.4.1")
|
||||||
// api project(":library:rustCrypto")
|
// api project(":library:rustCrypto")
|
||||||
|
|
||||||
testImplementation libs.tests.junit
|
testImplementation libs.tests.junit
|
||||||
|
|
|
@ -37,6 +37,10 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
|
||||||
import org.matrix.android.sdk.api.util.MatrixJsonParser
|
import org.matrix.android.sdk.api.util.MatrixJsonParser
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
// n.b MSC3886/MSC3903/MSC3906 that this is based on are now closed.
|
||||||
|
// However, we want to keep this implementation around for some time.
|
||||||
|
// TODO define an end-of-life date for this implementation.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of MSC3906 to sign in + E2EE set up using a QR code.
|
* Implementation of MSC3906 to sign in + E2EE set up using a QR code.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -31,8 +31,8 @@ import org.matrix.android.sdk.internal.crypto.verification.SasVerification
|
||||||
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
|
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
|
||||||
import org.matrix.android.sdk.internal.crypto.verification.prepareMethods
|
import org.matrix.android.sdk.internal.crypto.verification.prepareMethods
|
||||||
import org.matrix.rustcomponents.sdk.crypto.CryptoStoreException
|
import org.matrix.rustcomponents.sdk.crypto.CryptoStoreException
|
||||||
import org.matrix.rustcomponents.sdk.crypto.LocalTrust
|
|
||||||
import org.matrix.rustcomponents.sdk.crypto.SignatureException
|
import org.matrix.rustcomponents.sdk.crypto.SignatureException
|
||||||
|
import uniffi.matrix_sdk_crypto.LocalTrust
|
||||||
import org.matrix.rustcomponents.sdk.crypto.Device as InnerDevice
|
import org.matrix.rustcomponents.sdk.crypto.Device as InnerDevice
|
||||||
|
|
||||||
/** Class representing a device that supports E2EE in the Matrix world
|
/** Class representing a device that supports E2EE in the Matrix world
|
||||||
|
|
|
@ -75,7 +75,6 @@ import org.matrix.rustcomponents.sdk.crypto.DeviceLists
|
||||||
import org.matrix.rustcomponents.sdk.crypto.EncryptionSettings
|
import org.matrix.rustcomponents.sdk.crypto.EncryptionSettings
|
||||||
import org.matrix.rustcomponents.sdk.crypto.KeyRequestPair
|
import org.matrix.rustcomponents.sdk.crypto.KeyRequestPair
|
||||||
import org.matrix.rustcomponents.sdk.crypto.KeysImportResult
|
import org.matrix.rustcomponents.sdk.crypto.KeysImportResult
|
||||||
import org.matrix.rustcomponents.sdk.crypto.LocalTrust
|
|
||||||
import org.matrix.rustcomponents.sdk.crypto.Logger
|
import org.matrix.rustcomponents.sdk.crypto.Logger
|
||||||
import org.matrix.rustcomponents.sdk.crypto.MegolmV1BackupKey
|
import org.matrix.rustcomponents.sdk.crypto.MegolmV1BackupKey
|
||||||
import org.matrix.rustcomponents.sdk.crypto.Request
|
import org.matrix.rustcomponents.sdk.crypto.Request
|
||||||
|
@ -86,6 +85,7 @@ import org.matrix.rustcomponents.sdk.crypto.ShieldState
|
||||||
import org.matrix.rustcomponents.sdk.crypto.SignatureVerification
|
import org.matrix.rustcomponents.sdk.crypto.SignatureVerification
|
||||||
import org.matrix.rustcomponents.sdk.crypto.setLogger
|
import org.matrix.rustcomponents.sdk.crypto.setLogger
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import uniffi.matrix_sdk_crypto.LocalTrust
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -828,8 +828,14 @@ internal class OlmMachine @Inject constructor(
|
||||||
val requests = withContext(coroutineDispatchers.io) {
|
val requests = withContext(coroutineDispatchers.io) {
|
||||||
inner.bootstrapCrossSigning()
|
inner.bootstrapCrossSigning()
|
||||||
}
|
}
|
||||||
|
(requests.uploadKeysRequest)?.let {
|
||||||
|
when (it) {
|
||||||
|
is Request.KeysUpload -> requestSender.uploadKeys(it)
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
requestSender.uploadCrossSigningKeys(requests.uploadSigningKeysRequest, uiaInterceptor)
|
requestSender.uploadCrossSigningKeys(requests.uploadSigningKeysRequest, uiaInterceptor)
|
||||||
requestSender.sendSignatureUpload(requests.signatureRequest)
|
requestSender.sendSignatureUpload(requests.uploadSignatureRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -68,9 +68,9 @@ import org.matrix.android.sdk.internal.util.JsonCanonicalizer
|
||||||
import org.matrix.olm.OlmException
|
import org.matrix.olm.OlmException
|
||||||
import org.matrix.rustcomponents.sdk.crypto.Request
|
import org.matrix.rustcomponents.sdk.crypto.Request
|
||||||
import org.matrix.rustcomponents.sdk.crypto.RequestType
|
import org.matrix.rustcomponents.sdk.crypto.RequestType
|
||||||
import org.matrix.rustcomponents.sdk.crypto.SignatureState
|
|
||||||
import org.matrix.rustcomponents.sdk.crypto.SignatureVerification
|
import org.matrix.rustcomponents.sdk.crypto.SignatureVerification
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import uniffi.matrix_sdk_crypto.SignatureState
|
||||||
import java.security.InvalidParameterException
|
import java.security.InvalidParameterException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
|
@ -100,7 +100,7 @@ fun RealmToMigrate.getPickledAccount(pickleKey: ByteArray): MigrationData {
|
||||||
)
|
)
|
||||||
MigrationData(
|
MigrationData(
|
||||||
account = pickledAccount,
|
account = pickledAccount,
|
||||||
pickleKey = pickleKey.map { it.toUByte() },
|
pickleKey = pickleKey,
|
||||||
crossSigning = CrossSigningKeyExport(
|
crossSigning = CrossSigningKeyExport(
|
||||||
masterKey = masterKey,
|
masterKey = masterKey,
|
||||||
selfSigningKey = selfSignedKey,
|
selfSigningKey = selfSignedKey,
|
||||||
|
@ -153,7 +153,7 @@ fun RealmToMigrate.getPickledAccount(pickleKey: ByteArray): MigrationData {
|
||||||
|
|
||||||
migrationData = MigrationData(
|
migrationData = MigrationData(
|
||||||
account = pickledAccount,
|
account = pickledAccount,
|
||||||
pickleKey = pickleKey.map { it.toUByte() },
|
pickleKey = pickleKey,
|
||||||
crossSigning = CrossSigningKeyExport(
|
crossSigning = CrossSigningKeyExport(
|
||||||
masterKey = masterKey,
|
masterKey = masterKey,
|
||||||
selfSigningKey = selfSignedKey,
|
selfSigningKey = selfSignedKey,
|
||||||
|
@ -222,8 +222,10 @@ fun RealmToMigrate.pickledOlmSessions(pickleKey: ByteArray, chunkSize: Int, onCh
|
||||||
pickle = pickle,
|
pickle = pickle,
|
||||||
senderKey = deviceKey,
|
senderKey = deviceKey,
|
||||||
createdUsingFallbackKey = false,
|
createdUsingFallbackKey = false,
|
||||||
creationTime = lastReceivedMessageTs.toString(),
|
// / Unix timestamp (in seconds) when the session was created.
|
||||||
lastUseTime = lastReceivedMessageTs.toString()
|
creationTime = (lastReceivedMessageTs / 1000).toULong(),
|
||||||
|
// / Unix timestamp (in seconds) when the session was last used.
|
||||||
|
lastUseTime = (lastReceivedMessageTs / 1000).toULong(),
|
||||||
)
|
)
|
||||||
// should we check the tracking status?
|
// should we check the tracking status?
|
||||||
pickledSessions.add(pickledSession)
|
pickledSessions.add(pickledSession)
|
||||||
|
@ -323,8 +325,10 @@ private fun OlmSessionEntity.toPickledSession(pickleKey: ByteArray): PickledSess
|
||||||
pickle = pickledOlmSession,
|
pickle = pickledOlmSession,
|
||||||
senderKey = deviceKey,
|
senderKey = deviceKey,
|
||||||
createdUsingFallbackKey = false,
|
createdUsingFallbackKey = false,
|
||||||
creationTime = lastReceivedMessageTs.toString(),
|
// Rust expect in seconds
|
||||||
lastUseTime = lastReceivedMessageTs.toString()
|
creationTime = (lastReceivedMessageTs / 1000).toULong(),
|
||||||
|
// Rust expect in seconds
|
||||||
|
lastUseTime = (lastReceivedMessageTs / 1000).toULong(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,10 @@
|
||||||
package org.matrix.android.sdk.internal.session.content
|
package org.matrix.android.sdk.internal.session.content
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.media.MediaMetadataRetriever
|
import android.media.MediaMetadataRetriever
|
||||||
|
import android.os.Build
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
|
@ -115,7 +117,15 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
||||||
if (allCancelled) {
|
if (allCancelled) {
|
||||||
// there is no point in uploading the image!
|
// there is no point in uploading the image!
|
||||||
return Result.success(inputData)
|
return Result.success(inputData)
|
||||||
.also { Timber.e("## Send: Work cancelled by user") }
|
.also {
|
||||||
|
Timber.e("## Send: Work cancelled by user")
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
context.revokeUriPermission(context.packageName, params.attachment.queryUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
} else {
|
||||||
|
context.revokeUriPermission(params.attachment.queryUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val attachment = params.attachment
|
val attachment = params.attachment
|
||||||
|
@ -399,6 +409,12 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
||||||
)
|
)
|
||||||
return Result.success(WorkerParamsFactory.toData(sendParams)).also {
|
return Result.success(WorkerParamsFactory.toData(sendParams)).also {
|
||||||
Timber.v("## handleSuccess $attachmentUrl, work is stopped $isStopped")
|
Timber.v("## handleSuccess $attachmentUrl, work is stopped $isStopped")
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
context.revokeUriPermission(context.packageName, params.attachment.queryUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
} else {
|
||||||
|
context.revokeUriPermission(params.attachment.queryUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2638,18 +2638,20 @@
|
||||||
"a": "⊛ Head Shaking Horizontally",
|
"a": "⊛ Head Shaking Horizontally",
|
||||||
"b": "1F642-200D-2194-FE0F",
|
"b": "1F642-200D-2194-FE0F",
|
||||||
"j": [
|
"j": [
|
||||||
"head shaking horizontally",
|
|
||||||
"no",
|
"no",
|
||||||
"shake"
|
"shake",
|
||||||
|
"disapprove",
|
||||||
|
"indiffernt",
|
||||||
|
"left"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"head-shaking-vertically": {
|
"head-shaking-vertically": {
|
||||||
"a": "⊛ Head Shaking Vertically",
|
"a": "⊛ Head Shaking Vertically",
|
||||||
"b": "1F642-200D-2195-FE0F",
|
"b": "1F642-200D-2195-FE0F",
|
||||||
"j": [
|
"j": [
|
||||||
"head shaking vertically",
|
|
||||||
"nod",
|
"nod",
|
||||||
"yes"
|
"yes",
|
||||||
|
"down"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"relieved-face": {
|
"relieved-face": {
|
||||||
|
@ -6941,21 +6943,27 @@
|
||||||
"a": "⊛ Person Walking Facing Right",
|
"a": "⊛ Person Walking Facing Right",
|
||||||
"b": "1F6B6-200D-27A1-FE0F",
|
"b": "1F6B6-200D-27A1-FE0F",
|
||||||
"j": [
|
"j": [
|
||||||
""
|
"",
|
||||||
|
"peerson",
|
||||||
|
"exercise"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"woman-walking-facing-right": {
|
"woman-walking-facing-right": {
|
||||||
"a": "⊛ Woman Walking Facing Right",
|
"a": "⊛ Woman Walking Facing Right",
|
||||||
"b": "1F6B6-200D-2640-FE0F-200D-27A1-FE0F",
|
"b": "1F6B6-200D-2640-FE0F-200D-27A1-FE0F",
|
||||||
"j": [
|
"j": [
|
||||||
""
|
"",
|
||||||
|
"person",
|
||||||
|
"exercise"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"man-walking-facing-right": {
|
"man-walking-facing-right": {
|
||||||
"a": "⊛ Man Walking Facing Right",
|
"a": "⊛ Man Walking Facing Right",
|
||||||
"b": "1F6B6-200D-2642-FE0F-200D-27A1-FE0F",
|
"b": "1F6B6-200D-2642-FE0F-200D-27A1-FE0F",
|
||||||
"j": [
|
"j": [
|
||||||
""
|
"",
|
||||||
|
"person",
|
||||||
|
"exercise"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"person-standing": {
|
"person-standing": {
|
||||||
|
@ -7019,21 +7027,26 @@
|
||||||
"a": "⊛ Person Kneeling Facing Right",
|
"a": "⊛ Person Kneeling Facing Right",
|
||||||
"b": "1F9CE-200D-27A1-FE0F",
|
"b": "1F9CE-200D-27A1-FE0F",
|
||||||
"j": [
|
"j": [
|
||||||
""
|
"",
|
||||||
|
"pray"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"woman-kneeling-facing-right": {
|
"woman-kneeling-facing-right": {
|
||||||
"a": "⊛ Woman Kneeling Facing Right",
|
"a": "⊛ Woman Kneeling Facing Right",
|
||||||
"b": "1F9CE-200D-2640-FE0F-200D-27A1-FE0F",
|
"b": "1F9CE-200D-2640-FE0F-200D-27A1-FE0F",
|
||||||
"j": [
|
"j": [
|
||||||
""
|
"",
|
||||||
|
"pray",
|
||||||
|
"worship"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"man-kneeling-facing-right": {
|
"man-kneeling-facing-right": {
|
||||||
"a": "⊛ Man Kneeling Facing Right",
|
"a": "⊛ Man Kneeling Facing Right",
|
||||||
"b": "1F9CE-200D-2642-FE0F-200D-27A1-FE0F",
|
"b": "1F9CE-200D-2642-FE0F-200D-27A1-FE0F",
|
||||||
"j": [
|
"j": [
|
||||||
""
|
"",
|
||||||
|
"pray",
|
||||||
|
"worship"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"person-with-white-cane": {
|
"person-with-white-cane": {
|
||||||
|
@ -7049,7 +7062,10 @@
|
||||||
"a": "⊛ Person with White Cane Facing Right",
|
"a": "⊛ Person with White Cane Facing Right",
|
||||||
"b": "1F9D1-200D-1F9AF-200D-27A1-FE0F",
|
"b": "1F9D1-200D-1F9AF-200D-27A1-FE0F",
|
||||||
"j": [
|
"j": [
|
||||||
""
|
"",
|
||||||
|
"walk",
|
||||||
|
"visually impaired",
|
||||||
|
"blind"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"man-with-white-cane": {
|
"man-with-white-cane": {
|
||||||
|
@ -7066,7 +7082,11 @@
|
||||||
"a": "⊛ Man with White Cane Facing Right",
|
"a": "⊛ Man with White Cane Facing Right",
|
||||||
"b": "1F468-200D-1F9AF-200D-27A1-FE0F",
|
"b": "1F468-200D-1F9AF-200D-27A1-FE0F",
|
||||||
"j": [
|
"j": [
|
||||||
""
|
"",
|
||||||
|
"visually impaired",
|
||||||
|
"blind",
|
||||||
|
"walk",
|
||||||
|
"stick"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"woman-with-white-cane": {
|
"woman-with-white-cane": {
|
||||||
|
@ -7083,7 +7103,10 @@
|
||||||
"a": "⊛ Woman with White Cane Facing Right",
|
"a": "⊛ Woman with White Cane Facing Right",
|
||||||
"b": "1F469-200D-1F9AF-200D-27A1-FE0F",
|
"b": "1F469-200D-1F9AF-200D-27A1-FE0F",
|
||||||
"j": [
|
"j": [
|
||||||
""
|
"",
|
||||||
|
"stick",
|
||||||
|
"visually impaired",
|
||||||
|
"blind"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"person-in-motorized-wheelchair": {
|
"person-in-motorized-wheelchair": {
|
||||||
|
@ -7099,7 +7122,9 @@
|
||||||
"a": "⊛ Person in Motorized Wheelchair Facing Right",
|
"a": "⊛ Person in Motorized Wheelchair Facing Right",
|
||||||
"b": "1F9D1-200D-1F9BC-200D-27A1-FE0F",
|
"b": "1F9D1-200D-1F9BC-200D-27A1-FE0F",
|
||||||
"j": [
|
"j": [
|
||||||
""
|
"",
|
||||||
|
"accessibility",
|
||||||
|
"disability"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"man-in-motorized-wheelchair": {
|
"man-in-motorized-wheelchair": {
|
||||||
|
@ -7116,7 +7141,10 @@
|
||||||
"a": "⊛ Man in Motorized Wheelchair Facing Right",
|
"a": "⊛ Man in Motorized Wheelchair Facing Right",
|
||||||
"b": "1F468-200D-1F9BC-200D-27A1-FE0F",
|
"b": "1F468-200D-1F9BC-200D-27A1-FE0F",
|
||||||
"j": [
|
"j": [
|
||||||
""
|
"",
|
||||||
|
"disability",
|
||||||
|
"accessibility",
|
||||||
|
"mobility"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"woman-in-motorized-wheelchair": {
|
"woman-in-motorized-wheelchair": {
|
||||||
|
@ -7133,7 +7161,10 @@
|
||||||
"a": "⊛ Woman in Motorized Wheelchair Facing Right",
|
"a": "⊛ Woman in Motorized Wheelchair Facing Right",
|
||||||
"b": "1F469-200D-1F9BC-200D-27A1-FE0F",
|
"b": "1F469-200D-1F9BC-200D-27A1-FE0F",
|
||||||
"j": [
|
"j": [
|
||||||
""
|
"",
|
||||||
|
"mobility",
|
||||||
|
"accessibility",
|
||||||
|
"disability"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"person-in-manual-wheelchair": {
|
"person-in-manual-wheelchair": {
|
||||||
|
@ -7149,7 +7180,10 @@
|
||||||
"a": "⊛ Person in Manual Wheelchair Facing Right",
|
"a": "⊛ Person in Manual Wheelchair Facing Right",
|
||||||
"b": "1F9D1-200D-1F9BD-200D-27A1-FE0F",
|
"b": "1F9D1-200D-1F9BD-200D-27A1-FE0F",
|
||||||
"j": [
|
"j": [
|
||||||
""
|
"",
|
||||||
|
"mobility",
|
||||||
|
"accessibility",
|
||||||
|
"disability"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"man-in-manual-wheelchair": {
|
"man-in-manual-wheelchair": {
|
||||||
|
@ -7166,7 +7200,10 @@
|
||||||
"a": "⊛ Man in Manual Wheelchair Facing Right",
|
"a": "⊛ Man in Manual Wheelchair Facing Right",
|
||||||
"b": "1F468-200D-1F9BD-200D-27A1-FE0F",
|
"b": "1F468-200D-1F9BD-200D-27A1-FE0F",
|
||||||
"j": [
|
"j": [
|
||||||
""
|
"",
|
||||||
|
"mobility",
|
||||||
|
"accessibility",
|
||||||
|
"disability"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"woman-in-manual-wheelchair": {
|
"woman-in-manual-wheelchair": {
|
||||||
|
@ -7183,7 +7220,10 @@
|
||||||
"a": "⊛ Woman in Manual Wheelchair Facing Right",
|
"a": "⊛ Woman in Manual Wheelchair Facing Right",
|
||||||
"b": "1F469-200D-1F9BD-200D-27A1-FE0F",
|
"b": "1F469-200D-1F9BD-200D-27A1-FE0F",
|
||||||
"j": [
|
"j": [
|
||||||
""
|
"",
|
||||||
|
"disability",
|
||||||
|
"mobility",
|
||||||
|
"accessibility"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"person-running": {
|
"person-running": {
|
||||||
|
@ -7226,21 +7266,27 @@
|
||||||
"a": "⊛ Person Running Facing Right",
|
"a": "⊛ Person Running Facing Right",
|
||||||
"b": "1F3C3-200D-27A1-FE0F",
|
"b": "1F3C3-200D-27A1-FE0F",
|
||||||
"j": [
|
"j": [
|
||||||
""
|
"",
|
||||||
|
"exercise",
|
||||||
|
"jog"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"woman-running-facing-right": {
|
"woman-running-facing-right": {
|
||||||
"a": "⊛ Woman Running Facing Right",
|
"a": "⊛ Woman Running Facing Right",
|
||||||
"b": "1F3C3-200D-2640-FE0F-200D-27A1-FE0F",
|
"b": "1F3C3-200D-2640-FE0F-200D-27A1-FE0F",
|
||||||
"j": [
|
"j": [
|
||||||
""
|
"",
|
||||||
|
"exercise",
|
||||||
|
"jog"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"man-running-facing-right": {
|
"man-running-facing-right": {
|
||||||
"a": "⊛ Man Running Facing Right",
|
"a": "⊛ Man Running Facing Right",
|
||||||
"b": "1F3C3-200D-2642-FE0F-200D-27A1-FE0F",
|
"b": "1F3C3-200D-2642-FE0F-200D-27A1-FE0F",
|
||||||
"j": [
|
"j": [
|
||||||
""
|
"",
|
||||||
|
"jog",
|
||||||
|
"exercise"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"woman-dancing": {
|
"woman-dancing": {
|
||||||
|
@ -8538,28 +8584,40 @@
|
||||||
"a": "⊛ Family: Adult, Adult, Child",
|
"a": "⊛ Family: Adult, Adult, Child",
|
||||||
"b": "1F9D1-200D-1F9D1-200D-1F9D2",
|
"b": "1F9D1-200D-1F9D1-200D-1F9D2",
|
||||||
"j": [
|
"j": [
|
||||||
"family: adult, adult, child"
|
"family: adult, adult, child",
|
||||||
|
"family adult, adult, child",
|
||||||
|
"kid",
|
||||||
|
"parents"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"family-adult-adult-child-child": {
|
"family-adult-adult-child-child": {
|
||||||
"a": "⊛ Family: Adult, Adult, Child, Child",
|
"a": "⊛ Family: Adult, Adult, Child, Child",
|
||||||
"b": "1F9D1-200D-1F9D1-200D-1F9D2-200D-1F9D2",
|
"b": "1F9D1-200D-1F9D1-200D-1F9D2-200D-1F9D2",
|
||||||
"j": [
|
"j": [
|
||||||
"family: adult, adult, child, child"
|
"family: adult, adult, child, child",
|
||||||
|
"family adult, adult, child, child",
|
||||||
|
"children",
|
||||||
|
"parents"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"family-adult-child": {
|
"family-adult-child": {
|
||||||
"a": "⊛ Family: Adult, Child",
|
"a": "⊛ Family: Adult, Child",
|
||||||
"b": "1F9D1-200D-1F9D2",
|
"b": "1F9D1-200D-1F9D2",
|
||||||
"j": [
|
"j": [
|
||||||
"family: adult, child"
|
"family: adult, child",
|
||||||
|
"family adult, child",
|
||||||
|
"parent",
|
||||||
|
"kid"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"family-adult-child-child": {
|
"family-adult-child-child": {
|
||||||
"a": "⊛ Family: Adult, Child, Child",
|
"a": "⊛ Family: Adult, Child, Child",
|
||||||
"b": "1F9D1-200D-1F9D2-200D-1F9D2",
|
"b": "1F9D1-200D-1F9D2-200D-1F9D2",
|
||||||
"j": [
|
"j": [
|
||||||
"family: adult, child, child"
|
"family: adult, child, child",
|
||||||
|
"family adult, child, child",
|
||||||
|
"parent",
|
||||||
|
"children"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"footprints": {
|
"footprints": {
|
||||||
|
@ -9564,9 +9622,12 @@
|
||||||
"j": [
|
"j": [
|
||||||
"fantasy",
|
"fantasy",
|
||||||
"firebird",
|
"firebird",
|
||||||
"phoenix",
|
|
||||||
"rebirth",
|
"rebirth",
|
||||||
"reincarnation"
|
"reincarnation",
|
||||||
|
"immortal",
|
||||||
|
"bird",
|
||||||
|
"mythtical",
|
||||||
|
"reborn"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"frog": {
|
"frog": {
|
||||||
|
@ -10364,8 +10425,9 @@
|
||||||
"j": [
|
"j": [
|
||||||
"citrus",
|
"citrus",
|
||||||
"fruit",
|
"fruit",
|
||||||
"lime",
|
"tropical",
|
||||||
"tropical"
|
"acidic",
|
||||||
|
"citric"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"banana": {
|
"banana": {
|
||||||
|
@ -10689,11 +10751,11 @@
|
||||||
"a": "⊛ Brown Mushroom",
|
"a": "⊛ Brown Mushroom",
|
||||||
"b": "1F344-200D-1F7EB",
|
"b": "1F344-200D-1F7EB",
|
||||||
"j": [
|
"j": [
|
||||||
"brown mushroom",
|
|
||||||
"food",
|
"food",
|
||||||
"fungus",
|
"fungus",
|
||||||
"nature",
|
"nature",
|
||||||
"vegetable"
|
"vegetable",
|
||||||
|
"toadstool"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"bread": {
|
"bread": {
|
||||||
|
@ -17986,10 +18048,10 @@
|
||||||
"j": [
|
"j": [
|
||||||
"break",
|
"break",
|
||||||
"breaking",
|
"breaking",
|
||||||
"broken chain",
|
|
||||||
"chain",
|
"chain",
|
||||||
"cuffs",
|
"cuffs",
|
||||||
"freedom"
|
"freedom",
|
||||||
|
"constraint"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"chains": {
|
"chains": {
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
echo "Deleted existing plan..."
|
|
||||||
rm vector/src/main/java/im/vector/app/features/analytics/plan/*.*
|
|
||||||
|
|
||||||
echo "Cloning analytics project..."
|
|
||||||
mkdir analytics_tmp
|
|
||||||
cd analytics_tmp
|
|
||||||
git clone https://github.com/matrix-org/matrix-analytics-events.git
|
|
||||||
|
|
||||||
echo "Copy plan..."
|
|
||||||
cp matrix-analytics-events/types/kotlin2/* ../vector/src/main/java/im/vector/app/features/analytics/plan/
|
|
||||||
|
|
||||||
echo "Cleanup."
|
|
||||||
cd ..
|
|
||||||
rm -rf analytics_tmp
|
|
||||||
|
|
||||||
echo "Done."
|
|
|
@ -37,7 +37,7 @@ ext.versionMinor = 6
|
||||||
// Note: even values are reserved for regular release, odd values for hotfix release.
|
// Note: even values are reserved for regular release, odd values for hotfix release.
|
||||||
// When creating a hotfix, you should decrease the value, since the current value
|
// When creating a hotfix, you should decrease the value, since the current value
|
||||||
// is the value for the next regular release.
|
// is the value for the next regular release.
|
||||||
ext.versionPatch = 14
|
ext.versionPatch = 16
|
||||||
|
|
||||||
ext.scVersion = 77
|
ext.scVersion = 77
|
||||||
|
|
||||||
|
|
|
@ -161,6 +161,9 @@ dependencies {
|
||||||
// Debug
|
// Debug
|
||||||
api 'com.facebook.stetho:stetho:1.6.0'
|
api 'com.facebook.stetho:stetho:1.6.0'
|
||||||
|
|
||||||
|
// Analytics
|
||||||
|
api 'com.github.matrix-org:matrix-analytics-events:0.15.0'
|
||||||
|
|
||||||
api libs.google.phonenumber
|
api libs.google.phonenumber
|
||||||
|
|
||||||
// FlowBinding
|
// FlowBinding
|
||||||
|
@ -233,9 +236,7 @@ dependencies {
|
||||||
kapt libs.dagger.hiltCompiler
|
kapt libs.dagger.hiltCompiler
|
||||||
|
|
||||||
// Analytics
|
// Analytics
|
||||||
implementation('com.posthog.android:posthog:2.0.3') {
|
implementation 'com.posthog:posthog-android:3.2.0'
|
||||||
exclude group: 'com.android.support', module: 'support-annotations'
|
|
||||||
}
|
|
||||||
implementation libs.sentry.sentryAndroid
|
implementation libs.sentry.sentryAndroid
|
||||||
|
|
||||||
// UnifiedPush
|
// UnifiedPush
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 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
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import im.vector.app.InstrumentedTest
|
||||||
|
import im.vector.app.features.analytics.ReportedDecryptionFailurePersistence
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class ReportedDecryptionFailurePersistenceTest : InstrumentedTest {
|
||||||
|
|
||||||
|
private val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun shouldPersistReportedUtds() = runTest {
|
||||||
|
val persistence = ReportedDecryptionFailurePersistence(context)
|
||||||
|
persistence.load()
|
||||||
|
|
||||||
|
val eventIds = listOf("$0000", "$0001", "$0002", "$0003")
|
||||||
|
eventIds.forEach {
|
||||||
|
persistence.markAsReported(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
eventIds.forEach {
|
||||||
|
persistence.hasBeenReported(it) shouldBeEqualTo true
|
||||||
|
}
|
||||||
|
|
||||||
|
persistence.hasBeenReported("$0004") shouldBeEqualTo false
|
||||||
|
|
||||||
|
persistence.persist()
|
||||||
|
|
||||||
|
// Load a new one
|
||||||
|
val persistence2 = ReportedDecryptionFailurePersistence(context)
|
||||||
|
persistence2.load()
|
||||||
|
|
||||||
|
eventIds.forEach {
|
||||||
|
persistence2.hasBeenReported(it) shouldBeEqualTo true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSaturation() = runTest {
|
||||||
|
val persistence = ReportedDecryptionFailurePersistence(context)
|
||||||
|
|
||||||
|
for (i in 1..6000) {
|
||||||
|
persistence.markAsReported("000$i")
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should have saturated the bloom filter, making the rate of false positives too high.
|
||||||
|
// A new bloom filter should have been created to avoid that and the recent reported events should still be in the new filter.
|
||||||
|
for (i in 5800..6000) {
|
||||||
|
persistence.hasBeenReported("000$i") shouldBeEqualTo true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Old ones should not be there though
|
||||||
|
for (i in 1..1000) {
|
||||||
|
persistence.hasBeenReported("000$i") shouldBeEqualTo false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -178,10 +178,15 @@
|
||||||
|
|
||||||
<data android:scheme="https" />
|
<data android:scheme="https" />
|
||||||
<data android:host="riot.im" />
|
<data android:host="riot.im" />
|
||||||
<data android:host="app.element.io" />
|
</intent-filter>
|
||||||
<data android:host="mobile.element.io" />
|
<intent-filter android:autoVerify="true">
|
||||||
<data android:host="develop.element.io" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<data android:host="staging.element.io" />
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="https" />
|
||||||
|
<data android:host="*.element.io" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,7 @@ private const val MAX_WAIT_MILLIS = 60_000
|
||||||
class DecryptionFailureTracker @Inject constructor(
|
class DecryptionFailureTracker @Inject constructor(
|
||||||
private val analyticsTracker: AnalyticsTracker,
|
private val analyticsTracker: AnalyticsTracker,
|
||||||
private val sessionDataSource: ActiveSessionDataSource,
|
private val sessionDataSource: ActiveSessionDataSource,
|
||||||
|
private val decryptionFailurePersistence: ReportedDecryptionFailurePersistence,
|
||||||
private val clock: Clock
|
private val clock: Clock
|
||||||
) : Session.Listener, LiveEventListener {
|
) : Session.Listener, LiveEventListener {
|
||||||
|
|
||||||
|
@ -76,9 +77,6 @@ class DecryptionFailureTracker @Inject constructor(
|
||||||
// Only accessed on a `post` call, ensuring sequential access
|
// Only accessed on a `post` call, ensuring sequential access
|
||||||
private val trackedEventsMap = mutableMapOf<String, DecryptionFailure>()
|
private val trackedEventsMap = mutableMapOf<String, DecryptionFailure>()
|
||||||
|
|
||||||
// List of eventId that have been reported, to avoid double reporting
|
|
||||||
private val alreadyReported = mutableListOf<String>()
|
|
||||||
|
|
||||||
// Mutex to ensure sequential access to internal state
|
// Mutex to ensure sequential access to internal state
|
||||||
private val mutex = Mutex()
|
private val mutex = Mutex()
|
||||||
|
|
||||||
|
@ -98,10 +96,16 @@ class DecryptionFailureTracker @Inject constructor(
|
||||||
this.scope = scope
|
this.scope = scope
|
||||||
}
|
}
|
||||||
observeActiveSession()
|
observeActiveSession()
|
||||||
|
post {
|
||||||
|
decryptionFailurePersistence.load()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stop() {
|
fun stop() {
|
||||||
Timber.v("Stop DecryptionFailureTracker")
|
Timber.v("Stop DecryptionFailureTracker")
|
||||||
|
post {
|
||||||
|
decryptionFailurePersistence.persist()
|
||||||
|
}
|
||||||
activeSessionSourceDisposable.cancel(CancellationException("Closing DecryptionFailureTracker"))
|
activeSessionSourceDisposable.cancel(CancellationException("Closing DecryptionFailureTracker"))
|
||||||
|
|
||||||
activeSession?.removeListener(this)
|
activeSession?.removeListener(this)
|
||||||
|
@ -123,6 +127,7 @@ class DecryptionFailureTracker @Inject constructor(
|
||||||
delay(CHECK_INTERVAL)
|
delay(CHECK_INTERVAL)
|
||||||
post {
|
post {
|
||||||
checkFailures()
|
checkFailures()
|
||||||
|
decryptionFailurePersistence.persist()
|
||||||
currentTicker = null
|
currentTicker = null
|
||||||
if (trackedEventsMap.isNotEmpty()) {
|
if (trackedEventsMap.isNotEmpty()) {
|
||||||
// Reschedule
|
// Reschedule
|
||||||
|
@ -136,7 +141,7 @@ class DecryptionFailureTracker @Inject constructor(
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.onEach {
|
.onEach {
|
||||||
Timber.v("Active session changed ${it.getOrNull()?.myUserId}")
|
Timber.v("Active session changed ${it.getOrNull()?.myUserId}")
|
||||||
it.orNull()?.let { session ->
|
it.getOrNull()?.let { session ->
|
||||||
post {
|
post {
|
||||||
onSessionActive(session)
|
onSessionActive(session)
|
||||||
}
|
}
|
||||||
|
@ -144,7 +149,7 @@ class DecryptionFailureTracker @Inject constructor(
|
||||||
}.launchIn(scope)
|
}.launchIn(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onSessionActive(session: Session) {
|
private suspend fun onSessionActive(session: Session) {
|
||||||
Timber.v("onSessionActive ${session.myUserId} previous: ${activeSession?.myUserId}")
|
Timber.v("onSessionActive ${session.myUserId} previous: ${activeSession?.myUserId}")
|
||||||
val sessionId = session.sessionId
|
val sessionId = session.sessionId
|
||||||
if (sessionId == activeSession?.sessionId) {
|
if (sessionId == activeSession?.sessionId) {
|
||||||
|
@ -201,7 +206,8 @@ class DecryptionFailureTracker @Inject constructor(
|
||||||
// already tracked
|
// already tracked
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (alreadyReported.contains(eventId)) {
|
if (decryptionFailurePersistence.hasBeenReported(eventId)) {
|
||||||
|
Timber.v("Event $eventId already reported")
|
||||||
// already reported
|
// already reported
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -236,7 +242,7 @@ class DecryptionFailureTracker @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleEventDecrypted(eventId: String) {
|
private suspend fun handleEventDecrypted(eventId: String) {
|
||||||
Timber.v("Handle event decrypted $eventId time: ${clock.epochMillis()}")
|
Timber.v("Handle event decrypted $eventId time: ${clock.epochMillis()}")
|
||||||
// Only consider if it was tracked as a failure
|
// Only consider if it was tracked as a failure
|
||||||
val trackedFailure = trackedEventsMap[eventId] ?: return
|
val trackedFailure = trackedEventsMap[eventId] ?: return
|
||||||
|
@ -269,7 +275,7 @@ class DecryptionFailureTracker @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will mutate the trackedEventsMap, so don't call it while iterating on it.
|
// This will mutate the trackedEventsMap, so don't call it while iterating on it.
|
||||||
private fun reportFailure(decryptionFailure: DecryptionFailure) {
|
private suspend fun reportFailure(decryptionFailure: DecryptionFailure) {
|
||||||
Timber.v("Report failure for event ${decryptionFailure.failedEventId}")
|
Timber.v("Report failure for event ${decryptionFailure.failedEventId}")
|
||||||
val error = decryptionFailure.toAnalyticsEvent()
|
val error = decryptionFailure.toAnalyticsEvent()
|
||||||
|
|
||||||
|
@ -278,10 +284,10 @@ class DecryptionFailureTracker @Inject constructor(
|
||||||
// now remove from tracked
|
// now remove from tracked
|
||||||
trackedEventsMap.remove(decryptionFailure.failedEventId)
|
trackedEventsMap.remove(decryptionFailure.failedEventId)
|
||||||
// mark as already reported
|
// mark as already reported
|
||||||
alreadyReported.add(decryptionFailure.failedEventId)
|
decryptionFailurePersistence.markAsReported(decryptionFailure.failedEventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkFailures() {
|
private suspend fun checkFailures() {
|
||||||
val now = clock.epochMillis()
|
val now = clock.epochMillis()
|
||||||
Timber.v("Check failures now $now")
|
Timber.v("Check failures now $now")
|
||||||
// report the definitely failed
|
// report the definitely failed
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 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.analytics
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.LruCache
|
||||||
|
import com.google.common.hash.BloomFilter
|
||||||
|
import com.google.common.hash.Funnels
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private const val REPORTED_UTD_FILE_NAME = "im.vector.analytics.reported_utd"
|
||||||
|
private const val EXPECTED_INSERTIONS = 5000
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to keep track of the reported decryption failures to avoid double reporting.
|
||||||
|
* It uses a bloom filter to limit the memory/disk usage.
|
||||||
|
*/
|
||||||
|
class ReportedDecryptionFailurePersistence @Inject constructor(
|
||||||
|
private val context: Context,
|
||||||
|
) {
|
||||||
|
|
||||||
|
// Keep a cache of recent reported failures in memory.
|
||||||
|
// They will be persisted to the a new bloom filter if the previous one is getting saturated.
|
||||||
|
// Should be around 30KB max in memory.
|
||||||
|
// Also allows to have 0% false positive rate for recent failures.
|
||||||
|
private val inMemoryReportedFailures: LruCache<String, Unit> = LruCache(300)
|
||||||
|
|
||||||
|
// Thread-safe and lock-free.
|
||||||
|
// The expected insertions is 5000, and expected false positive probability of 3% when close to max capability.
|
||||||
|
// The persisted size is expected to be around 5KB (100 times less than if it was raw strings).
|
||||||
|
private var bloomFilter: BloomFilter<String> = BloomFilter.create<String>(Funnels.stringFunnel(Charsets.UTF_8), EXPECTED_INSERTIONS)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark an event as reported.
|
||||||
|
* @param eventId the event id to mark as reported.
|
||||||
|
*/
|
||||||
|
suspend fun markAsReported(eventId: String) {
|
||||||
|
// Add to in memory cache.
|
||||||
|
inMemoryReportedFailures.put(eventId, Unit)
|
||||||
|
bloomFilter.put(eventId)
|
||||||
|
|
||||||
|
// check if the filter is getting saturated? and then replace
|
||||||
|
if (bloomFilter.approximateElementCount() > EXPECTED_INSERTIONS - 500) {
|
||||||
|
// The filter is getting saturated, and the false positive rate is increasing.
|
||||||
|
// It's time to replace the filter with a new one. And move the in-memory cache to the new filter.
|
||||||
|
bloomFilter = BloomFilter.create<String>(Funnels.stringFunnel(Charsets.UTF_8), EXPECTED_INSERTIONS)
|
||||||
|
inMemoryReportedFailures.snapshot().keys.forEach {
|
||||||
|
bloomFilter.put(it)
|
||||||
|
}
|
||||||
|
persist()
|
||||||
|
}
|
||||||
|
Timber.v("## Bloom filter stats: expectedFpp: ${bloomFilter.expectedFpp()}, size: ${bloomFilter.approximateElementCount()}")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an event has been reported.
|
||||||
|
* @param eventId the event id to check.
|
||||||
|
* @return true if the event has been reported.
|
||||||
|
*/
|
||||||
|
fun hasBeenReported(eventId: String): Boolean {
|
||||||
|
// First check in memory cache.
|
||||||
|
if (inMemoryReportedFailures.get(eventId) != null) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return bloomFilter.mightContain(eventId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the reported failures from disk.
|
||||||
|
*/
|
||||||
|
suspend fun load() {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val file = File(context.applicationContext.cacheDir, REPORTED_UTD_FILE_NAME)
|
||||||
|
if (file.exists()) {
|
||||||
|
file.inputStream().use {
|
||||||
|
bloomFilter = BloomFilter.readFrom(it, Funnels.stringFunnel(Charsets.UTF_8))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Timber.e(e, "## Failed to load reported failures")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persist the reported failures to disk.
|
||||||
|
*/
|
||||||
|
suspend fun persist() {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val file = File(context.applicationContext.cacheDir, REPORTED_UTD_FILE_NAME)
|
||||||
|
if (!file.exists()) file.createNewFile()
|
||||||
|
FileOutputStream(file).buffered().use {
|
||||||
|
bloomFilter.writeTo(it)
|
||||||
|
}
|
||||||
|
Timber.v("## Successfully saved reported failures, size: ${file.length()}")
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Timber.e(e, "## Failed to save reported failures")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,9 +16,7 @@
|
||||||
|
|
||||||
package im.vector.app.features.analytics.impl
|
package im.vector.app.features.analytics.impl
|
||||||
|
|
||||||
import com.posthog.android.Options
|
import com.posthog.PostHogInterface
|
||||||
import com.posthog.android.PostHog
|
|
||||||
import com.posthog.android.Properties
|
|
||||||
import im.vector.app.core.di.NamedGlobalScope
|
import im.vector.app.core.di.NamedGlobalScope
|
||||||
import im.vector.app.features.analytics.AnalyticsConfig
|
import im.vector.app.features.analytics.AnalyticsConfig
|
||||||
import im.vector.app.features.analytics.VectorAnalytics
|
import im.vector.app.features.analytics.VectorAnalytics
|
||||||
|
@ -36,9 +34,6 @@ import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
private val REUSE_EXISTING_ID: String? = null
|
|
||||||
private val IGNORED_OPTIONS: Options? = null
|
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class DefaultVectorAnalytics @Inject constructor(
|
class DefaultVectorAnalytics @Inject constructor(
|
||||||
private val postHogFactory: PostHogFactory,
|
private val postHogFactory: PostHogFactory,
|
||||||
|
@ -49,9 +44,9 @@ class DefaultVectorAnalytics @Inject constructor(
|
||||||
@NamedGlobalScope private val globalScope: CoroutineScope
|
@NamedGlobalScope private val globalScope: CoroutineScope
|
||||||
) : VectorAnalytics {
|
) : VectorAnalytics {
|
||||||
|
|
||||||
private var posthog: PostHog? = null
|
private var posthog: PostHogInterface? = null
|
||||||
|
|
||||||
private fun createPosthog(): PostHog? {
|
private fun createPosthog(): PostHogInterface? {
|
||||||
return when {
|
return when {
|
||||||
analyticsConfig.isEnabled -> postHogFactory.createPosthog()
|
analyticsConfig.isEnabled -> postHogFactory.createPosthog()
|
||||||
else -> {
|
else -> {
|
||||||
|
@ -126,7 +121,7 @@ class DefaultVectorAnalytics @Inject constructor(
|
||||||
posthog?.reset()
|
posthog?.reset()
|
||||||
} else {
|
} else {
|
||||||
Timber.tag(analyticsTag.value).d("identify")
|
Timber.tag(analyticsTag.value).d("identify")
|
||||||
posthog?.identify(id, lateInitUserPropertiesFactory.createUserProperties()?.getProperties()?.toPostHogUserProperties(), IGNORED_OPTIONS)
|
posthog?.identify(id, lateInitUserPropertiesFactory.createUserProperties()?.getProperties()?.toPostHogUserProperties())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +150,7 @@ class DefaultVectorAnalytics @Inject constructor(
|
||||||
when (_userConsent) {
|
when (_userConsent) {
|
||||||
true -> {
|
true -> {
|
||||||
posthog = createPosthog()
|
posthog = createPosthog()
|
||||||
posthog?.optOut(false)
|
posthog?.optIn()
|
||||||
identifyPostHog()
|
identifyPostHog()
|
||||||
pendingUserProperties?.let { doUpdateUserProperties(it) }
|
pendingUserProperties?.let { doUpdateUserProperties(it) }
|
||||||
pendingUserProperties = null
|
pendingUserProperties = null
|
||||||
|
@ -163,8 +158,8 @@ class DefaultVectorAnalytics @Inject constructor(
|
||||||
false -> {
|
false -> {
|
||||||
// When opting out, ensure that the queue is flushed first, or it will be flushed later (after user has revoked consent)
|
// When opting out, ensure that the queue is flushed first, or it will be flushed later (after user has revoked consent)
|
||||||
posthog?.flush()
|
posthog?.flush()
|
||||||
posthog?.optOut(true)
|
posthog?.optOut()
|
||||||
posthog?.shutdown()
|
posthog?.close()
|
||||||
posthog = null
|
posthog = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,6 +172,7 @@ class DefaultVectorAnalytics @Inject constructor(
|
||||||
?.takeIf { userConsent == true }
|
?.takeIf { userConsent == true }
|
||||||
?.capture(
|
?.capture(
|
||||||
event.getName(),
|
event.getName(),
|
||||||
|
analyticsId,
|
||||||
event.getProperties()?.toPostHogProperties()
|
event.getProperties()?.toPostHogProperties()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -197,28 +193,38 @@ class DefaultVectorAnalytics @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun doUpdateUserProperties(userProperties: UserProperties) {
|
private fun doUpdateUserProperties(userProperties: UserProperties) {
|
||||||
|
// we need a distinct id to set user properties
|
||||||
|
val distinctId = analyticsId ?: return
|
||||||
posthog
|
posthog
|
||||||
?.takeIf { userConsent == true }
|
?.takeIf { userConsent == true }
|
||||||
?.identify(REUSE_EXISTING_ID, userProperties.getProperties()?.toPostHogUserProperties(), IGNORED_OPTIONS)
|
?.identify(distinctId, userProperties.getProperties())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Map<String, Any?>?.toPostHogProperties(): Properties? {
|
private fun Map<String, Any?>?.toPostHogProperties(): Map<String, Any>? {
|
||||||
if (this == null) return null
|
if (this == null) return null
|
||||||
|
|
||||||
return Properties().apply {
|
val nonNulls = HashMap<String, Any>()
|
||||||
putAll(this@toPostHogProperties)
|
this.forEach { (key, value) ->
|
||||||
|
if (value != null) {
|
||||||
|
nonNulls[key] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nonNulls
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We avoid sending nulls as part of the UserProperties as this will reset the values across all devices.
|
* We avoid sending nulls as part of the UserProperties as this will reset the values across all devices.
|
||||||
* The UserProperties event has nullable properties to allow for clients to opt in.
|
* The UserProperties event has nullable properties to allow for clients to opt in.
|
||||||
*/
|
*/
|
||||||
private fun Map<String, Any?>.toPostHogUserProperties(): Properties {
|
private fun Map<String, Any?>.toPostHogUserProperties(): Map<String, Any> {
|
||||||
return Properties().apply {
|
val nonNulls = HashMap<String, Any>()
|
||||||
putAll(this@toPostHogUserProperties.filter { it.value != null })
|
this.forEach { (key, value) ->
|
||||||
|
if (value != null) {
|
||||||
|
nonNulls[key] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nonNulls
|
||||||
|
}
|
||||||
|
|
||||||
override fun trackError(throwable: Throwable) {
|
override fun trackError(throwable: Throwable) {
|
||||||
sentryAnalytics
|
sentryAnalytics
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
package im.vector.app.features.analytics.impl
|
package im.vector.app.features.analytics.impl
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.posthog.android.PostHog
|
import com.posthog.PostHogInterface
|
||||||
|
import com.posthog.android.PostHogAndroid
|
||||||
|
import com.posthog.android.PostHogAndroidConfig
|
||||||
import im.vector.app.core.resources.BuildMeta
|
import im.vector.app.core.resources.BuildMeta
|
||||||
import im.vector.app.features.analytics.AnalyticsConfig
|
import im.vector.app.features.analytics.AnalyticsConfig
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -28,29 +30,17 @@ class PostHogFactory @Inject constructor(
|
||||||
private val buildMeta: BuildMeta,
|
private val buildMeta: BuildMeta,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun createPosthog(): PostHog {
|
fun createPosthog(): PostHogInterface {
|
||||||
return PostHog.Builder(context, analyticsConfig.postHogApiKey, analyticsConfig.postHogHost)
|
val config = PostHogAndroidConfig(
|
||||||
// Record certain application events automatically! (off/false by default)
|
apiKey = analyticsConfig.postHogApiKey,
|
||||||
// .captureApplicationLifecycleEvents()
|
host = analyticsConfig.postHogHost,
|
||||||
// Record screen views automatically! (off/false by default)
|
// we do that manually
|
||||||
// .recordScreenViews()
|
captureScreenViews = false,
|
||||||
// Capture deep links as part of the screen call. (off by default)
|
).also {
|
||||||
// .captureDeepLinks()
|
if (buildMeta.isDebug) {
|
||||||
// Maximum number of events to keep in queue before flushing (default 20)
|
it.debug = true
|
||||||
// .flushQueueSize(20)
|
|
||||||
// Max delay before flushing the queue (30 seconds)
|
|
||||||
// .flushInterval(30, TimeUnit.SECONDS)
|
|
||||||
// Enable or disable collection of ANDROID_ID (true)
|
|
||||||
.collectDeviceId(false)
|
|
||||||
.logLevel(getLogLevel())
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getLogLevel(): PostHog.LogLevel {
|
|
||||||
return if (buildMeta.isDebug) {
|
|
||||||
PostHog.LogLevel.DEBUG
|
|
||||||
} else {
|
|
||||||
PostHog.LogLevel.INFO
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return PostHogAndroid.with(context, config)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.itf
|
|
||||||
|
|
||||||
interface VectorAnalyticsEvent {
|
|
||||||
fun getName(): String
|
|
||||||
fun getProperties(): Map<String, Any?>?
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.itf
|
|
||||||
|
|
||||||
interface VectorAnalyticsScreen {
|
|
||||||
fun getName(): String
|
|
||||||
fun getProperties(): Map<String, Any>?
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when a call has ended.
|
|
||||||
*/
|
|
||||||
data class CallEnded(
|
|
||||||
/**
|
|
||||||
* The duration of the call in milliseconds.
|
|
||||||
*/
|
|
||||||
val durationMs: Int,
|
|
||||||
/**
|
|
||||||
* Whether its a video call or not.
|
|
||||||
*/
|
|
||||||
val isVideo: Boolean,
|
|
||||||
/**
|
|
||||||
* Number of participants in the call.
|
|
||||||
*/
|
|
||||||
val numParticipants: Int,
|
|
||||||
/**
|
|
||||||
* Whether this user placed it.
|
|
||||||
*/
|
|
||||||
val placed: Boolean,
|
|
||||||
) : VectorAnalyticsEvent {
|
|
||||||
|
|
||||||
override fun getName() = "CallEnded"
|
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
put("durationMs", durationMs)
|
|
||||||
put("isVideo", isVideo)
|
|
||||||
put("numParticipants", numParticipants)
|
|
||||||
put("placed", placed)
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when an error occurred in a call.
|
|
||||||
*/
|
|
||||||
data class CallError(
|
|
||||||
/**
|
|
||||||
* Whether its a video call or not.
|
|
||||||
*/
|
|
||||||
val isVideo: Boolean,
|
|
||||||
/**
|
|
||||||
* Number of participants in the call.
|
|
||||||
*/
|
|
||||||
val numParticipants: Int,
|
|
||||||
/**
|
|
||||||
* Whether this user placed it.
|
|
||||||
*/
|
|
||||||
val placed: Boolean,
|
|
||||||
) : VectorAnalyticsEvent {
|
|
||||||
|
|
||||||
override fun getName() = "CallError"
|
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
put("isVideo", isVideo)
|
|
||||||
put("numParticipants", numParticipants)
|
|
||||||
put("placed", placed)
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when a call is started.
|
|
||||||
*/
|
|
||||||
data class CallStarted(
|
|
||||||
/**
|
|
||||||
* Whether its a video call or not.
|
|
||||||
*/
|
|
||||||
val isVideo: Boolean,
|
|
||||||
/**
|
|
||||||
* Number of participants in the call.
|
|
||||||
*/
|
|
||||||
val numParticipants: Int,
|
|
||||||
/**
|
|
||||||
* Whether this user placed it.
|
|
||||||
*/
|
|
||||||
val placed: Boolean,
|
|
||||||
) : VectorAnalyticsEvent {
|
|
||||||
|
|
||||||
override fun getName() = "CallStarted"
|
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
put("isVideo", isVideo)
|
|
||||||
put("numParticipants", numParticipants)
|
|
||||||
put("placed", placed)
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when the user sends a message via the composer.
|
|
||||||
*/
|
|
||||||
data class Composer(
|
|
||||||
/**
|
|
||||||
* Whether the user was using the composer inside of a thread.
|
|
||||||
*/
|
|
||||||
val inThread: Boolean,
|
|
||||||
/**
|
|
||||||
* Whether the user's composer interaction was editing a previously sent
|
|
||||||
* event.
|
|
||||||
*/
|
|
||||||
val isEditing: Boolean,
|
|
||||||
/**
|
|
||||||
* Whether the user's composer interaction was a reply to a previously
|
|
||||||
* sent event.
|
|
||||||
*/
|
|
||||||
val isReply: Boolean,
|
|
||||||
/**
|
|
||||||
* The type of the message.
|
|
||||||
*/
|
|
||||||
val messageType: MessageType,
|
|
||||||
/**
|
|
||||||
* Whether this message begins a new thread or not.
|
|
||||||
*/
|
|
||||||
val startsThread: Boolean? = null,
|
|
||||||
) : VectorAnalyticsEvent {
|
|
||||||
|
|
||||||
enum class MessageType {
|
|
||||||
/**
|
|
||||||
* A pin drop location message.
|
|
||||||
*/
|
|
||||||
LocationPin,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A user current location message.
|
|
||||||
*/
|
|
||||||
LocationUser,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A poll message.
|
|
||||||
*/
|
|
||||||
Poll,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A text message.
|
|
||||||
*/
|
|
||||||
Text,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A voice message.
|
|
||||||
*/
|
|
||||||
VoiceMessage,
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getName() = "Composer"
|
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
put("inThread", inThread)
|
|
||||||
put("isEditing", isEditing)
|
|
||||||
put("isReply", isReply)
|
|
||||||
put("messageType", messageType.name)
|
|
||||||
startsThread?.let { put("startsThread", it) }
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when the user creates a room.
|
|
||||||
*/
|
|
||||||
data class CreatedRoom(
|
|
||||||
/**
|
|
||||||
* Whether the room is a DM.
|
|
||||||
*/
|
|
||||||
val isDM: Boolean,
|
|
||||||
) : VectorAnalyticsEvent {
|
|
||||||
|
|
||||||
override fun getName() = "CreatedRoom"
|
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
put("isDM", isDM)
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,183 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when an error occurred.
|
|
||||||
*/
|
|
||||||
data class Error(
|
|
||||||
/**
|
|
||||||
* Context - client defined, can be used for debugging.
|
|
||||||
*/
|
|
||||||
val context: String? = null,
|
|
||||||
/**
|
|
||||||
* DEPRECATED: Which crypto module is the client currently using.
|
|
||||||
*/
|
|
||||||
val cryptoModule: CryptoModule? = null,
|
|
||||||
/**
|
|
||||||
* Which crypto backend is the client currently using.
|
|
||||||
*/
|
|
||||||
val cryptoSDK: CryptoSDK? = null,
|
|
||||||
val domain: Domain,
|
|
||||||
/**
|
|
||||||
* An heuristic based on event origin_server_ts and the current device
|
|
||||||
* creation time (origin_server_ts - device_ts). This would be used to
|
|
||||||
* get the source of the event scroll-back/live/initialSync.
|
|
||||||
*/
|
|
||||||
val eventLocalAgeMillis: Int? = null,
|
|
||||||
/**
|
|
||||||
* true if userDomain != senderDomain.
|
|
||||||
*/
|
|
||||||
val isFederated: Boolean? = null,
|
|
||||||
/**
|
|
||||||
* true if the current user is using matrix.org.
|
|
||||||
*/
|
|
||||||
val isMatrixDotOrg: Boolean? = null,
|
|
||||||
val name: Name,
|
|
||||||
/**
|
|
||||||
* UTDs can be permanent or temporary. If temporary, this field will
|
|
||||||
* contain the time it took to decrypt the message in milliseconds. If
|
|
||||||
* permanent should be -1.
|
|
||||||
*/
|
|
||||||
val timeToDecryptMillis: Int? = null,
|
|
||||||
/**
|
|
||||||
* true if the current user trusts their own identity (verified session)
|
|
||||||
* at time of decryption.
|
|
||||||
*/
|
|
||||||
val userTrustsOwnIdentity: Boolean? = null,
|
|
||||||
/**
|
|
||||||
* true if that unable to decrypt error was visible to the user.
|
|
||||||
*/
|
|
||||||
val wasVisibleToUser: Boolean? = null,
|
|
||||||
) : VectorAnalyticsEvent {
|
|
||||||
|
|
||||||
enum class Domain {
|
|
||||||
E2EE,
|
|
||||||
TO_DEVICE,
|
|
||||||
VOIP,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class Name {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* E2EE domain error. Decryption failed for a message sent before the
|
|
||||||
* device logged in, and key backup is not enabled.
|
|
||||||
*/
|
|
||||||
HistoricalMessage,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* E2EE domain error. The room key is known but is ratcheted (index >
|
|
||||||
* 0).
|
|
||||||
*/
|
|
||||||
OlmIndexError,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* E2EE domain error. Generic unknown inbound group session error.
|
|
||||||
*/
|
|
||||||
OlmKeysNotSentError,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* E2EE domain error. Any other decryption error (missing field, format
|
|
||||||
* errors...).
|
|
||||||
*/
|
|
||||||
OlmUnspecifiedError,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TO_DEVICE domain error. The to-device message failed to decrypt.
|
|
||||||
*/
|
|
||||||
ToDeviceFailedToDecrypt,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* E2EE domain error. Decryption failed due to unknown error.
|
|
||||||
*/
|
|
||||||
UnknownError,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* VOIP domain error. ICE negotiation failed.
|
|
||||||
*/
|
|
||||||
VoipIceFailed,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* VOIP domain error. ICE negotiation timed out.
|
|
||||||
*/
|
|
||||||
VoipIceTimeout,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* VOIP domain error. The call invite timed out.
|
|
||||||
*/
|
|
||||||
VoipInviteTimeout,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* VOIP domain error. The user hung up the call.
|
|
||||||
*/
|
|
||||||
VoipUserHangup,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* VOIP domain error. The user's media failed to start.
|
|
||||||
*/
|
|
||||||
VoipUserMediaFailed,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class CryptoSDK {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Legacy crypto backend specific to each platform.
|
|
||||||
*/
|
|
||||||
Legacy,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cross-platform crypto backend written in Rust.
|
|
||||||
*/
|
|
||||||
Rust,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class CryptoModule {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Native / legacy crypto module specific to each platform.
|
|
||||||
*/
|
|
||||||
Native,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shared / cross-platform crypto module written in Rust.
|
|
||||||
*/
|
|
||||||
Rust,
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getName() = "Error"
|
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
context?.let { put("context", it) }
|
|
||||||
cryptoModule?.let { put("cryptoModule", it.name) }
|
|
||||||
cryptoSDK?.let { put("cryptoSDK", it.name) }
|
|
||||||
put("domain", domain.name)
|
|
||||||
eventLocalAgeMillis?.let { put("eventLocalAgeMillis", it) }
|
|
||||||
isFederated?.let { put("isFederated", it) }
|
|
||||||
isMatrixDotOrg?.let { put("isMatrixDotOrg", it) }
|
|
||||||
put("name", name.name)
|
|
||||||
timeToDecryptMillis?.let { put("timeToDecryptMillis", it) }
|
|
||||||
userTrustsOwnIdentity?.let { put("userTrustsOwnIdentity", it) }
|
|
||||||
wasVisibleToUser?.let { put("wasVisibleToUser", it) }
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,509 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when the user clicks/taps/activates a UI element.
|
|
||||||
*/
|
|
||||||
data class Interaction(
|
|
||||||
/**
|
|
||||||
* The index of the element, if its in a list of elements.
|
|
||||||
*/
|
|
||||||
val index: Int? = null,
|
|
||||||
/**
|
|
||||||
* The manner with which the user activated the UI element.
|
|
||||||
*/
|
|
||||||
val interactionType: InteractionType? = null,
|
|
||||||
/**
|
|
||||||
* The unique name of this element.
|
|
||||||
*/
|
|
||||||
val name: Name,
|
|
||||||
) : VectorAnalyticsEvent {
|
|
||||||
|
|
||||||
enum class Name {
|
|
||||||
/**
|
|
||||||
* User tapped the All filter in the All Chats filter tab.
|
|
||||||
*/
|
|
||||||
MobileAllChatsFilterAll,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User tapped the Favourites filter in the All Chats filter tab.
|
|
||||||
*/
|
|
||||||
MobileAllChatsFilterFavourites,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User tapped the People filter in the All Chats filter tab.
|
|
||||||
*/
|
|
||||||
MobileAllChatsFilterPeople,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User tapped the Unreads filter in the All Chats filter tab.
|
|
||||||
*/
|
|
||||||
MobileAllChatsFilterUnreads,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User disabled filters from the all chats layout settings.
|
|
||||||
*/
|
|
||||||
MobileAllChatsFiltersDisabled,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User enabled filters from the all chats layout settings.
|
|
||||||
*/
|
|
||||||
MobileAllChatsFiltersEnabled,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User disabled recents from the all chats layout settings.
|
|
||||||
*/
|
|
||||||
MobileAllChatsRecentsDisabled,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User enabled recents from the all chats layout settings.
|
|
||||||
*/
|
|
||||||
MobileAllChatsRecentsEnabled,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User tapped on Add to Home button on Room Details screen.
|
|
||||||
*/
|
|
||||||
MobileRoomAddHome,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User switched the favourite toggle on Room Details screen.
|
|
||||||
*/
|
|
||||||
MobileRoomFavouriteToggle,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User tapped on Leave Room button on Room Details screen.
|
|
||||||
*/
|
|
||||||
MobileRoomLeave,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User adjusted their favourite rooms using the context menu on a room
|
|
||||||
* in the room list.
|
|
||||||
*/
|
|
||||||
MobileRoomListRoomContextMenuFavouriteToggle,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User adjusted their unread rooms using the context menu on a room in
|
|
||||||
* the room list.
|
|
||||||
*/
|
|
||||||
MobileRoomListRoomContextMenuUnreadToggle,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User tapped on Threads button on Room screen.
|
|
||||||
*/
|
|
||||||
MobileRoomThreadListButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User tapped on a thread summary item on Room screen.
|
|
||||||
*/
|
|
||||||
MobileRoomThreadSummaryItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User validated the creation of a new space.
|
|
||||||
*/
|
|
||||||
MobileSpaceCreationValidated,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User tapped on the filter button on ThreadList screen.
|
|
||||||
*/
|
|
||||||
MobileThreadListFilterItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User selected a thread on ThreadList screen.
|
|
||||||
*/
|
|
||||||
MobileThreadListThreadItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User tapped the already selected space from the space list.
|
|
||||||
*/
|
|
||||||
SpacePanelSelectedSpace,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User tapped an unselected space from the space list -> space
|
|
||||||
* switching should occur.
|
|
||||||
*/
|
|
||||||
SpacePanelSwitchSpace,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User tapped an unselected sub space from the space list -> space
|
|
||||||
* switching should occur.
|
|
||||||
*/
|
|
||||||
SpacePanelSwitchSubSpace,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked the create room button in the add existing room to space
|
|
||||||
* dialog in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebAddExistingToSpaceDialogCreateRoomButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked the create DM button in the home page of Element
|
|
||||||
* Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebHomeCreateChatButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked the create room button in the home page of Element
|
|
||||||
* Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebHomeCreateRoomButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked the explore rooms button in the home page of Element
|
|
||||||
* Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebHomeExploreRoomsButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked on the mini avatar uploader in the home page of Element
|
|
||||||
* Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebHomeMiniAvatarUploadButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked the explore rooms button next to the search field at the
|
|
||||||
* top of the left panel in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebLeftPanelExploreRoomsButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked on the avatar uploader in the profile settings of
|
|
||||||
* Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebProfileSettingsAvatarUploadButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User interacted with pin to sidebar checkboxes in the quick settings
|
|
||||||
* menu of Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebQuickSettingsPinToSidebarCheckbox,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User interacted with the theme dropdown in the quick settings menu of
|
|
||||||
* Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebQuickSettingsThemeDropdown,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User accessed the room invite flow using the button at the top of the
|
|
||||||
* room member list in the right panel of Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRightPanelMemberListInviteButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User accessed room member list using the 'People' button in the right
|
|
||||||
* panel room summary card of Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRightPanelRoomInfoPeopleButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User accessed room settings using the 'Settings' button in the right
|
|
||||||
* panel room summary card of Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRightPanelRoomInfoSettingsButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User accessed room member list using the back button in the right
|
|
||||||
* panel user info card of Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRightPanelRoomUserInfoBackButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User invited someone to room by clicking invite on the right panel
|
|
||||||
* user info card in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRightPanelRoomUserInfoInviteButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked the threads 'show' filter dropdown in the threads panel
|
|
||||||
* in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRightPanelThreadPanelFilterDropdown,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked the create room button in the room directory of Element
|
|
||||||
* Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomDirectoryCreateRoomButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked the Threads button in the top right of a room in Element
|
|
||||||
* Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomHeaderButtonsThreadsButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User adjusted their favourites using the context menu on the header
|
|
||||||
* of a room in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomHeaderContextMenuFavouriteToggle,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User accessed the room invite flow using the context menu on the
|
|
||||||
* header of a room in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomHeaderContextMenuInviteItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User interacted with leave action in the context menu on the header
|
|
||||||
* of a room in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomHeaderContextMenuLeaveItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User accessed their room notification settings via the context menu
|
|
||||||
* on the header of a room in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomHeaderContextMenuNotificationsItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User accessed room member list using the context menu on the header
|
|
||||||
* of a room in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomHeaderContextMenuPeopleItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User accessed room settings using the context menu on the header of a
|
|
||||||
* room in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomHeaderContextMenuSettingsItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked the create DM button in the + context menu of the room
|
|
||||||
* list header in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomListHeaderPlusMenuCreateChatItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked the create room button in the + context menu of the room
|
|
||||||
* list header in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomListHeaderPlusMenuCreateRoomItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked the explore rooms button in the + context menu of the
|
|
||||||
* room list header in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomListHeaderPlusMenuExploreRoomsItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User adjusted their favourites using the context menu on a room tile
|
|
||||||
* in the room list in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomListRoomTileContextMenuFavouriteToggle,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User accessed the room invite flow using the context menu on a room
|
|
||||||
* tile in the room list in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomListRoomTileContextMenuInviteItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User interacted with leave action in the context menu on a room tile
|
|
||||||
* in the room list in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomListRoomTileContextMenuLeaveItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User marked a message as read using the context menu on a room tile
|
|
||||||
* in the room list in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomListRoomTileContextMenuMarkRead,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User marked a room as unread using the context menu on a room tile in
|
|
||||||
* the room list in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomListRoomTileContextMenuMarkUnread,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User accessed room settings using the context menu on a room tile in
|
|
||||||
* the room list in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomListRoomTileContextMenuSettingsItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User accessed their room notification settings via the context menu
|
|
||||||
* on a room tile in the room list in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomListRoomTileNotificationsMenu,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked the create DM button in the + context menu of the rooms
|
|
||||||
* sublist in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomListRoomsSublistPlusMenuCreateChatItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked the create room button in the + context menu of the
|
|
||||||
* rooms sublist in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomListRoomsSublistPlusMenuCreateRoomItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked the explore rooms button in the + context menu of the
|
|
||||||
* rooms sublist in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomListRoomsSublistPlusMenuExploreRoomsItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked on the button to return to the user onboarding list in
|
|
||||||
* the room list in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomListUserOnboardingButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked on the button to close the user onboarding button in the
|
|
||||||
* room list in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomListUserOnboardingIgnoreButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User interacted with leave action in the general tab of the room
|
|
||||||
* settings dialog in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomSettingsLeaveButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User interacted with the prompt to create a new room when adjusting
|
|
||||||
* security settings in an existing room in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomSettingsSecurityTabCreateNewRoomButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked a thread summary in the timeline of a room in Element
|
|
||||||
* Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomTimelineThreadSummaryButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User interacted with the theme radio selector in the Appearance tab
|
|
||||||
* of Settings in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebSettingsAppearanceTabThemeSelector,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User interacted with the pre-built space checkboxes in the Sidebar
|
|
||||||
* tab of Settings in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebSettingsSidebarTabSpacesCheckbox,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked the explore rooms button in the context menu of a space
|
|
||||||
* in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebSpaceContextMenuExploreRoomsItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked the home button in the context menu of a space in
|
|
||||||
* Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebSpaceContextMenuHomeItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked the new room button in the context menu of a space in
|
|
||||||
* Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebSpaceContextMenuNewRoomItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked the new room button in the context menu on the space
|
|
||||||
* home in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebSpaceHomeCreateRoomButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked the back button on a Thread view going back to the
|
|
||||||
* Threads Panel of Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebThreadViewBackButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked on the Threads Activity Centre button of Element
|
|
||||||
* Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebThreadsActivityCentreButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked on a room in the Threads Activity Centre of Element
|
|
||||||
* Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebThreadsActivityCentreRoomItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User selected a thread in the Threads panel in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebThreadsPanelThreadItem,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked the theme toggle button in the user menu of Element
|
|
||||||
* Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebUserMenuThemeToggleButton,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked on the send DM CTA in the header of the new user
|
|
||||||
* onboarding page in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebUserOnboardingHeaderSendDm,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked on the action of the download apps task on the new user
|
|
||||||
* onboarding page in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebUserOnboardingTaskDownloadApps,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked on the action of the enable notifications task on the
|
|
||||||
* new user onboarding page in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebUserOnboardingTaskEnableNotifications,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked on the action of the find people task on the new user
|
|
||||||
* onboarding page in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebUserOnboardingTaskSendDm,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User clicked on the action of the your profile task on the new user
|
|
||||||
* onboarding page in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebUserOnboardingTaskSetupProfile,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class InteractionType {
|
|
||||||
Keyboard,
|
|
||||||
Pointer,
|
|
||||||
Touch,
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getName() = "Interaction"
|
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
index?.let { put("index", it) }
|
|
||||||
interactionType?.let { put("interactionType", it.name) }
|
|
||||||
put("name", name.name)
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when the user joins a room.
|
|
||||||
*/
|
|
||||||
data class JoinedRoom(
|
|
||||||
/**
|
|
||||||
* Whether the room is a DM.
|
|
||||||
*/
|
|
||||||
val isDM: Boolean,
|
|
||||||
/**
|
|
||||||
* Whether the room is a Space.
|
|
||||||
*/
|
|
||||||
val isSpace: Boolean,
|
|
||||||
/**
|
|
||||||
* The size of the room.
|
|
||||||
*/
|
|
||||||
val roomSize: RoomSize,
|
|
||||||
/**
|
|
||||||
* The trigger for a room being joined if known.
|
|
||||||
*/
|
|
||||||
val trigger: Trigger? = null,
|
|
||||||
) : VectorAnalyticsEvent {
|
|
||||||
|
|
||||||
enum class Trigger {
|
|
||||||
/**
|
|
||||||
* Room joined via an invite.
|
|
||||||
*/
|
|
||||||
Invite,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room joined via link.
|
|
||||||
*/
|
|
||||||
MobilePermalink,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room joined via a push/desktop notification.
|
|
||||||
*/
|
|
||||||
Notification,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room joined via the public rooms directory.
|
|
||||||
*/
|
|
||||||
RoomDirectory,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room joined via its preview.
|
|
||||||
*/
|
|
||||||
RoomPreview,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room joined via the /join slash command.
|
|
||||||
*/
|
|
||||||
SlashCommand,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room joined via the space hierarchy view.
|
|
||||||
*/
|
|
||||||
SpaceHierarchy,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room joined via a timeline pill or link in another room.
|
|
||||||
*/
|
|
||||||
Timeline,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class RoomSize {
|
|
||||||
ElevenToOneHundred,
|
|
||||||
MoreThanAThousand,
|
|
||||||
One,
|
|
||||||
OneHundredAndOneToAThousand,
|
|
||||||
ThreeToTen,
|
|
||||||
Two,
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getName() = "JoinedRoom"
|
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
put("isDM", isDM)
|
|
||||||
put("isSpace", isSpace)
|
|
||||||
put("roomSize", roomSize.name)
|
|
||||||
trigger?.let { put("trigger", it.name) }
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,353 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsScreen
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when the user changed screen on Element Android/iOS.
|
|
||||||
*/
|
|
||||||
data class MobileScreen(
|
|
||||||
/**
|
|
||||||
* How long the screen was displayed for in milliseconds.
|
|
||||||
*/
|
|
||||||
val durationMs: Int? = null,
|
|
||||||
val screenName: ScreenName,
|
|
||||||
) : VectorAnalyticsScreen {
|
|
||||||
|
|
||||||
enum class ScreenName {
|
|
||||||
/**
|
|
||||||
* The screen that displays the user's breadcrumbs.
|
|
||||||
*/
|
|
||||||
Breadcrumbs,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen shown to create a poll.
|
|
||||||
*/
|
|
||||||
CreatePollView,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen shown to create a new (non-direct) room.
|
|
||||||
*/
|
|
||||||
CreateRoom,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen shown to create a new space.
|
|
||||||
*/
|
|
||||||
CreateSpace,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The confirmation screen shown before deactivating an account.
|
|
||||||
*/
|
|
||||||
DeactivateAccount,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The tab on mobile that displays the dialpad.
|
|
||||||
*/
|
|
||||||
Dialpad,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen shown to edit a poll.
|
|
||||||
*/
|
|
||||||
EditPollView,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Favourites tab on mobile that lists your favourite people/rooms.
|
|
||||||
*/
|
|
||||||
Favourites,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The form for the forgot password use case.
|
|
||||||
*/
|
|
||||||
ForgotPassword,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Legacy: The screen that shows information about a specific group.
|
|
||||||
*/
|
|
||||||
Group,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Home tab on iOS | possibly the same on Android?
|
|
||||||
*/
|
|
||||||
Home,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen shown to share a link to download the app.
|
|
||||||
*/
|
|
||||||
InviteFriends,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via space bottom sheet list.
|
|
||||||
*/
|
|
||||||
Invites,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen shown to share location.
|
|
||||||
*/
|
|
||||||
LocationSend,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen shown to view a shared location.
|
|
||||||
*/
|
|
||||||
LocationView,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen that displays the login flow (when the user already has an
|
|
||||||
* account).
|
|
||||||
*/
|
|
||||||
Login,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Legacy: The screen that shows all groups/communities you have joined.
|
|
||||||
*/
|
|
||||||
MyGroups,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen containing tests to help user to fix issues around
|
|
||||||
* notifications.
|
|
||||||
*/
|
|
||||||
NotificationTroubleshoot,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The People tab on mobile that lists all the DM rooms you have joined.
|
|
||||||
*/
|
|
||||||
People,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen that displays the registration flow (when the user wants
|
|
||||||
* to create an account).
|
|
||||||
*/
|
|
||||||
Register,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen that displays the messages and events received in a room.
|
|
||||||
*/
|
|
||||||
Room,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The room addresses screen shown from the Room Details screen.
|
|
||||||
*/
|
|
||||||
RoomAddresses,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen shown when tapping the name of a room from the Room
|
|
||||||
* screen.
|
|
||||||
*/
|
|
||||||
RoomDetails,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen that lists public rooms for you to discover.
|
|
||||||
*/
|
|
||||||
RoomDirectory,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen that lists all the user's rooms and let them filter the
|
|
||||||
* rooms.
|
|
||||||
*/
|
|
||||||
RoomFilter,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen that displays the list of members that are part of a room.
|
|
||||||
*/
|
|
||||||
RoomMembers,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The notifications settings screen shown from the Room Details screen.
|
|
||||||
*/
|
|
||||||
RoomNotifications,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The roles permissions screen shown from the Room Details screen.
|
|
||||||
*/
|
|
||||||
RoomPermissions,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Screen that displays room preview if user hasn't joined yet.
|
|
||||||
*/
|
|
||||||
RoomPreview,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen that allows you to search for messages/files in a specific
|
|
||||||
* room.
|
|
||||||
*/
|
|
||||||
RoomSearch,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The settings screen shown from the Room Details screen.
|
|
||||||
*/
|
|
||||||
RoomSettings,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen that allows you to see all of the files sent in a specific
|
|
||||||
* room.
|
|
||||||
*/
|
|
||||||
RoomUploads,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Rooms tab on mobile that lists all the (non-direct) rooms you've
|
|
||||||
* joined.
|
|
||||||
*/
|
|
||||||
Rooms,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Files tab shown in the global search screen on Mobile.
|
|
||||||
*/
|
|
||||||
SearchFiles,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Messages tab shown in the global search screen on Mobile.
|
|
||||||
*/
|
|
||||||
SearchMessages,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The People tab shown in the global search screen on Mobile.
|
|
||||||
*/
|
|
||||||
SearchPeople,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Rooms tab shown in the global search screen on Mobile.
|
|
||||||
*/
|
|
||||||
SearchRooms,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The global settings screen shown in the app.
|
|
||||||
*/
|
|
||||||
Settings,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The advanced settings screen (developer mode, rageshake, push
|
|
||||||
* notification rules).
|
|
||||||
*/
|
|
||||||
SettingsAdvanced,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The settings screen to change the default notification options.
|
|
||||||
*/
|
|
||||||
SettingsDefaultNotifications,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The settings screen with general profile settings.
|
|
||||||
*/
|
|
||||||
SettingsGeneral,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Help and About screen.
|
|
||||||
*/
|
|
||||||
SettingsHelp,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The settings screen with list of the ignored users.
|
|
||||||
*/
|
|
||||||
SettingsIgnoredUsers,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The experimental features settings screen.
|
|
||||||
*/
|
|
||||||
SettingsLabs,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The settings screen with legals information.
|
|
||||||
*/
|
|
||||||
SettingsLegals,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The settings screen to manage notification mentions and keywords.
|
|
||||||
*/
|
|
||||||
SettingsMentionsAndKeywords,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The notifications settings screen.
|
|
||||||
*/
|
|
||||||
SettingsNotifications,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The preferences screen (theme, language, editor preferences, etc.
|
|
||||||
*/
|
|
||||||
SettingsPreferences,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The global security settings screen.
|
|
||||||
*/
|
|
||||||
SettingsSecurity,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The calls settings screen.
|
|
||||||
*/
|
|
||||||
SettingsVoiceVideo,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The sidebar shown on mobile with spaces, settings etc.
|
|
||||||
*/
|
|
||||||
Sidebar,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via space bottom sheet list.
|
|
||||||
*/
|
|
||||||
SpaceBottomSheet,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Screen that displays the list of rooms and spaces of a space.
|
|
||||||
*/
|
|
||||||
SpaceExploreRooms,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Screen that displays the list of members of a space.
|
|
||||||
*/
|
|
||||||
SpaceMembers,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The bottom sheet that list all space options.
|
|
||||||
*/
|
|
||||||
SpaceMenu,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen shown to create a new direct room.
|
|
||||||
*/
|
|
||||||
StartChat,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen shown to select which room directory you'd like to use.
|
|
||||||
*/
|
|
||||||
SwitchDirectory,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Screen that displays list of threads for a room.
|
|
||||||
*/
|
|
||||||
ThreadList,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A screen that shows information about a room member.
|
|
||||||
*/
|
|
||||||
User,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The splash screen.
|
|
||||||
*/
|
|
||||||
Welcome,
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getName() = screenName.name
|
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
durationMs?.let { put("durationMs", it) }
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when the user runs the troubleshoot notification test suite.
|
|
||||||
*/
|
|
||||||
data class NotificationTroubleshoot(
|
|
||||||
/**
|
|
||||||
* Whether one or more tests are in error.
|
|
||||||
*/
|
|
||||||
val hasError: Boolean,
|
|
||||||
) : VectorAnalyticsEvent {
|
|
||||||
|
|
||||||
override fun getName() = "NotificationTroubleshoot"
|
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
put("hasError", hasError)
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered after timing an operation in the app.
|
|
||||||
*/
|
|
||||||
data class PerformanceTimer(
|
|
||||||
/**
|
|
||||||
* Client defined, can be used for debugging.
|
|
||||||
*/
|
|
||||||
val context: String? = null,
|
|
||||||
/**
|
|
||||||
* Client defined, an optional value to indicate how many items were
|
|
||||||
* handled during the operation.
|
|
||||||
*/
|
|
||||||
val itemCount: Int? = null,
|
|
||||||
/**
|
|
||||||
* The timer that is being reported.
|
|
||||||
*/
|
|
||||||
val name: Name,
|
|
||||||
/**
|
|
||||||
* The time reported by the timer in milliseconds.
|
|
||||||
*/
|
|
||||||
val timeMs: Int,
|
|
||||||
) : VectorAnalyticsEvent {
|
|
||||||
|
|
||||||
enum class Name {
|
|
||||||
/**
|
|
||||||
* The time spent parsing the response from an initial /sync request. In
|
|
||||||
* this case, `itemCount` should contain the number of joined rooms.
|
|
||||||
*/
|
|
||||||
InitialSyncParsing,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The time spent waiting for a response to an initial /sync request. In
|
|
||||||
* this case, `itemCount` should contain the number of joined rooms.
|
|
||||||
*/
|
|
||||||
InitialSyncRequest,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The time taken to display an event in the timeline that was opened
|
|
||||||
* from a notification.
|
|
||||||
*/
|
|
||||||
NotificationsOpenEvent,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The duration of a regular /sync request when resuming the app. In
|
|
||||||
* this case, `itemCount` should contain the number of joined rooms in
|
|
||||||
* the response.
|
|
||||||
*/
|
|
||||||
StartupIncrementalSync,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The duration of an initial /sync request during startup (if the store
|
|
||||||
* has been wiped). In this case, `itemCount` should contain the number
|
|
||||||
* of joined rooms.
|
|
||||||
*/
|
|
||||||
StartupInitialSync,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* How long the app launch screen is displayed for.
|
|
||||||
*/
|
|
||||||
StartupLaunchScreen,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The time to preload data in the MXStore on iOS. In this case,
|
|
||||||
* `itemCount` should contain the number of rooms in the store.
|
|
||||||
*/
|
|
||||||
StartupStorePreload,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The time to load all data from the store (including
|
|
||||||
* StartupStorePreload time). In this case, `itemCount` should contain
|
|
||||||
* the number of rooms loaded into the session
|
|
||||||
*/
|
|
||||||
StartupStoreReady,
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getName() = "PerformanceTimer"
|
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
context?.let { put("context", it) }
|
|
||||||
itemCount?.let { put("itemCount", it) }
|
|
||||||
put("name", name.name)
|
|
||||||
put("timeMs", timeMs)
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when the user changes a permission status.
|
|
||||||
*/
|
|
||||||
data class PermissionChanged(
|
|
||||||
/**
|
|
||||||
* Whether the permission has been granted by the user.
|
|
||||||
*/
|
|
||||||
val granted: Boolean,
|
|
||||||
/**
|
|
||||||
* The name of the permission.
|
|
||||||
*/
|
|
||||||
val permission: Permission,
|
|
||||||
) : VectorAnalyticsEvent {
|
|
||||||
|
|
||||||
enum class Permission {
|
|
||||||
/**
|
|
||||||
* Permissions related to sending notifications have changed.
|
|
||||||
*/
|
|
||||||
Notification,
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getName() = "PermissionChanged"
|
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
put("granted", granted)
|
|
||||||
put("permission", permission.name)
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when a poll is created or edited.
|
|
||||||
*/
|
|
||||||
data class PollCreation(
|
|
||||||
/**
|
|
||||||
* Whether this poll has been created or edited.
|
|
||||||
*/
|
|
||||||
val action: Action,
|
|
||||||
/**
|
|
||||||
* Whether this poll is undisclosed.
|
|
||||||
*/
|
|
||||||
val isUndisclosed: Boolean,
|
|
||||||
/**
|
|
||||||
* Number of answers in the poll.
|
|
||||||
*/
|
|
||||||
val numberOfAnswers: Int,
|
|
||||||
) : VectorAnalyticsEvent {
|
|
||||||
|
|
||||||
enum class Action {
|
|
||||||
/**
|
|
||||||
* Newly created poll.
|
|
||||||
*/
|
|
||||||
Create,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Edit of an existing poll.
|
|
||||||
*/
|
|
||||||
Edit,
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getName() = "PollCreation"
|
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
put("action", action.name)
|
|
||||||
put("isUndisclosed", isUndisclosed)
|
|
||||||
put("numberOfAnswers", numberOfAnswers)
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when a poll has been ended.
|
|
||||||
*/
|
|
||||||
data class PollEnd(
|
|
||||||
/**
|
|
||||||
* Do not use this. Remove this property when the kotlin type generator
|
|
||||||
* can properly generate types without properties other than the event
|
|
||||||
* name.
|
|
||||||
*/
|
|
||||||
val doNotUse: Boolean? = null,
|
|
||||||
) : VectorAnalyticsEvent {
|
|
||||||
|
|
||||||
override fun getName() = "PollEnd"
|
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
doNotUse?.let { put("doNotUse", it) }
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when a poll vote has been cast.
|
|
||||||
*/
|
|
||||||
data class PollVote(
|
|
||||||
/**
|
|
||||||
* Do not use this. Remove this property when the kotlin type generator
|
|
||||||
* can properly generate types without properties other than the event
|
|
||||||
* name.
|
|
||||||
*/
|
|
||||||
val doNotUse: Boolean? = null,
|
|
||||||
) : VectorAnalyticsEvent {
|
|
||||||
|
|
||||||
override fun getName() = "PollVote"
|
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
doNotUse?.let { put("doNotUse", it) }
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,137 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when a moderation action is performed within a room.
|
|
||||||
*/
|
|
||||||
data class RoomModeration(
|
|
||||||
/**
|
|
||||||
* The action that was performed.
|
|
||||||
*/
|
|
||||||
val action: Action,
|
|
||||||
/**
|
|
||||||
* When the action sets a particular power level, this is the suggested
|
|
||||||
* role for that the power level.
|
|
||||||
*/
|
|
||||||
val role: Role? = null,
|
|
||||||
) : VectorAnalyticsEvent {
|
|
||||||
|
|
||||||
enum class Action {
|
|
||||||
/**
|
|
||||||
* Banned a room member.
|
|
||||||
*/
|
|
||||||
BanMember,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changed a room member's power level.
|
|
||||||
*/
|
|
||||||
ChangeMemberRole,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changed the power level required to ban room members.
|
|
||||||
*/
|
|
||||||
ChangePermissionsBanMembers,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changed the power level required to invite users to the room.
|
|
||||||
*/
|
|
||||||
ChangePermissionsInviteUsers,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changed the power level required to kick room members.
|
|
||||||
*/
|
|
||||||
ChangePermissionsKickMembers,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changed the power level required to redact messages in the room.
|
|
||||||
*/
|
|
||||||
ChangePermissionsRedactMessages,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changed the power level required to set the room's avatar.
|
|
||||||
*/
|
|
||||||
ChangePermissionsRoomAvatar,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changed the power level required to set the room's name.
|
|
||||||
*/
|
|
||||||
ChangePermissionsRoomName,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changed the power level required to set the room's topic.
|
|
||||||
*/
|
|
||||||
ChangePermissionsRoomTopic,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changed the power level required to send messages in the room.
|
|
||||||
*/
|
|
||||||
ChangePermissionsSendMessages,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kicked a room member.
|
|
||||||
*/
|
|
||||||
KickMember,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset all of the room permissions back to their default values.
|
|
||||||
*/
|
|
||||||
ResetPermissions,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unbanned a room member.
|
|
||||||
*/
|
|
||||||
UnbanMember,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class Role {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A power level of 100.
|
|
||||||
*/
|
|
||||||
Administrator,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A power level of 50.
|
|
||||||
*/
|
|
||||||
Moderator,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Any other power level.
|
|
||||||
*/
|
|
||||||
Other,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A power level of 0.
|
|
||||||
*/
|
|
||||||
User,
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getName() = "RoomModeration"
|
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
put("action", action.name)
|
|
||||||
role?.let { put("role", it.name) }
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,84 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered once onboarding has completed, but only if the user registered a
|
|
||||||
* new account.
|
|
||||||
*/
|
|
||||||
data class Signup(
|
|
||||||
/**
|
|
||||||
* The type of authentication that was used to sign up.
|
|
||||||
*/
|
|
||||||
val authenticationType: AuthenticationType,
|
|
||||||
) : VectorAnalyticsEvent {
|
|
||||||
|
|
||||||
enum class AuthenticationType {
|
|
||||||
/**
|
|
||||||
* Social login using Apple.
|
|
||||||
*/
|
|
||||||
Apple,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Social login using Facebook.
|
|
||||||
*/
|
|
||||||
Facebook,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Social login using GitHub.
|
|
||||||
*/
|
|
||||||
GitHub,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Social login using GitLab.
|
|
||||||
*/
|
|
||||||
GitLab,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Social login using Google.
|
|
||||||
*/
|
|
||||||
Google,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registration using some other mechanism such as fallback.
|
|
||||||
*/
|
|
||||||
Other,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registration with a username and password.
|
|
||||||
*/
|
|
||||||
Password,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registration using another SSO provider.
|
|
||||||
*/
|
|
||||||
SSO,
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getName() = "Signup"
|
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
put("authenticationType", authenticationType.name)
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when the user runs a slash command in their composer.
|
|
||||||
*/
|
|
||||||
data class SlashCommand(
|
|
||||||
/**
|
|
||||||
* The name of this command.
|
|
||||||
*/
|
|
||||||
val command: Command,
|
|
||||||
) : VectorAnalyticsEvent {
|
|
||||||
|
|
||||||
enum class Command {
|
|
||||||
Invite,
|
|
||||||
Part,
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getName() = "SlashCommand"
|
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
put("command", command.name)
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Super Properties are properties associated with events that are sent with
|
|
||||||
* every capture call, be it a $pageview, an autocaptured button click, or
|
|
||||||
* anything else.
|
|
||||||
*/
|
|
||||||
data class SuperProperties(
|
|
||||||
/**
|
|
||||||
* Used by web to identify the platform (Web Platform/Electron Platform).
|
|
||||||
*/
|
|
||||||
val appPlatform: String? = null,
|
|
||||||
/**
|
|
||||||
* Which crypto backend is the client currently using.
|
|
||||||
*/
|
|
||||||
val cryptoSDK: CryptoSDK? = null,
|
|
||||||
/**
|
|
||||||
* Version of the crypto backend.
|
|
||||||
*/
|
|
||||||
val cryptoSDKVersion: String? = null,
|
|
||||||
) {
|
|
||||||
|
|
||||||
enum class CryptoSDK {
|
|
||||||
/**
|
|
||||||
* Legacy crypto backend specific to each platform.
|
|
||||||
*/
|
|
||||||
Legacy,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cross-platform crypto backend written in Rust.
|
|
||||||
*/
|
|
||||||
Rust,
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
appPlatform?.let { put("appPlatform", it) }
|
|
||||||
cryptoSDK?.let { put("cryptoSDK", it.name) }
|
|
||||||
cryptoSDKVersion?.let { put("cryptoSDKVersion", it) }
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when the user becomes unauthenticated without actually clicking
|
|
||||||
* sign out(E.g. Due to expiry of an access token without a way to refresh).
|
|
||||||
*/
|
|
||||||
data class UnauthenticatedError(
|
|
||||||
/**
|
|
||||||
* The error code as defined in matrix spec. The source of this error is
|
|
||||||
* from the homeserver.
|
|
||||||
*/
|
|
||||||
val errorCode: ErrorCode,
|
|
||||||
/**
|
|
||||||
* The reason for the error. The source of this error is from the
|
|
||||||
* homeserver, the reason can vary and is subject to change so there is
|
|
||||||
* no enum of possible values.
|
|
||||||
*/
|
|
||||||
val errorReason: String,
|
|
||||||
/**
|
|
||||||
* Whether the auth mechanism is refresh-token-based.
|
|
||||||
*/
|
|
||||||
val refreshTokenAuth: Boolean,
|
|
||||||
/**
|
|
||||||
* Whether a soft logout or hard logout was triggered.
|
|
||||||
*/
|
|
||||||
val softLogout: Boolean,
|
|
||||||
) : VectorAnalyticsEvent {
|
|
||||||
|
|
||||||
enum class ErrorCode {
|
|
||||||
M_FORBIDDEN,
|
|
||||||
M_UNKNOWN,
|
|
||||||
M_UNKNOWN_TOKEN,
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getName() = "UnauthenticatedError"
|
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
put("errorCode", errorCode.name)
|
|
||||||
put("errorReason", errorReason)
|
|
||||||
put("refreshTokenAuth", refreshTokenAuth)
|
|
||||||
put("softLogout", softLogout)
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,98 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The user properties to apply when identifying. This is not an event
|
|
||||||
* definition. These properties must all be device independent.
|
|
||||||
*/
|
|
||||||
data class UserProperties(
|
|
||||||
/**
|
|
||||||
* The active filter in the All Chats screen.
|
|
||||||
*/
|
|
||||||
val allChatsActiveFilter: AllChatsActiveFilter? = null,
|
|
||||||
/**
|
|
||||||
* The selected messaging use case during the onboarding flow.
|
|
||||||
*/
|
|
||||||
val ftueUseCaseSelection: FtueUseCaseSelection? = null,
|
|
||||||
/**
|
|
||||||
* Number of joined rooms the user has favourited.
|
|
||||||
*/
|
|
||||||
val numFavouriteRooms: Int? = null,
|
|
||||||
/**
|
|
||||||
* Number of spaces (and sub-spaces) the user is joined to.
|
|
||||||
*/
|
|
||||||
val numSpaces: Int? = null,
|
|
||||||
) {
|
|
||||||
|
|
||||||
enum class FtueUseCaseSelection {
|
|
||||||
/**
|
|
||||||
* The third option, Communities.
|
|
||||||
*/
|
|
||||||
CommunityMessaging,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The first option, Friends and family.
|
|
||||||
*/
|
|
||||||
PersonalMessaging,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The footer option to skip the question.
|
|
||||||
*/
|
|
||||||
Skip,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The second option, Teams.
|
|
||||||
*/
|
|
||||||
WorkMessaging,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class AllChatsActiveFilter {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filters are activated and All is selected.
|
|
||||||
*/
|
|
||||||
All,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filters are activated and Favourites is selected.
|
|
||||||
*/
|
|
||||||
Favourites,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filters are activated and People is selected.
|
|
||||||
*/
|
|
||||||
People,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filters are activated and Unreads is selected.
|
|
||||||
*/
|
|
||||||
Unreads,
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
allChatsActiveFilter?.let { put("allChatsActiveFilter", it.name) }
|
|
||||||
ftueUseCaseSelection?.let { put("ftueUseCaseSelection", it.name) }
|
|
||||||
numFavouriteRooms?.let { put("numFavouriteRooms", it) }
|
|
||||||
numSpaces?.let { put("numSpaces", it) }
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,312 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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.analytics.plan
|
|
||||||
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
|
|
||||||
// GENERATED FILE, DO NOT EDIT. FOR MORE INFORMATION VISIT
|
|
||||||
// https://github.com/matrix-org/matrix-analytics-events/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when the user changes rooms.
|
|
||||||
*/
|
|
||||||
data class ViewRoom(
|
|
||||||
/**
|
|
||||||
* active space when user navigated to the room.
|
|
||||||
*/
|
|
||||||
val activeSpace: ActiveSpace? = null,
|
|
||||||
/**
|
|
||||||
* Whether the room is a DM.
|
|
||||||
*/
|
|
||||||
val isDM: Boolean? = null,
|
|
||||||
/**
|
|
||||||
* Whether the room is a Space.
|
|
||||||
*/
|
|
||||||
val isSpace: Boolean? = null,
|
|
||||||
/**
|
|
||||||
* The reason for the room change if known.
|
|
||||||
*/
|
|
||||||
val trigger: Trigger? = null,
|
|
||||||
/**
|
|
||||||
* Whether the interaction was performed via the keyboard input.
|
|
||||||
*/
|
|
||||||
val viaKeyboard: Boolean? = null,
|
|
||||||
) : VectorAnalyticsEvent {
|
|
||||||
|
|
||||||
enum class Trigger {
|
|
||||||
/**
|
|
||||||
* Room accessed due to being just created.
|
|
||||||
*/
|
|
||||||
Created,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room switched due to user interacting with a message search result.
|
|
||||||
*/
|
|
||||||
MessageSearch,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room switched due to user selecting a user to go to a DM with.
|
|
||||||
*/
|
|
||||||
MessageUser,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via space explore.
|
|
||||||
*/
|
|
||||||
MobileExploreRooms,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room switched due to user interacting with a file search result.
|
|
||||||
*/
|
|
||||||
MobileFileSearch,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via interacting with the incall screen.
|
|
||||||
*/
|
|
||||||
MobileInCall,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed during external sharing.
|
|
||||||
*/
|
|
||||||
MobileLinkShare,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via link.
|
|
||||||
*/
|
|
||||||
MobilePermalink,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via interacting with direct chat item in the room
|
|
||||||
* contact detail screen.
|
|
||||||
*/
|
|
||||||
MobileRoomMemberDetail,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via preview.
|
|
||||||
*/
|
|
||||||
MobileRoomPreview,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room switched due to user interacting with a room search result.
|
|
||||||
*/
|
|
||||||
MobileRoomSearch,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via interacting with direct chat item in the search
|
|
||||||
* contact detail screen.
|
|
||||||
*/
|
|
||||||
MobileSearchContactDetail,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via space bottom sheet list.
|
|
||||||
*/
|
|
||||||
MobileSpaceBottomSheet,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via interacting with direct chat item in the space
|
|
||||||
* contact detail screen.
|
|
||||||
*/
|
|
||||||
MobileSpaceMemberDetail,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via space members list.
|
|
||||||
*/
|
|
||||||
MobileSpaceMembers,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Space accessed via interacting with the space menu.
|
|
||||||
*/
|
|
||||||
MobileSpaceMenu,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Space accessed via interacting with a space settings menu item.
|
|
||||||
*/
|
|
||||||
MobileSpaceSettings,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via a push/desktop notification.
|
|
||||||
*/
|
|
||||||
Notification,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via the predecessor link at the top of the upgraded
|
|
||||||
* room.
|
|
||||||
*/
|
|
||||||
Predecessor,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via the public rooms directory.
|
|
||||||
*/
|
|
||||||
RoomDirectory,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via the room list.
|
|
||||||
*/
|
|
||||||
RoomList,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via a shortcut.
|
|
||||||
*/
|
|
||||||
Shortcut,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via a slash command in Element Web/Desktop like /goto.
|
|
||||||
*/
|
|
||||||
SlashCommand,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via the space hierarchy view.
|
|
||||||
*/
|
|
||||||
SpaceHierarchy,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via a timeline pill or link in another room.
|
|
||||||
*/
|
|
||||||
Timeline,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via a tombstone at the bottom of a predecessor room.
|
|
||||||
*/
|
|
||||||
Tombstone,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room switched due to user interacting with incoming verification
|
|
||||||
* request.
|
|
||||||
*/
|
|
||||||
VerificationRequest,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room switched due to accepting a call in a different room in Element
|
|
||||||
* Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebAcceptCall,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room switched due to making a call via the dial pad in Element
|
|
||||||
* Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebDialPad,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via interacting with the floating call or Jitsi PIP in
|
|
||||||
* Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebFloatingCallWindow,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via the shortcut in Element Web/Desktop's forward
|
|
||||||
* modal.
|
|
||||||
*/
|
|
||||||
WebForwardShortcut,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via the Element Web/Desktop horizontal breadcrumbs at
|
|
||||||
* the top of the room list.
|
|
||||||
*/
|
|
||||||
WebHorizontalBreadcrumbs,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via an Element Web/Desktop keyboard shortcut like go to
|
|
||||||
* next room with unread messages.
|
|
||||||
*/
|
|
||||||
WebKeyboardShortcut,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via Element Web/Desktop's notification panel.
|
|
||||||
*/
|
|
||||||
WebNotificationPanel,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via the predecessor link in Settings > Advanced in
|
|
||||||
* Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebPredecessorSettings,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via clicking on a notifications badge on a room list
|
|
||||||
* sublist in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebRoomListNotificationBadge,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room switched due to the user changing space in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebSpaceContextSwitch,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via clicking on the notifications badge on the
|
|
||||||
* currently selected space in Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebSpacePanelNotificationBadge,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via interacting with the Threads Activity Centre in
|
|
||||||
* Element Web/Desktop.
|
|
||||||
*/
|
|
||||||
WebThreadsActivityCentre,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via Element Web/Desktop's Unified Search modal.
|
|
||||||
*/
|
|
||||||
WebUnifiedSearch,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room accessed via the Element Web/Desktop vertical breadcrumb hover
|
|
||||||
* menu.
|
|
||||||
*/
|
|
||||||
WebVerticalBreadcrumbs,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Room switched due to widget interaction.
|
|
||||||
*/
|
|
||||||
Widget,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class ActiveSpace {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Active space is Home.
|
|
||||||
*/
|
|
||||||
Home,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Active space is a meta space.
|
|
||||||
*/
|
|
||||||
Meta,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Active space is a private space.
|
|
||||||
*/
|
|
||||||
Private,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Active space is a public space.
|
|
||||||
*/
|
|
||||||
Public,
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getName() = "ViewRoom"
|
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
|
||||||
return mutableMapOf<String, Any>().apply {
|
|
||||||
activeSpace?.let { put("activeSpace", it.name) }
|
|
||||||
isDM?.let { put("isDM", it) }
|
|
||||||
isSpace?.let { put("isSpace", it) }
|
|
||||||
trigger?.let { put("trigger", it.name) }
|
|
||||||
viaKeyboard?.let { put("viaKeyboard", it) }
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -52,7 +52,7 @@ sealed class RoomDetailAction : VectorViewModelAction {
|
||||||
|
|
||||||
data class ResendMessage(val eventId: String) : RoomDetailAction()
|
data class ResendMessage(val eventId: String) : RoomDetailAction()
|
||||||
data class RemoveFailedEcho(val eventId: String) : RoomDetailAction()
|
data class RemoveFailedEcho(val eventId: String) : RoomDetailAction()
|
||||||
data class CancelSend(val eventId: String, val force: Boolean) : RoomDetailAction()
|
data class CancelSend(val event: TimelineEvent, val force: Boolean) : RoomDetailAction()
|
||||||
|
|
||||||
data class VoteToPoll(val eventId: String, val optionKey: String) : RoomDetailAction()
|
data class VoteToPoll(val eventId: String, val optionKey: String) : RoomDetailAction()
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,10 @@ sealed class RoomDetailViewEvents : VectorViewEvents {
|
||||||
val mimeType: String?
|
val mimeType: String?
|
||||||
) : RoomDetailViewEvents()
|
) : RoomDetailViewEvents()
|
||||||
|
|
||||||
|
data class RevokeFilePermission(
|
||||||
|
val uri: Uri
|
||||||
|
) : RoomDetailViewEvents()
|
||||||
|
|
||||||
data class DisplayAndAcceptCall(val call: WebRtcCall) : RoomDetailViewEvents()
|
data class DisplayAndAcceptCall(val call: WebRtcCall) : RoomDetailViewEvents()
|
||||||
|
|
||||||
object DisplayPromptForIntegrationManager : RoomDetailViewEvents()
|
object DisplayPromptForIntegrationManager : RoomDetailViewEvents()
|
||||||
|
|
|
@ -450,8 +450,11 @@ class TimelineFragment :
|
||||||
is RoomDetailViewEvents.ScDbgReadTracking -> handleScDbgReadTracking(it)
|
is RoomDetailViewEvents.ScDbgReadTracking -> handleScDbgReadTracking(it)
|
||||||
RoomDetailViewEvents.OpenElementCallWidget -> handleOpenElementCallWidget()
|
RoomDetailViewEvents.OpenElementCallWidget -> handleOpenElementCallWidget()
|
||||||
RoomDetailViewEvents.DisplayPromptToStopVoiceBroadcast -> displayPromptToStopVoiceBroadcast()
|
RoomDetailViewEvents.DisplayPromptToStopVoiceBroadcast -> displayPromptToStopVoiceBroadcast()
|
||||||
|
// SC
|
||||||
RoomDetailViewEvents.JumpToBottom -> doJumpToBottom()
|
RoomDetailViewEvents.JumpToBottom -> doJumpToBottom()
|
||||||
is RoomDetailViewEvents.SetInitialForceScroll -> setInitialForceScrollEnabled(it.enabled, stickToBottom = it.stickToBottom)
|
is RoomDetailViewEvents.SetInitialForceScroll -> setInitialForceScrollEnabled(it.enabled, stickToBottom = it.stickToBottom)
|
||||||
|
// SC end
|
||||||
|
is RoomDetailViewEvents.RevokeFilePermission -> revokeFilePermission(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1881,14 +1884,14 @@ class TimelineFragment :
|
||||||
|
|
||||||
private fun handleCancelSend(action: EventSharedAction.Cancel) {
|
private fun handleCancelSend(action: EventSharedAction.Cancel) {
|
||||||
if (action.force) {
|
if (action.force) {
|
||||||
timelineViewModel.handle(RoomDetailAction.CancelSend(action.eventId, true))
|
timelineViewModel.handle(RoomDetailAction.CancelSend(action.event, true))
|
||||||
} else {
|
} else {
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
.setTitle(R.string.dialog_title_confirmation)
|
.setTitle(R.string.dialog_title_confirmation)
|
||||||
.setMessage(getString(R.string.event_status_cancel_sending_dialog_message))
|
.setMessage(getString(R.string.event_status_cancel_sending_dialog_message))
|
||||||
.setNegativeButton(R.string.no, null)
|
.setNegativeButton(R.string.no, null)
|
||||||
.setPositiveButton(R.string.yes) { _, _ ->
|
.setPositiveButton(R.string.yes) { _, _ ->
|
||||||
timelineViewModel.handle(RoomDetailAction.CancelSend(action.eventId, false))
|
timelineViewModel.handle(RoomDetailAction.CancelSend(action.event, false))
|
||||||
}
|
}
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
@ -2402,6 +2405,21 @@ class TimelineFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun revokeFilePermission(revokeFilePermission: RoomDetailViewEvents.RevokeFilePermission) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
requireContext().revokeUriPermission(
|
||||||
|
requireContext().applicationContext.packageName,
|
||||||
|
revokeFilePermission.uri,
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
requireContext().revokeUriPermission(
|
||||||
|
revokeFilePermission.uri,
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onTapToReturnToCall() {
|
override fun onTapToReturnToCall() {
|
||||||
callManager.getCurrentCall()?.let { call ->
|
callManager.getCurrentCall()?.let { call ->
|
||||||
VectorCallActivity.newIntent(
|
VectorCallActivity.newIntent(
|
||||||
|
|
|
@ -18,6 +18,7 @@ package im.vector.app.features.home.room.detail
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.annotation.IdRes
|
import androidx.annotation.IdRes
|
||||||
|
import androidx.core.net.toUri
|
||||||
import androidx.lifecycle.asFlow
|
import androidx.lifecycle.asFlow
|
||||||
import com.airbnb.mvrx.Async
|
import com.airbnb.mvrx.Async
|
||||||
import com.airbnb.mvrx.Fail
|
import com.airbnb.mvrx.Fail
|
||||||
|
@ -97,6 +98,7 @@ import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.android.sdk.api.MatrixPatterns
|
import org.matrix.android.sdk.api.MatrixPatterns
|
||||||
|
import org.matrix.android.sdk.api.MatrixUrls.isMxcUrl
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||||
|
@ -124,6 +126,8 @@ import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho
|
import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
|
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
|
||||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.tombstone.RoomTombstoneContent
|
import org.matrix.android.sdk.api.session.room.model.tombstone.RoomTombstoneContent
|
||||||
|
@ -1197,18 +1201,17 @@ class TimelineViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
private fun handleCancel(action: RoomDetailAction.CancelSend) {
|
private fun handleCancel(action: RoomDetailAction.CancelSend) {
|
||||||
if (room == null) return
|
if (room == null) return
|
||||||
if (action.force) {
|
|
||||||
room.sendService().cancelSend(action.eventId)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val targetEventId = action.eventId
|
|
||||||
room.getTimelineEvent(targetEventId)?.let {
|
|
||||||
// State must be in one of the sending states
|
// State must be in one of the sending states
|
||||||
if (!it.root.sendState.isSending()) {
|
if (action.force || action.event.root.sendState.isSending()) {
|
||||||
Timber.e("Cannot cancel message, it is not sending")
|
room.sendService().cancelSend(action.event.eventId)
|
||||||
return
|
|
||||||
|
val clearContent = action.event.root.getClearContent()
|
||||||
|
val messageContent = clearContent?.toModel<MessageContent>() as? MessageWithAttachmentContent
|
||||||
|
messageContent?.getFileUrl()?.takeIf { !it.isMxcUrl() }?.let {
|
||||||
|
_viewEvents.post(RoomDetailViewEvents.RevokeFilePermission(it.toUri()))
|
||||||
}
|
}
|
||||||
room.sendService().cancelSend(targetEventId)
|
} else {
|
||||||
|
Timber.e("Cannot cancel message, it is not sending")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1270,7 +1273,22 @@ class TimelineViewModel @AssistedInject constructor(
|
||||||
if (room == null) return
|
if (room == null) return
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val event = try {
|
val event = try {
|
||||||
|
if (action.user && action.senderId != null) {
|
||||||
|
// When reporting a user, use the user state event if available (it should always be available)
|
||||||
|
val userStateEventId = room.stateService()
|
||||||
|
.getStateEvent(EventType.STATE_ROOM_MEMBER, QueryStringValue.Equals(action.senderId))
|
||||||
|
?.eventId
|
||||||
|
// If not found fallback to the provided event
|
||||||
|
val eventId = userStateEventId ?: action.eventId
|
||||||
|
room.reportingService()
|
||||||
|
.reportContent(
|
||||||
|
eventId = eventId,
|
||||||
|
score = -100,
|
||||||
|
reason = action.reason
|
||||||
|
)
|
||||||
|
} else {
|
||||||
room.reportingService().reportContent(action.eventId, -100, action.reason)
|
room.reportingService().reportContent(action.eventId, -100, action.reason)
|
||||||
|
}
|
||||||
RoomDetailViewEvents.ActionSuccess(action)
|
RoomDetailViewEvents.ActionSuccess(action)
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
RoomDetailViewEvents.ActionFailure(action, failure)
|
RoomDetailViewEvents.ActionFailure(action, failure)
|
||||||
|
|
|
@ -49,6 +49,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||||
import de.spiritcroc.util.ThumbnailGenerationVideoDownloadDecider
|
import de.spiritcroc.util.ThumbnailGenerationVideoDownloadDecider
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.error.fatalError
|
import im.vector.app.core.error.fatalError
|
||||||
|
import im.vector.app.core.extensions.orEmpty
|
||||||
import im.vector.app.core.extensions.registerStartForActivityResult
|
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||||
import im.vector.app.core.extensions.showKeyboard
|
import im.vector.app.core.extensions.showKeyboard
|
||||||
import im.vector.app.core.glide.GlideApp
|
import im.vector.app.core.glide.GlideApp
|
||||||
|
@ -278,7 +279,7 @@ class MessageComposerFragment : VectorBaseFragment<FragmentComposerBinding>(), A
|
||||||
it.isRecordingVoiceBroadcast && !requireActivity().isChangingConfigurations -> timelineViewModel.handle(VoiceBroadcastAction.Recording.Pause)
|
it.isRecordingVoiceBroadcast && !requireActivity().isChangingConfigurations -> timelineViewModel.handle(VoiceBroadcastAction.Recording.Pause)
|
||||||
else -> {
|
else -> {
|
||||||
timelineViewModel.handle(VoiceBroadcastAction.Listening.Pause)
|
timelineViewModel.handle(VoiceBroadcastAction.Listening.Pause)
|
||||||
messageComposerViewModel.handle(MessageComposerAction.OnEntersBackground(composer.text.toString()))
|
messageComposerViewModel.handle(MessageComposerAction.OnEntersBackground(composer.formattedText ?: composer.text.orEmpty().toString()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -460,7 +461,7 @@ class MessageComposerFragment : VectorBaseFragment<FragmentComposerBinding>(), A
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTextChanged(text: CharSequence) {
|
override fun onTextChanged(text: CharSequence) {
|
||||||
messageComposerViewModel.handle(MessageComposerAction.OnTextChanged(text))
|
messageComposerViewModel.handle(MessageComposerAction.OnTextChanged(composer.formattedText ?: text))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFullScreenModeChanged() = withState(messageComposerViewModel) { state ->
|
override fun onFullScreenModeChanged() = withState(messageComposerViewModel) { state ->
|
||||||
|
|
|
@ -23,6 +23,7 @@ import im.vector.app.core.platform.VectorSharedAction
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
|
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
|
|
||||||
sealed class EventSharedAction(
|
sealed class EventSharedAction(
|
||||||
@StringRes val titleRes: Int,
|
@StringRes val titleRes: Int,
|
||||||
|
@ -71,7 +72,7 @@ sealed class EventSharedAction(
|
||||||
data class Redact(val eventId: String, val askForReason: Boolean, val dialogTitleRes: Int, val dialogDescriptionRes: Int) :
|
data class Redact(val eventId: String, val askForReason: Boolean, val dialogTitleRes: Int, val dialogDescriptionRes: Int) :
|
||||||
EventSharedAction(R.string.message_action_item_redact, R.drawable.ic_delete, true)
|
EventSharedAction(R.string.message_action_item_redact, R.drawable.ic_delete, true)
|
||||||
|
|
||||||
data class Cancel(val eventId: String, val force: Boolean) :
|
data class Cancel(val event: TimelineEvent, val force: Boolean) :
|
||||||
EventSharedAction(R.string.action_cancel, R.drawable.ic_close_round)
|
EventSharedAction(R.string.action_cancel, R.drawable.ic_close_round)
|
||||||
|
|
||||||
data class ViewSource(val content: String) :
|
data class ViewSource(val content: String) :
|
||||||
|
|
|
@ -313,7 +313,7 @@ class MessageActionsViewModel @AssistedInject constructor(
|
||||||
private fun ArrayList<EventSharedAction>.addActionsForSendingState(timelineEvent: TimelineEvent) {
|
private fun ArrayList<EventSharedAction>.addActionsForSendingState(timelineEvent: TimelineEvent) {
|
||||||
// TODO is uploading attachment?
|
// TODO is uploading attachment?
|
||||||
if (canCancel(timelineEvent)) {
|
if (canCancel(timelineEvent)) {
|
||||||
add(EventSharedAction.Cancel(timelineEvent.eventId, false))
|
add(EventSharedAction.Cancel(timelineEvent, false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,7 +321,7 @@ class MessageActionsViewModel @AssistedInject constructor(
|
||||||
// If sent but not synced (synapse stuck at bottom bug)
|
// If sent but not synced (synapse stuck at bottom bug)
|
||||||
// Still offer action to cancel (will only remove local echo)
|
// Still offer action to cancel (will only remove local echo)
|
||||||
timelineEvent.root.eventId?.let {
|
timelineEvent.root.eventId?.let {
|
||||||
add(EventSharedAction.Cancel(it, true))
|
add(EventSharedAction.Cancel(timelineEvent, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Can be redacted
|
// TODO Can be redacted
|
||||||
|
|
|
@ -30,6 +30,10 @@ import im.vector.app.features.home.HomeActivity
|
||||||
import im.vector.lib.core.utils.compat.getParcelableCompat
|
import im.vector.lib.core.utils.compat.getParcelableCompat
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
// n.b MSC3886/MSC3903/MSC3906 that this is based on are now closed.
|
||||||
|
// However, we want to keep this implementation around for some time.
|
||||||
|
// TODO define an end-of-life date for this implementation.
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class QrCodeLoginActivity : SimpleFragmentActivity() {
|
class QrCodeLoginActivity : SimpleFragmentActivity() {
|
||||||
|
|
||||||
|
|
|
@ -174,15 +174,20 @@ class RoomMemberProfileViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleReportAction() {
|
private fun handleReportAction() {
|
||||||
|
room ?: return
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val event = try {
|
val event = try {
|
||||||
// The API need an Event, use the latest Event.
|
// The API needs an Event, use user state event if available (it should always be available)
|
||||||
val latestEventId = room?.roomSummary()?.latestPreviewableEvent?.eventId ?: return@launch
|
val userStateEventId = room.stateService()
|
||||||
|
.getStateEvent(EventType.STATE_ROOM_MEMBER, QueryStringValue.Equals(initialState.userId))
|
||||||
|
?.eventId
|
||||||
|
// If not found fallback to the latest event
|
||||||
|
val eventId = (userStateEventId ?: room.roomSummary()?.latestPreviewableEvent?.eventId) ?: return@launch
|
||||||
room.reportingService()
|
room.reportingService()
|
||||||
.reportContent(
|
.reportContent(
|
||||||
eventId = latestEventId,
|
eventId = eventId,
|
||||||
score = -100,
|
score = -100,
|
||||||
reason = "Reporting user ${initialState.userId} (eventId is not relevant)"
|
reason = "Reporting user ${initialState.userId}"
|
||||||
)
|
)
|
||||||
RoomMemberProfileViewEvents.OnReportActionSuccess
|
RoomMemberProfileViewEvents.OnReportActionSuccess
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -23,6 +23,7 @@ import im.vector.app.test.fakes.FakeAnalyticsTracker
|
||||||
import im.vector.app.test.fakes.FakeClock
|
import im.vector.app.test.fakes.FakeClock
|
||||||
import im.vector.app.test.fakes.FakeSession
|
import im.vector.app.test.fakes.FakeSession
|
||||||
import im.vector.app.test.shared.createTimberTestRule
|
import im.vector.app.test.shared.createTimberTestRule
|
||||||
|
import io.mockk.coEvery
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.just
|
import io.mockk.just
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
@ -60,9 +61,24 @@ class DecryptionFailureTrackerTest {
|
||||||
|
|
||||||
private val fakeClock = FakeClock()
|
private val fakeClock = FakeClock()
|
||||||
|
|
||||||
|
val reportedEvents = mutableSetOf<String>()
|
||||||
|
|
||||||
|
private val fakePersistence = mockk<ReportedDecryptionFailurePersistence> {
|
||||||
|
|
||||||
|
coEvery { load() } just runs
|
||||||
|
coEvery { persist() } just runs
|
||||||
|
coEvery { markAsReported(any()) } coAnswers {
|
||||||
|
reportedEvents.add(firstArg())
|
||||||
|
}
|
||||||
|
every { hasBeenReported(any()) } answers {
|
||||||
|
reportedEvents.contains(firstArg())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val decryptionFailureTracker = DecryptionFailureTracker(
|
private val decryptionFailureTracker = DecryptionFailureTracker(
|
||||||
fakeAnalyticsTracker,
|
fakeAnalyticsTracker,
|
||||||
fakeActiveSessionDataSource.instance,
|
fakeActiveSessionDataSource.instance,
|
||||||
|
fakePersistence,
|
||||||
fakeClock
|
fakeClock
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -101,6 +117,7 @@ class DecryptionFailureTrackerTest {
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setupTest() {
|
fun setupTest() {
|
||||||
|
reportedEvents.clear()
|
||||||
fakeMxOrgTestSession.fakeCryptoService.fakeCrossSigningService.givenIsCrossSigningVerifiedReturns(false)
|
fakeMxOrgTestSession.fakeCryptoService.fakeCrossSigningService.givenIsCrossSigningVerifiedReturns(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,6 @@
|
||||||
|
|
||||||
package im.vector.app.features.analytics.impl
|
package im.vector.app.features.analytics.impl
|
||||||
|
|
||||||
import com.posthog.android.Properties
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsScreen
|
|
||||||
import im.vector.app.test.fakes.FakeAnalyticsStore
|
import im.vector.app.test.fakes.FakeAnalyticsStore
|
||||||
import im.vector.app.test.fakes.FakeLateInitUserPropertiesFactory
|
import im.vector.app.test.fakes.FakeLateInitUserPropertiesFactory
|
||||||
import im.vector.app.test.fakes.FakePostHog
|
import im.vector.app.test.fakes.FakePostHog
|
||||||
|
@ -128,7 +125,7 @@ class DefaultVectorAnalyticsTest {
|
||||||
|
|
||||||
defaultVectorAnalytics.screen(A_SCREEN_EVENT)
|
defaultVectorAnalytics.screen(A_SCREEN_EVENT)
|
||||||
|
|
||||||
fakePostHog.verifyScreenTracked(A_SCREEN_EVENT.getName(), A_SCREEN_EVENT.toPostHogProperties())
|
fakePostHog.verifyScreenTracked(A_SCREEN_EVENT.getName(), A_SCREEN_EVENT.getProperties())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -146,7 +143,7 @@ class DefaultVectorAnalyticsTest {
|
||||||
|
|
||||||
defaultVectorAnalytics.capture(AN_EVENT)
|
defaultVectorAnalytics.capture(AN_EVENT)
|
||||||
|
|
||||||
fakePostHog.verifyEventTracked(AN_EVENT.getName(), AN_EVENT.toPostHogProperties())
|
fakePostHog.verifyEventTracked(AN_EVENT.getName(), AN_EVENT.getProperties().clearNulls())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -176,16 +173,16 @@ class DefaultVectorAnalyticsTest {
|
||||||
|
|
||||||
fakeSentryAnalytics.verifyNoErrorTracking()
|
fakeSentryAnalytics.verifyNoErrorTracking()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun VectorAnalyticsScreen.toPostHogProperties(): Properties? {
|
private fun Map<String, Any?>?.clearNulls(): Map<String, Any>? {
|
||||||
return getProperties()?.let { properties ->
|
if (this == null) return null
|
||||||
Properties().also { it.putAll(properties) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun VectorAnalyticsEvent.toPostHogProperties(): Properties? {
|
val nonNulls = HashMap<String, Any>()
|
||||||
return getProperties()?.let { properties ->
|
this.forEach { (key, value) ->
|
||||||
Properties().also { it.putAll(properties) }
|
if (value != null) {
|
||||||
|
nonNulls[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nonNulls
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,7 @@
|
||||||
package im.vector.app.test.fakes
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import com.posthog.android.PostHog
|
import com.posthog.PostHogInterface
|
||||||
import com.posthog.android.Properties
|
|
||||||
import im.vector.app.features.analytics.plan.UserProperties
|
import im.vector.app.features.analytics.plan.UserProperties
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
@ -36,16 +35,19 @@ class FakePostHog {
|
||||||
every { Looper.getMainLooper() } returns looper
|
every { Looper.getMainLooper() } returns looper
|
||||||
}
|
}
|
||||||
|
|
||||||
val instance = mockk<PostHog>(relaxed = true)
|
val instance = mockk<PostHogInterface>(relaxed = true)
|
||||||
|
|
||||||
fun verifyOptOutStatus(optedOut: Boolean) {
|
fun verifyOptOutStatus(optedOut: Boolean) {
|
||||||
verify { instance.optOut(optedOut) }
|
if (optedOut) {
|
||||||
|
verify { instance.optOut() }
|
||||||
|
} else {
|
||||||
|
verify { instance.optIn() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun verifyIdentifies(analyticsId: String, userProperties: UserProperties?) {
|
fun verifyIdentifies(analyticsId: String, userProperties: UserProperties?) {
|
||||||
verify {
|
verify {
|
||||||
val postHogProperties = userProperties?.getProperties()
|
val postHogProperties = userProperties?.getProperties()
|
||||||
?.let { rawProperties -> Properties().also { it.putAll(rawProperties) } }
|
|
||||||
?.takeIf { it.isNotEmpty() }
|
?.takeIf { it.isNotEmpty() }
|
||||||
instance.identify(analyticsId, postHogProperties, null)
|
instance.identify(analyticsId, postHogProperties, null)
|
||||||
}
|
}
|
||||||
|
@ -55,7 +57,7 @@ class FakePostHog {
|
||||||
verify { instance.reset() }
|
verify { instance.reset() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun verifyScreenTracked(name: String, properties: Properties?) {
|
fun verifyScreenTracked(name: String, properties: Map<String, Any>?) {
|
||||||
verify { instance.screen(name, properties) }
|
verify { instance.screen(name, properties) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,12 +65,11 @@ class FakePostHog {
|
||||||
verify(exactly = 0) {
|
verify(exactly = 0) {
|
||||||
instance.screen(any())
|
instance.screen(any())
|
||||||
instance.screen(any(), any())
|
instance.screen(any(), any())
|
||||||
instance.screen(any(), any(), any())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun verifyEventTracked(name: String, properties: Properties?) {
|
fun verifyEventTracked(name: String, properties: Map<String, Any>?) {
|
||||||
verify { instance.capture(name, properties) }
|
verify { instance.capture(name, null, properties) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun verifyNoEventTracking() {
|
fun verifyNoEventTracking() {
|
||||||
|
|
|
@ -16,12 +16,12 @@
|
||||||
|
|
||||||
package im.vector.app.test.fakes
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
import com.posthog.android.PostHog
|
import com.posthog.PostHogInterface
|
||||||
import im.vector.app.features.analytics.impl.PostHogFactory
|
import im.vector.app.features.analytics.impl.PostHogFactory
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
|
||||||
class FakePostHogFactory(postHog: PostHog) {
|
class FakePostHogFactory(postHog: PostHogInterface) {
|
||||||
val instance = mockk<PostHogFactory>().also {
|
val instance = mockk<PostHogFactory>().also {
|
||||||
every { it.createPosthog() } returns postHog
|
every { it.createPosthog() } returns postHog
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue