Skip to content

Commit

Permalink
Merge pull request #4734 from vector-im/feature/bma/analytics_next
Browse files Browse the repository at this point in the history
Implement analytics plan
  • Loading branch information
bmarty authored Jan 20, 2022
2 parents af43890 + 58197b8 commit 9bfeb6f
Show file tree
Hide file tree
Showing 59 changed files with 882 additions and 151 deletions.
1 change: 1 addition & 0 deletions changelog.d/4734.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Analytics: send more Events
2 changes: 1 addition & 1 deletion tools/check/forbidden_strings_in_code.txt
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ Formatter\.formatShortFileSize===1
# android\.text\.TextUtils

### This is not a rule, but a warning: the number of "enum class" has changed. For Json classes, it is mandatory that they have `@JsonClass(generateAdapter = false)`. If the enum is not used as a Json class, change the value in file forbidden_strings_in_code.txt
enum class===119
enum class===121

### Do not import temporary legacy classes
import org.matrix.android.sdk.internal.legacy.riot===3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -56,7 +56,7 @@ interface SingletonEntryPoint {

fun pinLocker(): PinLocker

fun analytics(): VectorAnalytics
fun analyticsTracker(): AnalyticsTracker

fun webRtcCallManager(): WebRtcCallManager

Expand Down
4 changes: 4 additions & 0 deletions vector/src/main/java/im/vector/app/core/di/SingletonModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import im.vector.app.core.error.DefaultErrorFormatter
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.time.Clock
import im.vector.app.core.time.DefaultClock
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.VectorAnalytics
import im.vector.app.features.analytics.impl.DefaultVectorAnalytics
import im.vector.app.features.invite.AutoAcceptInvites
Expand Down Expand Up @@ -64,6 +65,9 @@ abstract class VectorBindModule {
@Binds
abstract fun bindVectorAnalytics(analytics: DefaultVectorAnalytics): VectorAnalytics

@Binds
abstract fun bindAnalyticsTracker(analytics: DefaultVectorAnalytics): AnalyticsTracker

@Binds
abstract fun bindErrorFormatter(formatter: DefaultErrorFormatter): ErrorFormatter

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
* ========================================================================================== */
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -363,6 +373,7 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver

override fun onPause() {
super.onPause()
screenEvent?.send(analyticsTracker, analyticsScreenName)
Timber.i("onPause Activity ${javaClass.simpleName}")

rageShake.stop()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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?)

Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<*>
Expand All @@ -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

Expand Down Expand Up @@ -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()
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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

import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
import im.vector.app.features.analytics.itf.VectorAnalyticsScreen

interface AnalyticsTracker {
/**
* Capture an Event
*/
fun capture(event: VectorAnalyticsEvent)

/**
* Track a displayed screen
*/
fun screen(screen: VectorAnalyticsScreen)
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ private const val CHECK_INTERVAL = 2_000L
*/
@Singleton
class DecryptionFailureTracker @Inject constructor(
private val vectorAnalytics: VectorAnalytics,
private val analyticsTracker: AnalyticsTracker,
private val clock: Clock
) {

Expand Down Expand Up @@ -136,7 +136,7 @@ class DecryptionFailureTracker @Inject constructor(
// for now we ignore events already reported even if displayed again?
.filter { alreadyReported.contains(it).not() }
.forEach { failedEventId ->
vectorAnalytics.capture(Error(failedEventId, Error.Domain.E2EE, aggregation.key))
analyticsTracker.capture(Error(failedEventId, Error.Domain.E2EE, aggregation.key))
alreadyReported.add(failedEventId)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@

package im.vector.app.features.analytics

import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
import im.vector.app.features.analytics.itf.VectorAnalyticsScreen
import kotlinx.coroutines.flow.Flow

interface VectorAnalytics {
interface VectorAnalytics : AnalyticsTracker {
/**
* Return a Flow of Boolean, true if the user has given their consent
*/
Expand Down Expand Up @@ -60,14 +58,4 @@ interface VectorAnalytics {
* To be called when application is started
*/
fun init()

/**
* Capture an Event
*/
fun capture(event: VectorAnalyticsEvent)

/**
* Track a displayed screen
*/
fun screen(screen: VectorAnalyticsScreen)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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.extensions

import im.vector.app.features.analytics.plan.JoinedRoom
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom

fun Int?.toAnalyticsRoomSize(): JoinedRoom.RoomSize {
return when (this) {
null,
2 -> JoinedRoom.RoomSize.Two
in 3..10 -> JoinedRoom.RoomSize.ThreeToTen
in 11..100 -> JoinedRoom.RoomSize.ElevenToOneHundred
in 101..1000 -> JoinedRoom.RoomSize.OneHundredAndOneToAThousand
else -> JoinedRoom.RoomSize.MoreThanAThousand
}
}

fun RoomSummary?.toAnalyticsJoinedRoom(): JoinedRoom {
return JoinedRoom(
isDM = this?.isDirect.orFalse(),
roomSize = this?.joinedMembersCount?.toAnalyticsRoomSize() ?: JoinedRoom.RoomSize.Two
)
}

fun PublicRoom.toAnalyticsJoinedRoom(): JoinedRoom {
return JoinedRoom(
isDM = false,
roomSize = numJoinedMembers.toAnalyticsRoomSize()
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,22 @@ import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
* 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,
/**
* 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"
Expand Down
Loading

0 comments on commit 9bfeb6f

Please sign in to comment.