added analytics for app layout (#7242)

This commit is contained in:
Nikita Fedrunov 2022-10-03 11:47:58 +02:00 committed by GitHub
parent 8fd0107d84
commit 9f8c7688bf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 144 additions and 15 deletions

1
changelog.d/6508.misc Normal file
View file

@ -0,0 +1 @@
[AppLayout]: added tracking of new analytics events

View file

@ -22,6 +22,7 @@ import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.utils.BehaviorDataSource
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.plan.UserProperties
import im.vector.app.features.analytics.plan.ViewRoom
import im.vector.app.features.session.coroutineScope
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.ui.UiStateRepository
@ -82,6 +83,13 @@ class SpaceStateHandlerImpl @Inject constructor(
return
}
analyticsTracker.capture(
ViewRoom(
isDM = false,
isSpace = true,
)
)
if (isForwardNavigation) {
addToBackstack(spaceToLeave, spaceToSet)
}

View file

@ -17,6 +17,7 @@
package im.vector.app.features.analytics.extensions
import im.vector.app.features.analytics.plan.UserProperties
import im.vector.app.features.home.room.list.home.header.HomeRoomFilter
import im.vector.app.features.onboarding.FtueUseCase
fun FtueUseCase.toTrackingValue(): UserProperties.FtueUseCaseSelection {
@ -27,3 +28,12 @@ fun FtueUseCase.toTrackingValue(): UserProperties.FtueUseCaseSelection {
FtueUseCase.SKIP -> UserProperties.FtueUseCaseSelection.Skip
}
}
fun HomeRoomFilter.toTrackingValue(): UserProperties.AllChatsActiveFilter {
return when (this) {
HomeRoomFilter.ALL -> UserProperties.AllChatsActiveFilter.All
HomeRoomFilter.UNREADS -> UserProperties.AllChatsActiveFilter.Unreads
HomeRoomFilter.FAVOURITES -> UserProperties.AllChatsActiveFilter.Favourites
HomeRoomFilter.PEOPlE -> UserProperties.AllChatsActiveFilter.People
}
}

View file

@ -34,6 +34,9 @@ import im.vector.app.core.platform.StateView
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.DrawableProvider
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.extensions.toTrackingValue
import im.vector.app.features.analytics.plan.UserProperties
import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.room.list.home.header.HomeRoomFilter
import kotlinx.coroutines.flow.Flow
@ -74,6 +77,7 @@ class HomeRoomListViewModel @AssistedInject constructor(
private val preferencesStore: HomeLayoutPreferencesStore,
private val stringProvider: StringProvider,
private val drawableProvider: DrawableProvider,
private val analyticsTracker: AnalyticsTracker,
) : VectorViewModel<HomeRoomListViewState, HomeRoomListAction, HomeRoomListViewEvents>(initialState) {
@AssistedFactory
@ -358,6 +362,7 @@ class HomeRoomListViewModel @AssistedInject constructor(
}
setState { copy(headersData = headersData.copy(currentFilter = newFilter)) }
updateEmptyState()
analyticsTracker.updateUserProperties(UserProperties(allChatsActiveFilter = newFilter.toTrackingValue()))
filteredPagedRoomSummariesLive?.let { liveResults ->
liveResults.queryParams = getFilteredQueryParams(newFilter, liveResults.queryParams)
}

View file

@ -28,6 +28,7 @@ import com.google.android.material.color.MaterialColors
import im.vector.app.R
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.FirstItemUpdatedObserver
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.list.RoomListListener
import org.matrix.android.sdk.api.session.room.model.RoomSummary
@ -38,6 +39,7 @@ class HomeRoomsHeadersController @Inject constructor(
val stringProvider: StringProvider,
private val avatarRenderer: AvatarRenderer,
resources: Resources,
private val analyticsTracker: AnalyticsTracker,
) : EpoxyController() {
private var data: RoomsHeadersData = RoomsHeadersData()
@ -73,7 +75,11 @@ class HomeRoomsHeadersController @Inject constructor(
}
host.data.filtersList?.let {
addRoomFilterHeaderItem(host.onFilterChangedListener, it, host.data.currentFilter)
addRoomFilterHeaderItem(
filterChangedListener = host.onFilterChangedListener,
filtersList = it,
currentFilter = host.data.currentFilter,
analyticsTracker = analyticsTracker)
}
}
@ -158,12 +164,14 @@ class HomeRoomsHeadersController @Inject constructor(
filterChangedListener: ((HomeRoomFilter) -> Unit)?,
filtersList: List<HomeRoomFilter>,
currentFilter: HomeRoomFilter?,
analyticsTracker: AnalyticsTracker,
) {
roomFilterHeaderItem {
id("filter_header")
filtersData(filtersList)
selectedFilter(currentFilter)
onFilterChangedListener(filterChangedListener)
analyticsTracker(analyticsTracker)
}
}

View file

@ -22,6 +22,8 @@ import com.google.android.material.tabs.TabLayout
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.plan.Interaction
@EpoxyModelClass
abstract class RoomFilterHeaderItem : VectorEpoxyModel<RoomFilterHeaderItem.Holder>(R.layout.item_home_filter_tabs) {
@ -35,6 +37,9 @@ abstract class RoomFilterHeaderItem : VectorEpoxyModel<RoomFilterHeaderItem.Hold
@EpoxyAttribute
var selectedFilter: HomeRoomFilter? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var analyticsTracker: AnalyticsTracker? = null
override fun bind(holder: Holder) {
super.bind(holder)
with(holder.tabLayout) {
@ -51,6 +56,7 @@ abstract class RoomFilterHeaderItem : VectorEpoxyModel<RoomFilterHeaderItem.Hold
addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
(tab?.tag as? HomeRoomFilter)?.let { filter ->
trackFilterChangeEvent(filter)
onFilterChangedListener?.invoke(filter)
}
}
@ -61,6 +67,23 @@ abstract class RoomFilterHeaderItem : VectorEpoxyModel<RoomFilterHeaderItem.Hold
}
}
private fun trackFilterChangeEvent(filter: HomeRoomFilter) {
val interactionName = when (filter) {
HomeRoomFilter.ALL -> Interaction.Name.MobileAllChatsFilterAll
HomeRoomFilter.UNREADS -> Interaction.Name.MobileAllChatsFilterUnreads
HomeRoomFilter.FAVOURITES -> Interaction.Name.MobileAllChatsFilterFavourites
HomeRoomFilter.PEOPlE -> Interaction.Name.MobileAllChatsFilterPeople
}
analyticsTracker?.capture(
Interaction(
index = null,
interactionType = null,
name = interactionName
)
)
}
override fun unbind(holder: Holder) {
holder.tabLayout.clearOnTabSelectedListeners()
super.unbind(holder)

View file

@ -27,6 +27,7 @@ import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.StateView
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentInvitesBinding
import im.vector.app.features.analytics.plan.MobileScreen
import im.vector.app.features.analytics.plan.ViewRoom
import im.vector.app.features.home.room.list.RoomListListener
import im.vector.app.features.notifications.NotificationDrawerManager
@ -48,6 +49,11 @@ class InvitesFragment : VectorBaseFragment<FragmentInvitesBinding>(), RoomListLi
return FragmentInvitesBinding.inflate(inflater, container, false)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
analyticsScreenName = MobileScreen.ScreenName.Invites
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

View file

@ -25,6 +25,7 @@ import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.databinding.BottomSheetHomeLayoutSettingsBinding
import im.vector.app.features.analytics.plan.Interaction
import im.vector.app.features.home.room.list.home.HomeLayoutPreferencesStore
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
@ -54,9 +55,11 @@ class HomeLayoutSettingBottomDialogFragment : VectorBaseBottomSheetDialogFragmen
}
views.homeLayoutSettingsRecents.setOnCheckedChangeListener { _, isChecked ->
trackRecentsStateEvent(isChecked)
setRecentsEnabled(isChecked)
}
views.homeLayoutSettingsFilters.setOnCheckedChangeListener { _, isChecked ->
trackFiltersStateEvent(isChecked)
setFiltersEnabled(isChecked)
}
views.homeLayoutSettingsSortGroup.setOnCheckedChangeListener { _, checkedId ->
@ -64,10 +67,40 @@ class HomeLayoutSettingBottomDialogFragment : VectorBaseBottomSheetDialogFragmen
}
}
private fun trackRecentsStateEvent(areEnabled: Boolean) {
val interactionName = if (areEnabled) {
Interaction.Name.MobileAllChatsRecentsEnabled
} else {
Interaction.Name.MobileAllChatsRecentsDisabled
}
analyticsTracker.capture(
Interaction(
index = null,
interactionType = null,
name = interactionName
)
)
}
private fun setRecentsEnabled(isEnabled: Boolean) = lifecycleScope.launch {
preferencesStore.setRecentsEnabled(isEnabled)
}
private fun trackFiltersStateEvent(areEnabled: Boolean) {
val interactionName = if (areEnabled) {
Interaction.Name.MobileAllChatsFiltersEnabled
} else {
Interaction.Name.MobileAllChatsFiltersDisabled
}
analyticsTracker.capture(
Interaction(
index = null,
interactionType = null,
name = interactionName
)
)
}
private fun setFiltersEnabled(isEnabled: Boolean) = lifecycleScope.launch {
preferencesStore.setFiltersEnabled(isEnabled)
}

View file

@ -72,7 +72,7 @@ class NewSpaceSummaryController @Inject constructor(
text(host.stringProvider.getString(R.string.all_chats))
selected(selected)
countState(UnreadCounterBadgeView.State.Count(homeCount.totalCount, homeCount.isHighlight))
listener { host.callback?.onSpaceSelected(null) }
listener { host.callback?.onSpaceSelected(null, isSubSpace = false) }
}
}
@ -99,7 +99,7 @@ class NewSpaceSummaryController @Inject constructor(
hasChildren(hasChildren)
matrixItem(spaceSummary.toMatrixItem())
onLongClickListener { host.callback?.onSpaceSettings(spaceSummary) }
onSpaceSelectedListener { host.callback?.onSpaceSelected(spaceSummary) }
onSpaceSelectedListener { host.callback?.onSpaceSelected(spaceSummary, isSubSpace = false) }
onToggleExpandListener { host.callback?.onToggleExpand(spaceSummary) }
selected(isSelected)
}
@ -140,7 +140,7 @@ class NewSpaceSummaryController @Inject constructor(
indent(depth)
matrixItem(childSummary.toMatrixItem())
onLongClickListener { host.callback?.onSpaceSettings(childSummary) }
onSubSpaceSelectedListener { host.callback?.onSpaceSelected(childSummary) }
onSubSpaceSelectedListener { host.callback?.onSpaceSelected(childSummary, isSubSpace = true) }
onToggleExpandListener { host.callback?.onToggleExpand(childSummary) }
selected(isSelected)
}
@ -184,8 +184,10 @@ class NewSpaceSummaryController @Inject constructor(
}
}
/**
* This is a full duplicate of [SpaceSummaryController.Callback]. We need to merge them ASAP*/
interface Callback {
fun onSpaceSelected(spaceSummary: RoomSummary?)
fun onSpaceSelected(spaceSummary: RoomSummary?, isSubSpace: Boolean)
fun onSpaceInviteSelected(spaceSummary: RoomSummary)
fun onSpaceSettings(spaceSummary: RoomSummary)
fun onToggleExpand(spaceSummary: RoomSummary)

View file

@ -20,7 +20,7 @@ import im.vector.app.core.platform.VectorViewModelAction
import org.matrix.android.sdk.api.session.room.model.RoomSummary
sealed class SpaceListAction : VectorViewModelAction {
data class SelectSpace(val spaceSummary: RoomSummary?) : SpaceListAction()
data class SelectSpace(val spaceSummary: RoomSummary?, val isSubSpace: Boolean) : SpaceListAction()
data class OpenSpaceInvite(val spaceSummary: RoomSummary) : SpaceListAction()
data class LeaveSpace(val spaceSummary: RoomSummary) : SpaceListAction()
data class ToggleExpand(val spaceSummary: RoomSummary) : SpaceListAction()

View file

@ -25,6 +25,7 @@ import im.vector.app.R
import im.vector.app.core.extensions.replaceChildFragment
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.databinding.FragmentSpacesBottomSheetBinding
import im.vector.app.features.analytics.plan.MobileScreen
class SpaceListBottomSheet : VectorBaseBottomSheetDialogFragment<FragmentSpacesBottomSheetBinding>() {
@ -32,6 +33,11 @@ class SpaceListBottomSheet : VectorBaseBottomSheetDialogFragment<FragmentSpacesB
return FragmentSpacesBottomSheetBinding.inflate(inflater, container, false)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
analyticsScreenName = MobileScreen.ScreenName.SpaceBottomSheet
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (savedInstanceState == null) {
replaceChildFragment(R.id.space_list, SpaceListFragment::class.java)

View file

@ -176,8 +176,8 @@ class SpaceListFragment :
}
}
override fun onSpaceSelected(spaceSummary: RoomSummary?) {
viewModel.handle(SpaceListAction.SelectSpace(spaceSummary))
override fun onSpaceSelected(spaceSummary: RoomSummary?, isSubSpace: Boolean) {
viewModel.handle(SpaceListAction.SelectSpace(spaceSummary, isSubSpace = isSubSpace))
roomListSharedActionViewModel.post(RoomListSharedAction.CloseBottomSheet)
}

View file

@ -215,7 +215,18 @@ class SpaceListViewModel @AssistedInject constructor(
private fun handleSelectSpace(action: SpaceListAction.SelectSpace) = withState { state ->
if (state.selectedSpace?.roomId != action.spaceSummary?.roomId) {
analyticsTracker.capture(Interaction(null, null, Interaction.Name.SpacePanelSwitchSpace))
val interactionName = if (action.isSubSpace) {
Interaction.Name.SpacePanelSwitchSubSpace
} else {
Interaction.Name.SpacePanelSwitchSpace
}
analyticsTracker.capture(
Interaction(
index = null,
interactionType = null,
name = interactionName
)
)
setState { copy(selectedSpace = action.spaceSummary) }
spaceStateHandler.setCurrentSpace(action.spaceSummary?.roomId)
_viewEvents.post(SpaceListViewEvents.CloseDrawer)

View file

@ -88,7 +88,7 @@ class SpaceSummaryController @Inject constructor(
id("space_home")
selected(selectedSpace == null)
countState(UnreadCounterBadgeView.State.Count(homeCount.totalCount, homeCount.isHighlight))
listener { host.callback?.onSpaceSelected(null) }
listener { host.callback?.onSpaceSelected(null, isSubSpace = false) }
}
rootSpaces
@ -114,7 +114,7 @@ class SpaceSummaryController @Inject constructor(
selected(isSelected)
canDrag(true)
onMore { host.callback?.onSpaceSettings(roomSummary) }
listener { host.callback?.onSpaceSelected(roomSummary) }
listener { host.callback?.onSpaceSelected(roomSummary, isSubSpace = false) }
toggleExpand { host.callback?.onToggleExpand(roomSummary) }
countState(
UnreadCounterBadgeView.State.Count(
@ -165,7 +165,7 @@ class SpaceSummaryController @Inject constructor(
expanded(expanded)
onMore { host.callback?.onSpaceSettings(childSummary) }
matrixItem(childSummary.toMatrixItem())
listener { host.callback?.onSpaceSelected(childSummary) }
listener { host.callback?.onSpaceSelected(childSummary, isSubSpace = true) }
toggleExpand { host.callback?.onToggleExpand(childSummary) }
indent(currentDepth)
countState(
@ -184,7 +184,7 @@ class SpaceSummaryController @Inject constructor(
}
interface Callback {
fun onSpaceSelected(spaceSummary: RoomSummary?)
fun onSpaceSelected(spaceSummary: RoomSummary?, isSubSpace: Boolean)
fun onSpaceInviteSelected(spaceSummary: RoomSummary)
fun onSpaceSettings(spaceSummary: RoomSummary)
fun onToggleExpand(spaceSummary: RoomSummary)

View file

@ -25,6 +25,7 @@ import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentSpaceCreateChooseTypeBinding
import im.vector.app.features.analytics.plan.MobileScreen
@AndroidEntryPoint
class ChooseSpaceTypeFragment :
@ -35,6 +36,11 @@ class ChooseSpaceTypeFragment :
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) =
FragmentSpaceCreateChooseTypeBinding.inflate(layoutInflater, container, false)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
analyticsScreenName = MobileScreen.ScreenName.CreateSpace
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

View file

@ -32,6 +32,8 @@ import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.extensions.isEmail
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.plan.Interaction
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.MatrixPatterns
@ -46,7 +48,8 @@ class CreateSpaceViewModel @AssistedInject constructor(
private val session: Session,
private val stringProvider: StringProvider,
private val createSpaceViewModelTask: CreateSpaceViewModelTask,
private val errorFormatter: ErrorFormatter
private val errorFormatter: ErrorFormatter,
private val analyticsTracker: AnalyticsTracker,
) : VectorViewModel<CreateSpaceState, CreateSpaceAction, CreateSpaceEvents>(initialState) {
private val identityService = session.identityService()
@ -350,6 +353,13 @@ class CreateSpaceViewModel @AssistedInject constructor(
}
viewModelScope.launch(Dispatchers.IO) {
try {
analyticsTracker.capture(
Interaction(
index = null,
interactionType = null,
name = Interaction.Name.MobileSpaceCreationValidated
)
)
val alias = if (state.spaceType == SpaceType.Public) {
state.aliasLocalPart
} else null

View file

@ -19,4 +19,4 @@ package im.vector.app.test.fakes
import im.vector.app.features.analytics.AnalyticsTracker
import io.mockk.mockk
class FakeAnalyticsTracker : AnalyticsTracker by mockk()
class FakeAnalyticsTracker : AnalyticsTracker by mockk(relaxUnitFun = true)