mirror of
https://github.com/vector-im/riotX-android
synced 2025-10-06 00:02:48 +02:00
Compare commits
7 Commits
v1.6.46
...
feature/bc
Author | SHA1 | Date | |
---|---|---|---|
|
d8c54ae8fa | ||
|
02efbe03d9 | ||
|
82c41333a0 | ||
|
9ab29a7154 | ||
|
23a73bf575 | ||
|
62683e83aa | ||
|
9c932549b9 |
@@ -17,6 +17,7 @@
|
||||
package org.matrix.android.sdk.internal.util
|
||||
|
||||
import java.security.MessageDigest
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Compute a Hash of a String, using md5 algorithm
|
||||
@@ -31,3 +32,14 @@ fun String.md5() = try {
|
||||
// Should not happen, but just in case
|
||||
hashCode().toString()
|
||||
}
|
||||
|
||||
fun String.sha256() = try {
|
||||
val digest = MessageDigest.getInstance("SHA-256")
|
||||
digest.update(toByteArray())
|
||||
digest.digest()
|
||||
.joinToString("") { String.format("%02X", it) }
|
||||
.toLowerCase(Locale.getDefault())
|
||||
} catch (exc: Exception) {
|
||||
// Should not happen, but just in case
|
||||
hashCode().toString()
|
||||
}
|
||||
|
@@ -203,7 +203,7 @@ android {
|
||||
buildTypes {
|
||||
debug {
|
||||
applicationIdSuffix ".debug"
|
||||
resValue "string", "app_name", "Element dbg"
|
||||
resValue "string", "app_name", "Element Countly dbg"
|
||||
|
||||
resValue "bool", "debug_mode", "true"
|
||||
buildConfigField "boolean", "LOW_PRIVACY_LOG_ENABLE", "false"
|
||||
@@ -441,6 +441,10 @@ dependencies {
|
||||
implementation 'com.google.zxing:core:3.3.3'
|
||||
implementation 'me.dm7.barcodescanner:zxing:1.9.13'
|
||||
|
||||
// Analytics
|
||||
implementation 'ly.count.android:sdk:20.04.5'
|
||||
implementation 'ly.count.android:sdk-native:20.04.5'
|
||||
|
||||
// TESTS
|
||||
testImplementation 'junit:junit:4.13'
|
||||
testImplementation "org.amshove.kluent:kluent-android:$kluent_version"
|
||||
|
@@ -43,6 +43,7 @@ import im.vector.app.core.di.VectorComponent
|
||||
import im.vector.app.core.extensions.configureAndStart
|
||||
import im.vector.app.core.rx.RxConfig
|
||||
import im.vector.app.features.call.WebRtcPeerConnectionManager
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import im.vector.app.features.configuration.VectorConfiguration
|
||||
import im.vector.app.features.disclaimer.doNotShowDisclaimerDialog
|
||||
import im.vector.app.features.lifecycle.VectorActivityLifecycleCallbacks
|
||||
@@ -91,6 +92,7 @@ class VectorApplication :
|
||||
@Inject lateinit var popupAlertManager: PopupAlertManager
|
||||
@Inject lateinit var pinLocker: PinLocker
|
||||
@Inject lateinit var webRtcPeerConnectionManager: WebRtcPeerConnectionManager
|
||||
@Inject lateinit var analyticsEngine: AnalyticsEngine
|
||||
|
||||
lateinit var vectorComponent: VectorComponent
|
||||
|
||||
@@ -114,7 +116,6 @@ class VectorApplication :
|
||||
vectorComponent.inject(this)
|
||||
vectorUncaughtExceptionHandler.activate(this)
|
||||
rxConfig.setupRxPlugin()
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
Timber.plant(Timber.DebugTree())
|
||||
}
|
||||
@@ -128,7 +129,7 @@ class VectorApplication :
|
||||
|
||||
EpoxyController.defaultDiffingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler()
|
||||
EpoxyController.defaultModelBuildingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler()
|
||||
registerActivityLifecycleCallbacks(VectorActivityLifecycleCallbacks(popupAlertManager))
|
||||
registerActivityLifecycleCallbacks(VectorActivityLifecycleCallbacks(popupAlertManager, analyticsEngine))
|
||||
val fontRequest = FontRequest(
|
||||
"com.google.android.gms.fonts",
|
||||
"com.google.android.gms",
|
||||
@@ -155,6 +156,7 @@ class VectorApplication :
|
||||
val lastAuthenticatedSession = authenticationService.getLastAuthenticatedSession()!!
|
||||
activeSessionHolder.setActiveSession(lastAuthenticatedSession)
|
||||
lastAuthenticatedSession.configureAndStart(applicationContext)
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.Init(lastAuthenticatedSession))
|
||||
}
|
||||
ProcessLifecycleOwner.get().lifecycle.addObserver(object : LifecycleObserver {
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
||||
|
@@ -67,6 +67,7 @@ import im.vector.app.features.roommemberprofile.RoomMemberProfileActivity
|
||||
import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet
|
||||
import im.vector.app.features.roomprofile.RoomProfileActivity
|
||||
import im.vector.app.features.settings.VectorSettingsActivity
|
||||
import im.vector.app.features.settings.VectorSettingsBaseFragment
|
||||
import im.vector.app.features.settings.devices.DeviceVerificationInfoBottomSheet
|
||||
import im.vector.app.features.share.IncomingShareActivity
|
||||
import im.vector.app.features.signout.soft.SoftLogoutActivity
|
||||
@@ -166,7 +167,7 @@ interface ScreenComponent {
|
||||
fun inject(view: VectorInviteView)
|
||||
fun inject(preference: UserAvatarPreference)
|
||||
fun inject(button: ReactionButton)
|
||||
|
||||
fun inject(baseFragment: VectorSettingsBaseFragment)
|
||||
/* ==========================================================================================
|
||||
* Factory
|
||||
* ========================================================================================== */
|
||||
|
@@ -28,6 +28,7 @@ import im.vector.app.core.dialogs.UnrecognizedCertificateDialog
|
||||
import im.vector.app.core.error.ErrorFormatter
|
||||
import im.vector.app.core.pushers.PushersManager
|
||||
import im.vector.app.core.utils.AssetReader
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import im.vector.app.core.utils.DimensionConverter
|
||||
import im.vector.app.features.call.WebRtcPeerConnectionManager
|
||||
import im.vector.app.features.configuration.VectorConfiguration
|
||||
@@ -151,6 +152,8 @@ interface VectorComponent {
|
||||
|
||||
fun reAuthHelper(): ReAuthHelper
|
||||
|
||||
fun analyticsEngine(): AnalyticsEngine
|
||||
|
||||
fun pinLocker(): PinLocker
|
||||
|
||||
fun webRtcPeerConnectionManager(): WebRtcPeerConnectionManager
|
||||
|
304
vector/src/main/java/im/vector/app/core/utils/AnalyticsEngine.kt
Normal file
304
vector/src/main/java/im/vector/app/core/utils/AnalyticsEngine.kt
Normal file
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.core.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import im.vector.app.BuildConfig
|
||||
import im.vector.app.core.extensions.exhaustive
|
||||
import im.vector.app.features.home.RoomListDisplayMode
|
||||
import ly.count.android.sdk.Countly
|
||||
import ly.count.android.sdk.CountlyConfig
|
||||
import ly.count.android.sdknative.CountlyNative
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
|
||||
import org.matrix.android.sdk.internal.util.sha256
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class AnalyticsEngine @Inject constructor(private val context: Context) {
|
||||
|
||||
sealed class AnalyticEvent {
|
||||
data class Init(val session: Session) : AnalyticEvent()
|
||||
|
||||
// Used for session detection
|
||||
data class StartActivity(val activity: Activity) : AnalyticEvent()
|
||||
data class EndActivity(val activity: Activity) : AnalyticEvent()
|
||||
|
||||
data class RoomView(val roomId: String, val isEncrypted: Boolean, val memberCount: Int, val isPublic: Boolean) : AnalyticEvent()
|
||||
data class HomeView(val mode: RoomListDisplayMode) : AnalyticEvent()
|
||||
data class SettingsView(val category: String) : AnalyticEvent()
|
||||
data class StartCall(val roomId: String, val isVideo: Boolean) : AnalyticEvent()
|
||||
data class JoinCall(val roomId: String, val isVideo: Boolean) : AnalyticEvent()
|
||||
data class StartJitsiConf(val roomId: String, val isVideo: Boolean) : AnalyticEvent()
|
||||
data class JoinConference(val roomId: String, val isVideo: Boolean) : AnalyticEvent()
|
||||
data class SendText(val roomId: String) : AnalyticEvent()
|
||||
data class SendReply(val roomId: String) : AnalyticEvent()
|
||||
data class SendQuote(val roomId: String) : AnalyticEvent()
|
||||
data class SendEdit(val roomId: String) : AnalyticEvent()
|
||||
data class SendMedia(val roomId: String, val type: ContentAttachmentData.Type) : AnalyticEvent()
|
||||
data class BeginInvite(val roomId: String, val isEncrypted: Boolean, val memberCount: Int, val isPublic: Boolean) : AnalyticEvent()
|
||||
data class SendInvite(val roomId: String, val inviteeCount: Int, val isEncrypted: Boolean, val memberCount: Int, val isPublic: Boolean) : AnalyticEvent()
|
||||
|
||||
object StartRoomDirectory : AnalyticEvent()
|
||||
object EndRoomDirectory : AnalyticEvent()
|
||||
|
||||
object StartRoomDirectorySearch : AnalyticEvent()
|
||||
data class EndRoomDirectorySearch(val resultCount: Int) : AnalyticEvent()
|
||||
|
||||
object StartJoinRoomEvent : AnalyticEvent()
|
||||
data class EndJoinRoomEvent(val roomId: String, val isEncrypted: Boolean, val memberCount: Int, val isPublic: Boolean) : AnalyticEvent()
|
||||
object CancelJoinRoomEvent : AnalyticEvent()
|
||||
|
||||
data class UnHandledCrash(val exception: Throwable) : AnalyticEvent()
|
||||
data class GetFeedBacks(val id: String, val closeText: String, val activity: Activity) : AnalyticEvent()
|
||||
}
|
||||
|
||||
private var countly: Countly? = null
|
||||
|
||||
fun isEnabled(): Boolean {
|
||||
// Should check consent here
|
||||
return countly?.isInitialized ?: false
|
||||
}
|
||||
|
||||
fun report(event: AnalyticEvent) {
|
||||
when (event) {
|
||||
is AnalyticEvent.Init -> {
|
||||
val session = event.session
|
||||
CountlyConfig(context, "8abf1ee15646bc884556b82e5053857904264b66", "https://try.count.ly/").let { config ->
|
||||
if (BuildConfig.DEBUG) {
|
||||
config.setLoggingEnabled(true)
|
||||
}
|
||||
config.enableCrashReporting()
|
||||
CountlyNative.initNative(context.applicationContext)
|
||||
// config.setCustomCrashSegment(mapOf(
|
||||
// "flavor" to BuildConfig.FLAVOR,
|
||||
// "branch" to BuildConfig.GIT_BRANCH_NAME
|
||||
// ))
|
||||
config.setDeviceId(session.myUserId.sha256())
|
||||
Countly.sharedInstance().init(config).also {
|
||||
countly = it
|
||||
}
|
||||
Countly.userData.setUserData(
|
||||
mapOf("home_server" to session.sessionParams.homeServerHost)
|
||||
)
|
||||
Countly.userData.save()
|
||||
}
|
||||
}
|
||||
is AnalyticEvent.RoomView -> {
|
||||
countly?.views()?.recordView(
|
||||
"view_room",
|
||||
mapOf(
|
||||
"room_id" to event.roomId.sha256(),
|
||||
"is_encrypted" to event.isEncrypted,
|
||||
"num_users" to event.memberCount,
|
||||
"is_public" to event.isPublic
|
||||
)
|
||||
)
|
||||
Unit
|
||||
}
|
||||
is AnalyticEvent.HomeView -> {
|
||||
countly?.views()?.recordView(
|
||||
"view_home",
|
||||
mapOf<String, Any>("filter" to event.mode.name)
|
||||
)
|
||||
Unit
|
||||
}
|
||||
is AnalyticEvent.StartCall -> {
|
||||
countly?.events()?.recordEvent(
|
||||
"start_call",
|
||||
mapOf<String, Any>(
|
||||
"room_id" to event.roomId.sha256(),
|
||||
"is_video" to event.isVideo,
|
||||
"is_jitsi" to false
|
||||
)
|
||||
)
|
||||
}
|
||||
is AnalyticEvent.StartJitsiConf -> {
|
||||
countly?.events()?.recordEvent(
|
||||
"start_call",
|
||||
mapOf<String, Any>(
|
||||
"room_id" to event.roomId.sha256(),
|
||||
"is_video" to event.isVideo,
|
||||
"is_jitsi" to true
|
||||
)
|
||||
)
|
||||
}
|
||||
is AnalyticEvent.SendText -> {
|
||||
countly?.events()?.recordEvent(
|
||||
"send_message",
|
||||
mapOf<String, Any>(
|
||||
"room_id" to event.roomId.sha256(),
|
||||
"is_edit" to false,
|
||||
"is_reply" to false,
|
||||
"message_type" to "Text"
|
||||
)
|
||||
)
|
||||
}
|
||||
is AnalyticEvent.SendReply -> {
|
||||
countly?.events()?.recordEvent(
|
||||
"send_message",
|
||||
mapOf<String, Any>(
|
||||
"room_id" to event.roomId.sha256(),
|
||||
"is_edit" to false,
|
||||
"is_reply" to true,
|
||||
"message_type" to "Text"
|
||||
)
|
||||
)
|
||||
}
|
||||
is AnalyticEvent.SendQuote -> {
|
||||
countly?.events()?.recordEvent(
|
||||
"send_message",
|
||||
mapOf<String, Any>(
|
||||
"room_id" to event.roomId.sha256(),
|
||||
"is_edit" to false,
|
||||
"is_reply" to false,
|
||||
"message_type" to "Text"
|
||||
)
|
||||
)
|
||||
}
|
||||
is AnalyticEvent.SendMedia -> {
|
||||
countly?.events()?.recordEvent(
|
||||
"send_message",
|
||||
mapOf<String, Any>(
|
||||
"room_id" to event.roomId.sha256(),
|
||||
"is_edit" to false,
|
||||
"is_reply" to false,
|
||||
"message_type" to when (event.type) {
|
||||
ContentAttachmentData.Type.FILE -> "File"
|
||||
ContentAttachmentData.Type.IMAGE -> "Image"
|
||||
ContentAttachmentData.Type.AUDIO -> "Audio"
|
||||
ContentAttachmentData.Type.VIDEO -> "Video"
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
is AnalyticEvent.SendEdit -> {
|
||||
countly?.events()?.recordEvent(
|
||||
"send_message",
|
||||
mapOf<String, Any>(
|
||||
"room_id" to event.roomId.sha256(),
|
||||
"is_edit" to true,
|
||||
"is_reply" to false,
|
||||
"message_type" to "Text"
|
||||
)
|
||||
)
|
||||
}
|
||||
is AnalyticEvent.JoinCall -> {
|
||||
countly?.events()?.recordEvent(
|
||||
"join_call",
|
||||
mapOf<String, Any>(
|
||||
"room_id" to event.roomId.sha256(),
|
||||
"is_video" to event.isVideo,
|
||||
"is_jitsi" to false
|
||||
)
|
||||
)
|
||||
}
|
||||
is AnalyticEvent.JoinConference -> {
|
||||
countly?.events()?.recordEvent(
|
||||
"join_call",
|
||||
mapOf<String, Any>(
|
||||
"room_id" to event.roomId.sha256(),
|
||||
"is_video" to event.isVideo,
|
||||
"is_jitsi" to true
|
||||
)
|
||||
)
|
||||
}
|
||||
is AnalyticEvent.StartActivity -> {
|
||||
countly?.onStart(event.activity)
|
||||
}
|
||||
is AnalyticEvent.EndActivity -> {
|
||||
countly?.onStop()
|
||||
}
|
||||
AnalyticEvent.StartRoomDirectory -> {
|
||||
countly?.events()?.startEvent("room_directory")
|
||||
Unit
|
||||
}
|
||||
AnalyticEvent.EndRoomDirectory -> {
|
||||
countly?.events()?.endEvent("room_directory")
|
||||
Unit
|
||||
}
|
||||
AnalyticEvent.StartRoomDirectorySearch -> {
|
||||
countly?.events()?.startEvent("room_directory_search")
|
||||
Unit
|
||||
}
|
||||
is AnalyticEvent.EndRoomDirectorySearch -> {
|
||||
countly?.events()?.endEvent("room_directory_search", mapOf("result_count" to event.resultCount), 1, 0.0)
|
||||
Unit
|
||||
}
|
||||
AnalyticEvent.StartJoinRoomEvent -> {
|
||||
countly?.events()?.startEvent("join_room")
|
||||
Unit
|
||||
}
|
||||
is AnalyticEvent.EndJoinRoomEvent -> {
|
||||
countly?.events()?.endEvent(
|
||||
"join_room",
|
||||
mapOf<String, Any>("room_id" to event.roomId.sha256()),
|
||||
1,
|
||||
0.0
|
||||
)
|
||||
Unit
|
||||
}
|
||||
AnalyticEvent.CancelJoinRoomEvent -> {
|
||||
countly?.events()?.cancelEvent("join_room")
|
||||
Unit
|
||||
}
|
||||
is AnalyticEvent.UnHandledCrash -> {
|
||||
countly?.crashes()?.recordUnhandledException(event.exception)
|
||||
Unit
|
||||
}
|
||||
is AnalyticEvent.GetFeedBacks -> {
|
||||
countly?.ratings()?.showFeedbackPopup(event.id, event.closeText, event.activity) {
|
||||
// if (it) error is null no error...
|
||||
}
|
||||
}
|
||||
is AnalyticEvent.SettingsView -> {
|
||||
countly?.views()?.recordView("settings_view", mapOf("category" to event.category))
|
||||
Unit
|
||||
}
|
||||
is AnalyticEvent.BeginInvite -> {
|
||||
countly?.events()?.recordEvent(
|
||||
"begin_invite",
|
||||
mapOf(
|
||||
"room_id" to event.roomId.sha256(),
|
||||
"is_encrypted" to event.isEncrypted,
|
||||
"num_users" to event.memberCount,
|
||||
"is_public" to event.isPublic
|
||||
),
|
||||
1,
|
||||
0.0
|
||||
)
|
||||
Unit
|
||||
}
|
||||
is AnalyticEvent.SendInvite -> {
|
||||
countly?.events()?.recordEvent(
|
||||
"send_invite",
|
||||
mapOf(
|
||||
"room_id" to event.roomId.sha256(),
|
||||
"is_encrypted" to event.isEncrypted,
|
||||
"num_users" to event.memberCount,
|
||||
"is_public" to event.isPublic
|
||||
),
|
||||
1,
|
||||
event.inviteeCount.toDouble()
|
||||
)
|
||||
Unit
|
||||
}
|
||||
}.exhaustive
|
||||
}
|
||||
}
|
@@ -27,6 +27,7 @@ import im.vector.app.core.services.BluetoothHeadsetReceiver
|
||||
import im.vector.app.core.services.CallService
|
||||
import im.vector.app.core.services.WiredHeadsetStateReceiver
|
||||
import im.vector.app.push.fcm.FcmHelper
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.ReplaySubject
|
||||
@@ -75,7 +76,8 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class WebRtcPeerConnectionManager @Inject constructor(
|
||||
private val context: Context,
|
||||
private val activeSessionDataSource: ActiveSessionDataSource
|
||||
private val activeSessionDataSource: ActiveSessionDataSource,
|
||||
private val analyticsEngine: AnalyticsEngine
|
||||
) : CallsListener, LifecycleObserver {
|
||||
|
||||
private val currentSession: Session?
|
||||
@@ -543,6 +545,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||
getTurnServer { turnServer ->
|
||||
internalAcceptIncomingCall(currentCall!!, turnServer)
|
||||
}
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.JoinCall(mxCall.roomId, mxCall.isVideoCall))
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -38,6 +38,7 @@ import im.vector.app.core.extensions.replaceFragment
|
||||
import im.vector.app.core.platform.ToolbarConfigurable
|
||||
import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.core.pushers.PushersManager
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import im.vector.app.features.disclaimer.showDisclaimerDialog
|
||||
import im.vector.app.features.notifications.NotificationDrawerManager
|
||||
import im.vector.app.features.popup.DefaultVectorAlert
|
||||
@@ -82,6 +83,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
|
||||
@Inject lateinit var popupAlertManager: PopupAlertManager
|
||||
@Inject lateinit var shortcutsHandler: ShortcutsHandler
|
||||
@Inject lateinit var unknownDeviceViewModelFactory: UnknownDeviceDetectorSharedViewModel.Factory
|
||||
@Inject lateinit var analyticsEngine: AnalyticsEngine
|
||||
|
||||
private val drawerListener = object : DrawerLayout.SimpleDrawerListener() {
|
||||
override fun onDrawerStateChanged(newState: Int) {
|
||||
@@ -306,7 +308,17 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.menu_home_suggestion -> {
|
||||
bugReporter.openBugReportScreen(this, true)
|
||||
if (analyticsEngine.isEnabled()) {
|
||||
analyticsEngine.report(
|
||||
AnalyticsEngine.AnalyticEvent.GetFeedBacks(
|
||||
"5f987e2370c8d359e50687eb",
|
||||
getString(R.string.action_close),
|
||||
this
|
||||
)
|
||||
)
|
||||
} else {
|
||||
bugReporter.openBugReportScreen(this, true)
|
||||
}
|
||||
return true
|
||||
}
|
||||
R.id.menu_home_report_bug -> {
|
||||
|
@@ -164,9 +164,7 @@ class HomeDetailFragment @Inject constructor(
|
||||
)
|
||||
}
|
||||
dismissedAction = Runnable {
|
||||
unknownDeviceDetectorSharedViewModel.handle(
|
||||
UnknownDeviceDetectorSharedViewModel.Action.IgnoreDevice(newest.deviceId?.let { listOf(it) }.orEmpty())
|
||||
)
|
||||
unknownDeviceDetectorSharedViewModel.handle(UnknownDeviceDetectorSharedViewModel.Action.IgnoreNewDevices)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@@ -24,7 +24,7 @@ import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.app.core.di.HasScreenInjector
|
||||
import im.vector.app.core.platform.EmptyViewEvents
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import im.vector.app.features.grouplist.SelectedGroupDataSource
|
||||
import im.vector.app.features.ui.UiStateRepository
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
@@ -41,7 +41,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
|
||||
private val uiStateRepository: UiStateRepository,
|
||||
private val selectedGroupStore: SelectedGroupDataSource,
|
||||
private val homeRoomListStore: HomeRoomListDataSource,
|
||||
private val stringProvider: StringProvider)
|
||||
private val analyticsEngine: AnalyticsEngine)
|
||||
: VectorViewModel<HomeDetailViewState, HomeDetailAction, EmptyViewEvents>(initialState) {
|
||||
|
||||
@AssistedInject.Factory
|
||||
@@ -82,7 +82,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
|
||||
setState {
|
||||
copy(displayMode = action.displayMode)
|
||||
}
|
||||
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.HomeView(action.displayMode))
|
||||
uiStateRepository.storeDisplayMode(action.displayMode)
|
||||
}
|
||||
}
|
||||
|
@@ -63,6 +63,7 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted
|
||||
|
||||
sealed class Action : VectorViewModelAction {
|
||||
data class IgnoreDevice(val deviceIds: List<String>) : Action()
|
||||
object IgnoreNewDevices : Action()
|
||||
}
|
||||
|
||||
@AssistedInject.Factory
|
||||
@@ -159,6 +160,17 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted
|
||||
}
|
||||
}
|
||||
}
|
||||
Action.IgnoreNewDevices -> {
|
||||
withState { state ->
|
||||
val detected = state.unknownSessions.invoke() ?: emptyList()
|
||||
val allNew = detected.filter { it.isNew }.mapNotNull { it.deviceInfo.deviceId }
|
||||
ignoredDeviceList.addAll(allNew)
|
||||
val updated = detected.filter { !ignoredDeviceList.contains(it.deviceInfo.deviceId) }
|
||||
setState {
|
||||
copy(unknownSessions = Success(updated))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -90,4 +90,5 @@ sealed class RoomDetailAction : VectorViewModelAction {
|
||||
|
||||
data class OpenOrCreateDm(val userId: String) : RoomDetailAction()
|
||||
data class JumpToReadReceipt(val userId: String) : RoomDetailAction()
|
||||
object ReportView: RoomDetailAction()
|
||||
}
|
||||
|
@@ -90,6 +90,7 @@ import im.vector.app.core.ui.views.ActiveCallViewHolder
|
||||
import im.vector.app.core.ui.views.ActiveConferenceView
|
||||
import im.vector.app.core.ui.views.JumpToReadMarkerView
|
||||
import im.vector.app.core.ui.views.NotificationAreaView
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import im.vector.app.core.utils.Debouncer
|
||||
import im.vector.app.core.utils.KeyboardStateUtils
|
||||
import im.vector.app.core.utils.PERMISSIONS_FOR_AUDIO_IP_CALL
|
||||
@@ -221,7 +222,8 @@ class RoomDetailFragment @Inject constructor(
|
||||
private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager,
|
||||
private val matrixItemColorProvider: MatrixItemColorProvider,
|
||||
private val imageContentRenderer: ImageContentRenderer,
|
||||
private val roomDetailPendingActionStore: RoomDetailPendingActionStore
|
||||
private val roomDetailPendingActionStore: RoomDetailPendingActionStore,
|
||||
private val analyticsEngine: AnalyticsEngine
|
||||
) :
|
||||
VectorBaseFragment(),
|
||||
TimelineEventController.Callback,
|
||||
@@ -372,6 +374,11 @@ class RoomDetailFragment @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
roomDetailViewModel.handle(RoomDetailAction.ReportView)
|
||||
}
|
||||
|
||||
private fun handleOpenRoom(openRoom: RoomDetailViewEvents.OpenRoom) {
|
||||
navigator.openRoom(requireContext(), openRoom.roomId, null)
|
||||
}
|
||||
@@ -445,6 +452,7 @@ class RoomDetailFragment @Inject constructor(
|
||||
|
||||
private fun joinJitsiRoom(jitsiWidget: Widget, enableVideo: Boolean) {
|
||||
navigator.openRoomWidget(requireContext(), roomDetailArgs.roomId, jitsiWidget, mapOf(JitsiCallViewModel.ENABLE_VIDEO_OPTION to enableVideo))
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.JoinConference(roomId = roomDetailArgs.roomId, isVideo = enableVideo))
|
||||
}
|
||||
|
||||
private fun openStickerPicker(event: RoomDetailViewEvents.OpenStickerPicker) {
|
||||
@@ -653,36 +661,36 @@ class RoomDetailFragment @Inject constructor(
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.invite -> {
|
||||
R.id.invite -> {
|
||||
navigator.openInviteUsersToRoom(requireActivity(), roomDetailArgs.roomId)
|
||||
true
|
||||
}
|
||||
R.id.timeline_setting -> {
|
||||
R.id.timeline_setting -> {
|
||||
navigator.openRoomProfile(requireActivity(), roomDetailArgs.roomId)
|
||||
true
|
||||
}
|
||||
R.id.resend_all -> {
|
||||
R.id.resend_all -> {
|
||||
roomDetailViewModel.handle(RoomDetailAction.ResendAll)
|
||||
true
|
||||
}
|
||||
R.id.open_matrix_apps -> {
|
||||
R.id.open_matrix_apps -> {
|
||||
roomDetailViewModel.handle(RoomDetailAction.ManageIntegrations)
|
||||
true
|
||||
}
|
||||
R.id.voice_call,
|
||||
R.id.video_call -> {
|
||||
R.id.video_call -> {
|
||||
handleCallRequest(item)
|
||||
true
|
||||
}
|
||||
R.id.hangup_call -> {
|
||||
R.id.hangup_call -> {
|
||||
roomDetailViewModel.handle(RoomDetailAction.EndCall)
|
||||
true
|
||||
}
|
||||
R.id.search -> {
|
||||
R.id.search -> {
|
||||
handleSearchAction()
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -31,6 +31,7 @@ import im.vector.app.R
|
||||
import im.vector.app.core.extensions.exhaustive
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import im.vector.app.core.utils.subscribeLogError
|
||||
import im.vector.app.features.call.WebRtcPeerConnectionManager
|
||||
import im.vector.app.features.command.CommandParser
|
||||
@@ -74,6 +75,8 @@ import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||
import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
@@ -115,7 +118,8 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||
private val roomSummaryHolder: RoomSummaryHolder,
|
||||
private val typingHelper: TypingHelper,
|
||||
private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager,
|
||||
timelineSettingsFactory: TimelineSettingsFactory
|
||||
timelineSettingsFactory: TimelineSettingsFactory,
|
||||
private val analyticsEngine: AnalyticsEngine
|
||||
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState), Timeline.Listener {
|
||||
|
||||
private val room = session.getRoom(initialState.roomId)!!
|
||||
@@ -275,6 +279,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||
is RoomDetailAction.CancelSend -> handleCancel(action)
|
||||
is RoomDetailAction.OpenOrCreateDm -> handleOpenOrCreateDm(action)
|
||||
is RoomDetailAction.JumpToReadReceipt -> handleJumpToReadReceipt(action)
|
||||
RoomDetailAction.ReportView -> handleReportView()
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
@@ -295,6 +300,23 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleReportView() {
|
||||
viewModelScope.launch {
|
||||
val roomSummary = room.roomSummary()
|
||||
val joinRules = room.getStateEvent(EventType.STATE_ROOM_JOIN_RULES, QueryStringValue.IsNotEmpty)
|
||||
?.content.toModel<RoomJoinRulesContent>()
|
||||
?.joinRules
|
||||
analyticsEngine.report(
|
||||
AnalyticsEngine.AnalyticEvent.RoomView(
|
||||
roomId = room.roomId,
|
||||
isEncrypted = roomSummary?.isEncrypted ?: false,
|
||||
memberCount = roomSummary?.joinedMembersCount ?: 0,
|
||||
isPublic = joinRules == RoomJoinRules.PUBLIC
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleJumpToReadReceipt(action: RoomDetailAction.JumpToReadReceipt) {
|
||||
room.getUserReadReceipt(action.userId)
|
||||
?.let { handleNavigateToEvent(RoomDetailAction.NavigateToEvent(it, true)) }
|
||||
@@ -308,6 +330,12 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||
room.roomSummary()?.otherMemberIds?.firstOrNull()?.let {
|
||||
webRtcPeerConnectionManager.startOutgoingCall(room.roomId, it, action.isVideo)
|
||||
}
|
||||
viewModelScope.launch {
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.StartCall(
|
||||
roomId = room.roomId,
|
||||
isVideo = action.isVideo
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleEndCall() {
|
||||
@@ -403,6 +431,15 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||
_viewEvents.post(RoomDetailViewEvents.HideWaitingView)
|
||||
}
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
analyticsEngine.report(
|
||||
AnalyticsEngine.AnalyticEvent.StartJitsiConf(
|
||||
roomId = room.roomId,
|
||||
isVideo = action.withVideo
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleDeleteWidget(widgetId: String) {
|
||||
@@ -559,16 +596,16 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||
return@withState false
|
||||
}
|
||||
when (itemId) {
|
||||
R.id.resend_all -> state.asyncRoomSummary()?.hasFailedSending == true
|
||||
R.id.timeline_setting -> true
|
||||
R.id.invite -> state.canInvite
|
||||
R.id.clear_all -> state.asyncRoomSummary()?.hasFailedSending == true
|
||||
R.id.open_matrix_apps -> true
|
||||
R.id.resend_all -> state.asyncRoomSummary()?.hasFailedSending == true
|
||||
R.id.timeline_setting -> true
|
||||
R.id.invite -> state.canInvite
|
||||
R.id.clear_all -> state.asyncRoomSummary()?.hasFailedSending == true
|
||||
R.id.open_matrix_apps -> true
|
||||
R.id.voice_call,
|
||||
R.id.video_call -> true // always show for discoverability
|
||||
R.id.hangup_call -> webRtcPeerConnectionManager.currentCall != null
|
||||
R.id.search -> true
|
||||
else -> false
|
||||
R.id.video_call -> true // always show for discoverability
|
||||
R.id.hangup_call -> webRtcPeerConnectionManager.currentCall != null
|
||||
R.id.search -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -582,6 +619,9 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||
is ParsedCommand.ErrorNotACommand -> {
|
||||
// Send the text message to the room
|
||||
room.sendTextMessage(action.text, autoMarkdown = action.autoMarkdown)
|
||||
viewModelScope.launch {
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.SendText(room.roomId))
|
||||
}
|
||||
_viewEvents.post(RoomDetailViewEvents.MessageSent)
|
||||
popDraft()
|
||||
}
|
||||
@@ -598,6 +638,9 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||
// Send the text message to the room, without markdown
|
||||
room.sendTextMessage(slashCommandResult.message, autoMarkdown = false)
|
||||
_viewEvents.post(RoomDetailViewEvents.MessageSent)
|
||||
viewModelScope.launch {
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.SendText(room.roomId))
|
||||
}
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.Invite -> {
|
||||
@@ -650,6 +693,9 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||
is ParsedCommand.SendRainbow -> {
|
||||
slashCommandResult.message.toString().let {
|
||||
room.sendFormattedTextMessage(it, rainbowGenerator.generate(it))
|
||||
viewModelScope.launch {
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.SendText(room.roomId))
|
||||
}
|
||||
}
|
||||
_viewEvents.post(RoomDetailViewEvents.SlashCommandHandled())
|
||||
popDraft()
|
||||
@@ -657,6 +703,9 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||
is ParsedCommand.SendRainbowEmote -> {
|
||||
slashCommandResult.message.toString().let {
|
||||
room.sendFormattedTextMessage(it, rainbowGenerator.generate(it), MessageType.MSGTYPE_EMOTE)
|
||||
viewModelScope.launch {
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.SendText(room.roomId))
|
||||
}
|
||||
}
|
||||
_viewEvents.post(RoomDetailViewEvents.SlashCommandHandled())
|
||||
popDraft()
|
||||
@@ -728,6 +777,9 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||
messageContent?.msgType ?: MessageType.MSGTYPE_TEXT,
|
||||
action.text,
|
||||
action.autoMarkdown)
|
||||
viewModelScope.launch {
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.SendEdit(room.roomId))
|
||||
}
|
||||
} else {
|
||||
Timber.w("Same message content, do not send edition")
|
||||
}
|
||||
@@ -755,12 +807,19 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||
} else {
|
||||
room.sendFormattedTextMessage(finalText, htmlText)
|
||||
}
|
||||
viewModelScope.launch {
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.SendText(room.roomId))
|
||||
}
|
||||
_viewEvents.post(RoomDetailViewEvents.MessageSent)
|
||||
popDraft()
|
||||
}
|
||||
is SendMode.REPLY -> {
|
||||
state.sendMode.timelineEvent.let {
|
||||
room.replyToMessage(it, action.text.toString(), action.autoMarkdown)
|
||||
viewModelScope.launch {
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.SendReply(room.roomId))
|
||||
}
|
||||
|
||||
_viewEvents.post(RoomDetailViewEvents.MessageSent)
|
||||
popDraft()
|
||||
}
|
||||
@@ -922,6 +981,11 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||
))
|
||||
}
|
||||
}
|
||||
viewModelScope.launch {
|
||||
attachments.forEach {
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.SendMedia(roomId = room.roomId, type = it.type))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleEventVisible(action: RoomDetailAction.TimelineEventTurnsVisible) {
|
||||
|
@@ -21,4 +21,5 @@ import im.vector.app.features.userdirectory.PendingInvitee
|
||||
|
||||
sealed class InviteUsersToRoomAction : VectorViewModelAction {
|
||||
data class InviteSelectedUsers(val invitees: Set<PendingInvitee>) : InviteUsersToRoomAction()
|
||||
object AnalyticsReportStartEvent : InviteUsersToRoomAction()
|
||||
}
|
||||
|
@@ -68,6 +68,10 @@ class InviteUsersToRoomActivity : SimpleFragmentActivity() {
|
||||
injector.inject(this)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
viewModel.handle(InviteUsersToRoomAction.AnalyticsReportStartEvent)
|
||||
}
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
|
@@ -24,15 +24,22 @@ import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import im.vector.app.features.userdirectory.PendingInvitee
|
||||
import io.reactivex.Observable
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
|
||||
import org.matrix.android.sdk.rx.rx
|
||||
|
||||
class InviteUsersToRoomViewModel @AssistedInject constructor(@Assisted
|
||||
initialState: InviteUsersToRoomViewState,
|
||||
session: Session,
|
||||
val stringProvider: StringProvider)
|
||||
val stringProvider: StringProvider,
|
||||
private val analyticsEngine: AnalyticsEngine)
|
||||
: VectorViewModel<InviteUsersToRoomViewState, InviteUsersToRoomAction, InviteUsersToRoomViewEvents>(initialState) {
|
||||
|
||||
private val room = session.getRoom(initialState.roomId)!!
|
||||
@@ -54,6 +61,38 @@ class InviteUsersToRoomViewModel @AssistedInject constructor(@Assisted
|
||||
override fun handle(action: InviteUsersToRoomAction) {
|
||||
when (action) {
|
||||
is InviteUsersToRoomAction.InviteSelectedUsers -> inviteUsersToRoom(action.invitees)
|
||||
InviteUsersToRoomAction.AnalyticsReportStartEvent -> handleReportInviteStart()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleReportInviteStart() {
|
||||
val joinRules = room.getStateEvent(EventType.STATE_ROOM_JOIN_RULES, QueryStringValue.IsNotEmpty)
|
||||
?.content.toModel<RoomJoinRulesContent>()
|
||||
?.joinRules
|
||||
|
||||
room.roomSummary()?.let { sum ->
|
||||
analyticsEngine.report(event = AnalyticsEngine.AnalyticEvent.BeginInvite(
|
||||
roomId = room.roomId,
|
||||
isEncrypted = sum.isEncrypted,
|
||||
memberCount = sum.joinedMembersCount ?: 0,
|
||||
isPublic = joinRules == RoomJoinRules.PUBLIC
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleReportSendInvites(invitees: Set<PendingInvitee>) {
|
||||
val joinRules = room.getStateEvent(EventType.STATE_ROOM_JOIN_RULES, QueryStringValue.IsNotEmpty)
|
||||
?.content.toModel<RoomJoinRulesContent>()
|
||||
?.joinRules
|
||||
|
||||
room.roomSummary()?.let { sum ->
|
||||
analyticsEngine.report(event = AnalyticsEngine.AnalyticEvent.SendInvite(
|
||||
roomId = room.roomId,
|
||||
inviteeCount = invitees.size,
|
||||
isEncrypted = sum.isEncrypted,
|
||||
memberCount = sum.joinedMembersCount ?: 0,
|
||||
isPublic = joinRules == RoomJoinRules.PUBLIC
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +118,7 @@ class InviteUsersToRoomViewModel @AssistedInject constructor(@Assisted
|
||||
invitees.size - 1)
|
||||
}
|
||||
_viewEvents.post(InviteUsersToRoomViewEvents.Success(successMessage))
|
||||
handleReportSendInvites(invitees)
|
||||
},
|
||||
{
|
||||
_viewEvents.post(InviteUsersToRoomViewEvents.Failure(it))
|
||||
|
@@ -19,9 +19,12 @@ package im.vector.app.features.lifecycle
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.os.Bundle
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import im.vector.app.features.popup.PopupAlertManager
|
||||
|
||||
class VectorActivityLifecycleCallbacks constructor(private val popupAlertManager: PopupAlertManager) : Application.ActivityLifecycleCallbacks {
|
||||
class VectorActivityLifecycleCallbacks constructor(
|
||||
private val popupAlertManager: PopupAlertManager,
|
||||
private val analyticsEngine: AnalyticsEngine) : Application.ActivityLifecycleCallbacks {
|
||||
override fun onActivityPaused(activity: Activity) {
|
||||
}
|
||||
|
||||
@@ -30,6 +33,7 @@ class VectorActivityLifecycleCallbacks constructor(private val popupAlertManager
|
||||
}
|
||||
|
||||
override fun onActivityStarted(activity: Activity) {
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.StartActivity(activity))
|
||||
}
|
||||
|
||||
override fun onActivityDestroyed(activity: Activity) {
|
||||
@@ -39,6 +43,7 @@ class VectorActivityLifecycleCallbacks constructor(private val popupAlertManager
|
||||
}
|
||||
|
||||
override fun onActivityStopped(activity: Activity) {
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.EndActivity(activity))
|
||||
}
|
||||
|
||||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
||||
|
@@ -34,6 +34,7 @@ import im.vector.app.core.extensions.configureAndStart
|
||||
import im.vector.app.core.extensions.exhaustive
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import im.vector.app.core.utils.ensureTrailingSlash
|
||||
import im.vector.app.features.signout.soft.SoftLogoutActivity
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
@@ -63,7 +64,8 @@ class LoginViewModel @AssistedInject constructor(
|
||||
private val activeSessionHolder: ActiveSessionHolder,
|
||||
private val homeServerConnectionConfigFactory: HomeServerConnectionConfigFactory,
|
||||
private val reAuthHelper: ReAuthHelper,
|
||||
private val stringProvider: StringProvider
|
||||
private val stringProvider: StringProvider,
|
||||
private val analyticsEngine: AnalyticsEngine
|
||||
) : VectorViewModel<LoginViewState, LoginAction, LoginViewEvents>(initialState) {
|
||||
|
||||
@AssistedInject.Factory
|
||||
@@ -686,6 +688,7 @@ class LoginViewModel @AssistedInject constructor(
|
||||
activeSessionHolder.setActiveSession(session)
|
||||
authenticationService.reset()
|
||||
session.configureAndStart(applicationContext)
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.Init(session))
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Success(Unit)
|
||||
|
@@ -21,6 +21,7 @@ import android.os.Build
|
||||
import androidx.core.content.edit
|
||||
import im.vector.app.core.di.DefaultSharedPreferences
|
||||
import im.vector.app.core.resources.VersionCodeProvider
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import im.vector.app.features.version.VersionProvider
|
||||
import org.matrix.android.sdk.api.Matrix
|
||||
import timber.log.Timber
|
||||
@@ -32,7 +33,8 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class VectorUncaughtExceptionHandler @Inject constructor(private val bugReporter: BugReporter,
|
||||
private val versionProvider: VersionProvider,
|
||||
private val versionCodeProvider: VersionCodeProvider) : Thread.UncaughtExceptionHandler {
|
||||
private val versionCodeProvider: VersionCodeProvider,
|
||||
private val analyticsEngine: AnalyticsEngine) : Thread.UncaughtExceptionHandler {
|
||||
|
||||
// key to save the crash status
|
||||
companion object {
|
||||
@@ -107,6 +109,8 @@ class VectorUncaughtExceptionHandler @Inject constructor(private val bugReporter
|
||||
|
||||
// Show the classical system popup
|
||||
previousHandler?.uncaughtException(thread, throwable)
|
||||
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.UnHandledCrash(throwable))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -95,6 +95,16 @@ class PublicRoomsFragment @Inject constructor(
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
viewModel.handle(RoomDirectoryAction.StartReport)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
viewModel.handle(RoomDirectoryAction.EndReport)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
publicRoomsController.callback = null
|
||||
publicRoomsList.cleanup()
|
||||
|
@@ -24,4 +24,6 @@ sealed class RoomDirectoryAction : VectorViewModelAction {
|
||||
data class FilterWith(val filter: String) : RoomDirectoryAction()
|
||||
object LoadMore : RoomDirectoryAction()
|
||||
data class JoinRoom(val roomId: String) : RoomDirectoryAction()
|
||||
object StartReport : RoomDirectoryAction()
|
||||
object EndReport : RoomDirectoryAction()
|
||||
}
|
||||
|
@@ -26,6 +26,7 @@ import com.airbnb.mvrx.appendAt
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
@@ -43,6 +44,7 @@ import timber.log.Timber
|
||||
private const val PUBLIC_ROOMS_LIMIT = 20
|
||||
|
||||
class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState: PublicRoomsViewState,
|
||||
private val analyticsEngine: AnalyticsEngine,
|
||||
private val session: Session)
|
||||
: VectorViewModel<PublicRoomsViewState, RoomDirectoryAction, RoomDirectoryViewEvents>(initialState) {
|
||||
|
||||
@@ -105,6 +107,12 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
||||
is RoomDirectoryAction.FilterWith -> filterWith(action)
|
||||
RoomDirectoryAction.LoadMore -> loadMore()
|
||||
is RoomDirectoryAction.JoinRoom -> joinRoom(action)
|
||||
RoomDirectoryAction.StartReport -> {
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.StartRoomDirectory)
|
||||
}
|
||||
RoomDirectoryAction.EndReport -> {
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.EndRoomDirectory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,6 +162,7 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
||||
}
|
||||
|
||||
private fun load(filter: String, roomDirectoryData: RoomDirectoryData) {
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.StartRoomDirectorySearch)
|
||||
currentTask = session.getPublicRooms(roomDirectoryData.homeServer,
|
||||
PublicRoomsParams(
|
||||
limit = PUBLIC_ROOMS_LIMIT,
|
||||
@@ -178,11 +187,13 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
||||
hasMore = since != null
|
||||
)
|
||||
}
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.EndRoomDirectorySearch(data.chunk?.size ?: 0))
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
if (failure is Failure.Cancelled) {
|
||||
// Ignore, another request should be already started
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.EndRoomDirectorySearch(0))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -193,6 +204,7 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
||||
asyncPublicRoomsRequest = Fail(failure)
|
||||
)
|
||||
}
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.EndRoomDirectorySearch(0))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -207,15 +219,23 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
||||
val viaServers = state.roomDirectoryData.homeServer?.let {
|
||||
listOf(it)
|
||||
} ?: emptyList()
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.StartJoinRoomEvent)
|
||||
session.joinRoom(action.roomId, viaServers = viaServers, callback = object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
||||
// Instead, we wait for the room to be joined
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.EndJoinRoomEvent(
|
||||
roomId = action.roomId,
|
||||
isPublic = true,
|
||||
memberCount = 0,
|
||||
isEncrypted = false
|
||||
))
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
// Notify the user
|
||||
_viewEvents.post(RoomDirectoryViewEvents.Failure(failure))
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.CancelJoinRoomEvent)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@@ -19,6 +19,7 @@ import androidx.preference.Preference
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.preference.PushRulePreference
|
||||
import im.vector.app.core.preference.VectorPreference
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import im.vector.app.core.utils.toast
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.pushrules.RuleIds
|
||||
@@ -81,6 +82,11 @@ class VectorSettingsAdvancedNotificationPreferenceFragment @Inject constructor()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.SettingsView("advanced_notification"))
|
||||
}
|
||||
|
||||
private fun refreshDisplay() {
|
||||
listView?.adapter?.notifyDataSetChanged()
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@ import androidx.preference.SeekBarPreference
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.core.preference.VectorSwitchPreference
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import im.vector.app.features.rageshake.RageShake
|
||||
|
||||
class VectorSettingsAdvancedSettingsFragment : VectorSettingsBaseFragment() {
|
||||
@@ -37,6 +38,7 @@ class VectorSettingsAdvancedSettingsFragment : VectorSettingsBaseFragment() {
|
||||
rageshake?.interceptor = {
|
||||
(activity as? VectorBaseActivity)?.showSnackbar(getString(R.string.rageshake_detected))
|
||||
}
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.SettingsView("advanced"))
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
|
@@ -27,11 +27,13 @@ import im.vector.app.core.di.HasScreenInjector
|
||||
import im.vector.app.core.di.ScreenComponent
|
||||
import im.vector.app.core.error.ErrorFormatter
|
||||
import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import im.vector.app.core.utils.toast
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.disposables.Disposable
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat(), HasScreenInjector {
|
||||
|
||||
@@ -62,7 +64,11 @@ abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat(), HasScree
|
||||
injectWith(injector())
|
||||
}
|
||||
|
||||
protected open fun injectWith(injector: ScreenComponent) = Unit
|
||||
@Inject protected lateinit var analyticsEngine: AnalyticsEngine
|
||||
|
||||
protected fun injectWith(injector: ScreenComponent) {
|
||||
injector.inject(this)
|
||||
}
|
||||
|
||||
override fun injector(): ScreenComponent {
|
||||
return screenComponent
|
||||
|
@@ -45,6 +45,7 @@ import im.vector.app.core.preference.UserAvatarPreference
|
||||
import im.vector.app.core.preference.VectorPreference
|
||||
import im.vector.app.core.preference.VectorSwitchPreference
|
||||
import im.vector.app.core.resources.ColorProvider
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import im.vector.app.core.utils.TextUtils
|
||||
import im.vector.app.core.utils.getSizeOfFiles
|
||||
import im.vector.app.core.utils.toast
|
||||
@@ -268,6 +269,7 @@ class VectorSettingsGeneralFragment @Inject constructor(
|
||||
mIdentityServerPreference.summary = session.identityService().getCurrentIdentityServerUrl() ?: getString(R.string.identity_server_not_defined)
|
||||
refreshIntegrationManagerSettings()
|
||||
session.integrationManagerService().addListener(integrationServiceListener)
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.SettingsView("general"))
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
|
@@ -23,6 +23,7 @@ import androidx.preference.Preference
|
||||
import im.vector.app.BuildConfig
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.preference.VectorPreference
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import im.vector.app.core.utils.copyToClipboard
|
||||
import im.vector.app.core.utils.displayInWebView
|
||||
import im.vector.app.core.utils.openUrlInChromeCustomTab
|
||||
@@ -124,6 +125,10 @@ class VectorSettingsHelpAboutFragment @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.SettingsView("about"))
|
||||
}
|
||||
companion object {
|
||||
private const val APP_INFO_LINK_PREFERENCE_KEY = "APP_INFO_LINK_PREFERENCE_KEY"
|
||||
}
|
||||
|
@@ -17,6 +17,7 @@
|
||||
package im.vector.app.features.settings
|
||||
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import javax.inject.Inject
|
||||
|
||||
class VectorSettingsLabsFragment @Inject constructor(
|
||||
@@ -29,4 +30,9 @@ class VectorSettingsLabsFragment @Inject constructor(
|
||||
override fun bindPref() {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.SettingsView("root"))
|
||||
}
|
||||
}
|
||||
|
@@ -33,6 +33,7 @@ import im.vector.app.core.preference.VectorPreference
|
||||
import im.vector.app.core.preference.VectorPreferenceCategory
|
||||
import im.vector.app.core.preference.VectorSwitchPreference
|
||||
import im.vector.app.core.pushers.PushersManager
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import im.vector.app.core.utils.isIgnoringBatteryOptimizations
|
||||
import im.vector.app.core.utils.requestDisablingBatteryOptimization
|
||||
import im.vector.app.features.notifications.NotificationUtils
|
||||
@@ -244,6 +245,7 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
|
||||
}
|
||||
|
||||
refreshPref()
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.SettingsView("notifications"))
|
||||
}
|
||||
|
||||
private fun refreshPref() {
|
||||
|
@@ -36,6 +36,7 @@ import im.vector.app.core.extensions.cleanup
|
||||
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||
import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import im.vector.app.features.notifications.NotificationUtils
|
||||
import im.vector.app.features.rageshake.BugReporter
|
||||
import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager
|
||||
@@ -47,7 +48,8 @@ import javax.inject.Inject
|
||||
|
||||
class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
|
||||
private val bugReporter: BugReporter,
|
||||
private val testManagerFactory: NotificationTroubleshootTestManagerFactory
|
||||
private val testManagerFactory: NotificationTroubleshootTestManagerFactory,
|
||||
private val analyticsEngine: AnalyticsEngine
|
||||
) : VectorBaseFragment() {
|
||||
|
||||
@BindView(R.id.troubleshoot_test_recycler_view)
|
||||
@@ -172,6 +174,8 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
|
||||
LocalBroadcastManager.getInstance(requireContext())
|
||||
.registerReceiver(broadcastReceiverNotification, IntentFilter(NotificationUtils.DIAGNOSTIC_ACTION))
|
||||
}
|
||||
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.SettingsView("troubleshoot_notification"))
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
|
@@ -27,6 +27,7 @@ import im.vector.app.R
|
||||
import im.vector.app.core.extensions.restart
|
||||
import im.vector.app.core.preference.VectorListPreference
|
||||
import im.vector.app.core.preference.VectorPreference
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import im.vector.app.features.configuration.VectorConfiguration
|
||||
import im.vector.app.features.themes.ThemeUtils
|
||||
import javax.inject.Inject
|
||||
@@ -46,6 +47,11 @@ class VectorSettingsPreferencesFragment @Inject constructor(
|
||||
findPreference<VectorPreference>(VectorPreferences.SETTINGS_INTERFACE_TEXT_SIZE_KEY)!!
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.SettingsView("preferences"))
|
||||
}
|
||||
|
||||
override fun bindPref() {
|
||||
// user interface preferences
|
||||
setUserInterfacePreferences()
|
||||
|
@@ -18,6 +18,7 @@ package im.vector.app.features.settings
|
||||
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.preference.VectorPreference
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import javax.inject.Inject
|
||||
|
||||
class VectorSettingsRootFragment @Inject constructor() : VectorSettingsBaseFragment() {
|
||||
@@ -34,4 +35,9 @@ class VectorSettingsRootFragment @Inject constructor() : VectorSettingsBaseFragm
|
||||
(preferenceScreen.getPreference(i) as? VectorPreference)?.let { it.tintIcon = true }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.SettingsView("root"))
|
||||
}
|
||||
}
|
||||
|
@@ -47,6 +47,7 @@ import im.vector.app.core.intent.getFilenameFromUri
|
||||
import im.vector.app.core.platform.SimpleTextWatcher
|
||||
import im.vector.app.core.preference.VectorPreference
|
||||
import im.vector.app.core.preference.VectorPreferenceCategory
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import im.vector.app.core.utils.copyToClipboard
|
||||
import im.vector.app.core.utils.openFileSelection
|
||||
import im.vector.app.core.utils.toast
|
||||
@@ -161,6 +162,8 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
|
||||
.getElementWellknown(session.myUserId)
|
||||
?.isE2EByDefault() == false
|
||||
}
|
||||
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.SettingsView("privacy"))
|
||||
}
|
||||
|
||||
private val secureBackupCategory by lazy {
|
||||
|
@@ -25,6 +25,7 @@ import androidx.preference.SwitchPreference
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||
import im.vector.app.core.preference.VectorPreference
|
||||
import im.vector.app.core.utils.AnalyticsEngine
|
||||
import im.vector.app.core.utils.getCallRingtoneName
|
||||
import im.vector.app.core.utils.getCallRingtoneUri
|
||||
import im.vector.app.core.utils.setCallRingtoneUri
|
||||
@@ -58,6 +59,11 @@ class VectorSettingsVoiceVideoFragment : VectorSettingsBaseFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
analyticsEngine.report(AnalyticsEngine.AnalyticEvent.SettingsView("voice_and_video"))
|
||||
}
|
||||
|
||||
private val ringtoneStartForActivityResult = registerStartForActivityResult { activityResult ->
|
||||
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||
val callRingtoneUri: Uri? = activityResult.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
|
||||
|
Reference in New Issue
Block a user