diff --git a/changelog.d/6508.misc b/changelog.d/6508.misc new file mode 100644 index 0000000000..096fb750c2 --- /dev/null +++ b/changelog.d/6508.misc @@ -0,0 +1 @@ +[AppLayout]: added tracking of new analytics events diff --git a/vector/src/main/java/im/vector/app/SpaceStateHandlerImpl.kt b/vector/src/main/java/im/vector/app/SpaceStateHandlerImpl.kt index a665b619c0..d230e76c1e 100644 --- a/vector/src/main/java/im/vector/app/SpaceStateHandlerImpl.kt +++ b/vector/src/main/java/im/vector/app/SpaceStateHandlerImpl.kt @@ -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) } diff --git a/vector/src/main/java/im/vector/app/features/analytics/extensions/UserPropertiesExt.kt b/vector/src/main/java/im/vector/app/features/analytics/extensions/UserPropertiesExt.kt index e5446f438b..0ff04f0854 100644 --- a/vector/src/main/java/im/vector/app/features/analytics/extensions/UserPropertiesExt.kt +++ b/vector/src/main/java/im/vector/app/features/analytics/extensions/UserPropertiesExt.kt @@ -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 + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt index 7f62c68850..734fc102bf 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt @@ -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(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) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/header/HomeRoomsHeadersController.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/header/HomeRoomsHeadersController.kt index 56cccd9c36..3cc058985a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/home/header/HomeRoomsHeadersController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/header/HomeRoomsHeadersController.kt @@ -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, currentFilter: HomeRoomFilter?, + analyticsTracker: AnalyticsTracker, ) { roomFilterHeaderItem { id("filter_header") filtersData(filtersList) selectedFilter(currentFilter) onFilterChangedListener(filterChangedListener) + analyticsTracker(analyticsTracker) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/header/RoomFilterHeaderItem.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/header/RoomFilterHeaderItem.kt index ed99b51681..fd4333b722 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/home/header/RoomFilterHeaderItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/header/RoomFilterHeaderItem.kt @@ -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(R.layout.item_home_filter_tabs) { @@ -35,6 +37,9 @@ abstract class RoomFilterHeaderItem : VectorEpoxyModel + trackFilterChangeEvent(filter) onFilterChangedListener?.invoke(filter) } } @@ -61,6 +67,23 @@ abstract class RoomFilterHeaderItem : VectorEpoxyModel 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) diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesFragment.kt index 0dbc1b8f34..ac39d7d567 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesFragment.kt @@ -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(), 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) diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/layout/HomeLayoutSettingBottomDialogFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/layout/HomeLayoutSettingBottomDialogFragment.kt index 0c4d64a1cc..63b7f557e3 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/home/layout/HomeLayoutSettingBottomDialogFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/layout/HomeLayoutSettingBottomDialogFragment.kt @@ -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) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/NewSpaceSummaryController.kt b/vector/src/main/java/im/vector/app/features/spaces/NewSpaceSummaryController.kt index 5061eb4036..199169484c 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/NewSpaceSummaryController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/NewSpaceSummaryController.kt @@ -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) diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceListAction.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceListAction.kt index fd2e68e172..1ef755e684 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceListAction.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceListAction.kt @@ -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() diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceListBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceListBottomSheet.kt index 4787aed8ae..9991384643 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceListBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceListBottomSheet.kt @@ -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() { @@ -32,6 +33,11 @@ class SpaceListBottomSheet : VectorBaseBottomSheetDialogFragment 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) diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryController.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryController.kt index ff8f5c38f7..acc1df5405 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryController.kt @@ -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) diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/ChooseSpaceTypeFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/create/ChooseSpaceTypeFragment.kt index 4c44bfc7a8..6c31b9e856 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/ChooseSpaceTypeFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/ChooseSpaceTypeFragment.kt @@ -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) diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt index b680f77df2..1cfac4a5fe 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt @@ -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(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 diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeAnalyticsTracker.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeAnalyticsTracker.kt index 1a490a7287..d35f1bd08c 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeAnalyticsTracker.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeAnalyticsTracker.kt @@ -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)