Analytics: Framework to send screen event

This commit is contained in:
Benoit Marty 2021-12-15 15:47:53 +01:00 committed by Benoit Marty
parent a8c29f55f5
commit 0a08a50e11
7 changed files with 121 additions and 18 deletions

View file

@ -21,7 +21,7 @@ import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import im.vector.app.core.dialogs.UnrecognizedCertificateDialog
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.features.analytics.VectorAnalytics
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.call.webrtc.WebRtcCallManager
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.navigation.Navigator
@ -56,7 +56,7 @@ interface SingletonEntryPoint {
fun pinLocker(): PinLocker
fun analytics(): VectorAnalytics
fun analyticsTracker(): AnalyticsTracker
fun webRtcCallManager(): WebRtcCallManager

View file

@ -65,7 +65,9 @@ import im.vector.app.core.extensions.toMvRxBundle
import im.vector.app.core.utils.toast
import im.vector.app.features.MainActivity
import im.vector.app.features.MainActivityArgs
import im.vector.app.features.analytics.VectorAnalytics
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.plan.Screen
import im.vector.app.features.analytics.screen.ScreenEvent
import im.vector.app.features.configuration.VectorConfiguration
import im.vector.app.features.consent.ConsentNotGivenHelper
import im.vector.app.features.navigation.Navigator
@ -90,6 +92,15 @@ import timber.log.Timber
import javax.inject.Inject
abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), MavericksView {
/* ==========================================================================================
* Analytics
* ========================================================================================== */
protected var analyticsScreenName: Screen.ScreenName? = null
private var screenEvent: ScreenEvent? = null
protected lateinit var analyticsTracker: AnalyticsTracker
/* ==========================================================================================
* View
* ========================================================================================== */
@ -133,7 +144,6 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
private lateinit var sessionListener: SessionListener
protected lateinit var bugReporter: BugReporter
private lateinit var pinLocker: PinLocker
protected lateinit var analytics: VectorAnalytics
@Inject
lateinit var rageShake: RageShake
@ -189,7 +199,7 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
configurationViewModel = viewModelProvider.get(ConfigurationViewModel::class.java)
bugReporter = singletonEntryPoint.bugReporter()
pinLocker = singletonEntryPoint.pinLocker()
analytics = singletonEntryPoint.analytics()
analyticsTracker = singletonEntryPoint.analyticsTracker()
navigator = singletonEntryPoint.navigator()
activeSessionHolder = singletonEntryPoint.activeSessionHolder()
vectorPreferences = singletonEntryPoint.vectorPreferences()
@ -324,7 +334,7 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
override fun onResume() {
super.onResume()
Timber.i("onResume Activity ${javaClass.simpleName}")
screenEvent = analyticsScreenName?.let { ScreenEvent(it) }
configurationViewModel.onActivityResumed()
if (this !is BugReportActivity && vectorPreferences.useRageshake()) {
@ -363,6 +373,7 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
override fun onPause() {
super.onPause()
screenEvent?.send(analyticsTracker)
Timber.i("onPause Activity ${javaClass.simpleName}")
rageShake.stop()

View file

@ -37,7 +37,9 @@ import im.vector.app.core.di.ActivityEntryPoint
import im.vector.app.core.extensions.singletonEntryPoint
import im.vector.app.core.extensions.toMvRxBundle
import im.vector.app.core.utils.DimensionConverter
import im.vector.app.features.analytics.VectorAnalytics
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.plan.Screen
import im.vector.app.features.analytics.screen.ScreenEvent
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.view.clicks
@ -47,6 +49,14 @@ import timber.log.Timber
* Add Mavericks capabilities, handle DI and bindings.
*/
abstract class VectorBaseBottomSheetDialogFragment<VB : ViewBinding> : BottomSheetDialogFragment(), MavericksView {
/* ==========================================================================================
* Analytics
* ========================================================================================== */
protected var analyticsScreenName: Screen.ScreenName? = null
private var screenEvent: ScreenEvent? = null
protected lateinit var analyticsTracker: AnalyticsTracker
/* ==========================================================================================
* View
@ -84,8 +94,6 @@ abstract class VectorBaseBottomSheetDialogFragment<VB : ViewBinding> : BottomShe
open val showExpanded = false
protected lateinit var analytics: VectorAnalytics
interface ResultListener {
fun onBottomSheetResult(resultCode: Int, data: Any?)
@ -124,13 +132,19 @@ abstract class VectorBaseBottomSheetDialogFragment<VB : ViewBinding> : BottomShe
val activityEntryPoint = EntryPointAccessors.fromActivity(vectorBaseActivity, ActivityEntryPoint::class.java)
viewModelFactory = activityEntryPoint.viewModelFactory()
val singletonEntryPoint = context.singletonEntryPoint()
analytics = singletonEntryPoint.analytics()
analyticsTracker = singletonEntryPoint.analyticsTracker()
super.onAttach(context)
}
override fun onResume() {
super.onResume()
Timber.i("onResume BottomSheet ${javaClass.simpleName}")
screenEvent = analyticsScreenName?.let { ScreenEvent(it) }
}
override fun onPause() {
super.onPause()
screenEvent?.send(analyticsTracker)
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {

View file

@ -42,7 +42,9 @@ import im.vector.app.core.dialogs.UnrecognizedCertificateDialog
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.extensions.singletonEntryPoint
import im.vector.app.core.extensions.toMvRxBundle
import im.vector.app.features.analytics.VectorAnalytics
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.plan.Screen
import im.vector.app.features.analytics.screen.ScreenEvent
import im.vector.app.features.navigation.Navigator
import im.vector.lib.ui.styles.dialogs.MaterialProgressDialog
import kotlinx.coroutines.flow.launchIn
@ -51,6 +53,18 @@ import reactivecircus.flowbinding.android.view.clicks
import timber.log.Timber
abstract class VectorBaseFragment<VB : ViewBinding> : Fragment(), MavericksView {
/* ==========================================================================================
* Analytics
* ========================================================================================== */
protected var analyticsScreenName: Screen.ScreenName? = null
private var screenEvent: ScreenEvent? = null
protected lateinit var analyticsTracker: AnalyticsTracker
/* ==========================================================================================
* Activity
* ========================================================================================== */
protected val vectorBaseActivity: VectorBaseActivity<*> by lazy {
activity as VectorBaseActivity<*>
@ -61,7 +75,6 @@ abstract class VectorBaseFragment<VB : ViewBinding> : Fragment(), MavericksView
* ========================================================================================== */
protected lateinit var navigator: Navigator
protected lateinit var analytics: VectorAnalytics
protected lateinit var errorFormatter: ErrorFormatter
protected lateinit var unrecognizedCertificateDialog: UnrecognizedCertificateDialog
@ -98,7 +111,7 @@ abstract class VectorBaseFragment<VB : ViewBinding> : Fragment(), MavericksView
val activityEntryPoint = EntryPointAccessors.fromActivity(vectorBaseActivity, ActivityEntryPoint::class.java)
navigator = singletonEntryPoint.navigator()
errorFormatter = singletonEntryPoint.errorFormatter()
analytics = singletonEntryPoint.analytics()
analyticsTracker = singletonEntryPoint.analyticsTracker()
unrecognizedCertificateDialog = singletonEntryPoint.unrecognizedCertificateDialog()
viewModelFactory = activityEntryPoint.viewModelFactory()
childFragmentManager.fragmentFactory = activityEntryPoint.fragmentFactory()
@ -125,12 +138,14 @@ abstract class VectorBaseFragment<VB : ViewBinding> : Fragment(), MavericksView
override fun onResume() {
super.onResume()
Timber.i("onResume Fragment ${javaClass.simpleName}")
screenEvent = analyticsScreenName?.let { ScreenEvent(it) }
}
@CallSuper
override fun onPause() {
super.onPause()
Timber.i("onPause Fragment ${javaClass.simpleName}")
screenEvent?.send(analyticsTracker)
}
@CallSuper

View file

@ -0,0 +1,46 @@
/*
* 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.screen
import android.os.SystemClock
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.plan.Screen
import timber.log.Timber
/**
* Track a screen display. Unique usage.
*/
class ScreenEvent(val screenName: Screen.ScreenName) {
private val startTime = SystemClock.elapsedRealtime()
// Protection to avoid multiple sending
private var isSent = false
fun send(analyticsTracker: AnalyticsTracker) {
if (isSent) {
Timber.w("Event $screenName Already sent!")
return
}
isSent = true
analyticsTracker.screen(
Screen(
screenName = screenName,
durationMs = (SystemClock.elapsedRealtime() - startTime).toInt()
)
)
}
}

View file

@ -116,7 +116,6 @@ import im.vector.app.core.utils.startInstallFromSourceIntent
import im.vector.app.core.utils.toast
import im.vector.app.databinding.DialogReportContentBinding
import im.vector.app.databinding.FragmentRoomDetailBinding
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.plan.Click
import im.vector.app.features.attachments.AttachmentTypeSelectorView
import im.vector.app.features.attachments.AttachmentsHelper
@ -259,7 +258,6 @@ class RoomDetailFragment @Inject constructor(
private val roomDetailPendingActionStore: RoomDetailPendingActionStore,
private val pillsPostProcessorFactory: PillsPostProcessor.Factory,
private val callManager: WebRtcCallManager,
private val analyticsTracker: AnalyticsTracker,
private val voiceMessagePlaybackTracker: VoiceMessagePlaybackTracker,
private val clock: Clock
) :

View file

@ -29,7 +29,9 @@ import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.extensions.singletonEntryPoint
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.utils.toast
import im.vector.app.features.analytics.VectorAnalytics
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.plan.Screen
import im.vector.app.features.analytics.screen.ScreenEvent
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.session.Session
@ -37,6 +39,18 @@ import reactivecircus.flowbinding.android.view.clicks
import timber.log.Timber
abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat(), MavericksView {
/* ==========================================================================================
* Analytics
* ========================================================================================== */
protected var analyticsScreenName: Screen.ScreenName? = null
private var screenEvent: ScreenEvent? = null
protected lateinit var analyticsTracker: AnalyticsTracker
/* ==========================================================================================
* Activity
* ========================================================================================== */
val vectorActivity: VectorBaseActivity<*> by lazy {
activity as VectorBaseActivity<*>
@ -47,7 +61,6 @@ abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat(), Maverick
// members
protected lateinit var session: Session
protected lateinit var errorFormatter: ErrorFormatter
protected lateinit var analytics: VectorAnalytics
/* ==========================================================================================
* Views
@ -72,17 +85,23 @@ abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat(), Maverick
super.onAttach(context)
session = singletonEntryPoint.activeSessionHolder().getActiveSession()
errorFormatter = singletonEntryPoint.errorFormatter()
analytics = singletonEntryPoint.analytics()
analyticsTracker = singletonEntryPoint.analyticsTracker()
}
override fun onResume() {
super.onResume()
Timber.i("onResume Fragment ${javaClass.simpleName}")
screenEvent = analyticsScreenName?.let { ScreenEvent(it) }
vectorActivity.supportActionBar?.setTitle(titleRes)
// find the view from parent activity
mLoadingView = vectorActivity.findViewById(R.id.vector_settings_spinner_views)
}
override fun onPause() {
super.onPause()
screenEvent?.send(analyticsTracker)
}
abstract fun bindPref()
abstract var titleRes: Int