From e59f2bba0a05b6962c8ebc40261af993331a2283 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Mon, 28 Feb 2022 17:13:06 +0200 Subject: [PATCH] Add analytics to threads --- .../session/room/timeline/TimelineEvent.kt | 8 ++ .../analytics/extensions/ComposerExt.kt | 28 ++++++ .../analytics/extensions/InteractionExt.kt | 24 +++++ .../app/features/analytics/plan/Composer.kt | 5 + .../features/analytics/plan/Interaction.kt | 95 +++++++++++++++++-- .../features/analytics/plan/MobileScreen.kt | 5 + .../home/room/detail/TimelineFragment.kt | 13 +-- .../composer/MessageComposerViewModel.kt | 4 + .../composer/MessageComposerViewState.kt | 3 + .../timeline/action/EventSharedAction.kt | 2 +- .../action/MessageActionsViewModel.kt | 3 +- .../home/room/threads/ThreadsActivity.kt | 3 + .../threads/arguments/ThreadTimelineArgs.kt | 3 +- .../list/viewmodel/ThreadListViewModel.kt | 7 +- .../threads/list/views/ThreadListFragment.kt | 2 + 15 files changed, 189 insertions(+), 16 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/analytics/extensions/ComposerExt.kt create mode 100644 vector/src/main/java/im/vector/app/features/analytics/extensions/InteractionExt.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt index c03d0fd17b..d7796c8808 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.api.session.room.timeline import org.matrix.android.sdk.BuildConfig +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.RelationType @@ -159,6 +160,13 @@ fun TimelineEvent.isSticker(): Boolean { return root.isSticker() } +/** + * Returns whether or not the event is a root thread event + */ +fun TimelineEvent.isRootThread(): Boolean { + return root.threadDetails?.isRootThread.orFalse() +} + /** * Get the latest message body, after a possible edition, stripping the reply prefix if necessary */ diff --git a/vector/src/main/java/im/vector/app/features/analytics/extensions/ComposerExt.kt b/vector/src/main/java/im/vector/app/features/analytics/extensions/ComposerExt.kt new file mode 100644 index 0000000000..80675ac57c --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/analytics/extensions/ComposerExt.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022 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.Composer +import im.vector.app.features.home.room.detail.composer.MessageComposerViewState +import im.vector.app.features.home.room.detail.composer.SendMode + +fun MessageComposerViewState.toAnalyticsComposer(): Composer = + Composer( + inThread = isInThreadTimeline(), + isEditing = sendMode is SendMode.Edit, + isReply = sendMode is SendMode.Reply, + startsThread = startsThread) diff --git a/vector/src/main/java/im/vector/app/features/analytics/extensions/InteractionExt.kt b/vector/src/main/java/im/vector/app/features/analytics/extensions/InteractionExt.kt new file mode 100644 index 0000000000..c46230cdd1 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/analytics/extensions/InteractionExt.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022 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.Interaction + +fun Interaction.Name.toAnalyticsInteraction(interactionType: Interaction.InteractionType = Interaction.InteractionType.Touch) = + Interaction( + name = this, + interactionType = interactionType) diff --git a/vector/src/main/java/im/vector/app/features/analytics/plan/Composer.kt b/vector/src/main/java/im/vector/app/features/analytics/plan/Composer.kt index a3b847a1bd..79be8aae2b 100644 --- a/vector/src/main/java/im/vector/app/features/analytics/plan/Composer.kt +++ b/vector/src/main/java/im/vector/app/features/analytics/plan/Composer.kt @@ -39,6 +39,10 @@ data class Composer( * sent event. */ val isReply: Boolean, + /** + * Whether this message begins a new thread or not. + */ + val startsThread: Boolean? = null, ) : VectorAnalyticsEvent { override fun getName() = "Composer" @@ -48,6 +52,7 @@ data class Composer( put("inThread", inThread) put("isEditing", isEditing) put("isReply", isReply) + startsThread?.let { put("startsThread", it) } }.takeIf { it.isNotEmpty() } } } diff --git a/vector/src/main/java/im/vector/app/features/analytics/plan/Interaction.kt b/vector/src/main/java/im/vector/app/features/analytics/plan/Interaction.kt index 7bdc7740e1..2007f75fbc 100644 --- a/vector/src/main/java/im/vector/app/features/analytics/plan/Interaction.kt +++ b/vector/src/main/java/im/vector/app/features/analytics/plan/Interaction.kt @@ -40,6 +40,36 @@ data class Interaction( ) : VectorAnalyticsEvent { enum class Name { + /** + * User tapped on Add to Home button on Room Details screen. + */ + MobileRoomAddHome, + + /** + * User tapped on Leave Room button on Room Details screen. + */ + MobileRoomLeave, + + /** + * User tapped on Threads button on Room screen. + */ + MobileRoomThreadListButton, + + /** + * User tapped on a thread summary item on Room screen. + */ + MobileRoomThreadSummaryItem, + + /** + * User tapped on the filter button on ThreadList screen. + */ + MobileThreadListFilterItem, + + /** + * User selected a thread on ThreadList screen. + */ + MobileThreadListThreadItem, + /** * User tapped the already selected space from the space list. */ @@ -52,8 +82,8 @@ data class Interaction( SpacePanelSwitchSpace, /** - * User clicked the create room button in the + context menu of the room - * list header in Element Web/Desktop. + * User clicked the create room button in the add existing room to space + * dialog in Element Web/Desktop. */ WebAddExistingToSpaceDialogCreateRoomButton, @@ -105,12 +135,24 @@ data class Interaction( */ WebRightPanelRoomUserInfoInviteButton, + /** + * User clicked the threads 'show' filter dropdown in the threads panel + * in Element Web/Desktop. + */ + WebRightPanelThreadPanelFilterDropdown, + /** * User clicked the create room button in the room directory of Element * Web/Desktop. */ WebRoomDirectoryCreateRoomButton, + /** + * User clicked the Threads button in the top right of a room in Element + * Web/Desktop. + */ + WebRoomHeaderButtonsThreadsButton, + /** * User adjusted their favourites using the context menu on the header * of a room in Element Web/Desktop. @@ -153,6 +195,12 @@ data class Interaction( */ WebRoomListHeaderPlusMenuCreateRoomItem, + /** + * User clicked the explore rooms button in the + context menu of the + * room list header in Element Web/Desktop. + */ + WebRoomListHeaderPlusMenuExploreRoomsItem, + /** * User adjusted their favourites using the context menu on a room tile * in the room list in Element Web/Desktop. @@ -189,6 +237,12 @@ data class Interaction( */ WebRoomListRoomsSublistPlusMenuCreateRoomItem, + /** + * User clicked the explore rooms button in the + context menu of the + * rooms sublist in Element Web/Desktop. + */ + WebRoomListRoomsSublistPlusMenuExploreRoomsItem, + /** * User interacted with leave action in the general tab of the room * settings dialog in Element Web/Desktop. @@ -201,6 +255,12 @@ data class Interaction( */ WebRoomSettingsSecurityTabCreateNewRoomButton, + /** + * User clicked a thread summary in the timeline of a room in Element + * Web/Desktop. + */ + WebRoomTimelineThreadSummaryButton, + /** * User interacted with the theme radio selector in the Appearance tab * of Settings in Element Web/Desktop. @@ -214,17 +274,40 @@ data class Interaction( WebSettingsSidebarTabSpacesCheckbox, /** - * User clicked the create room button in the + context menu of the room - * list header in Element Web/Desktop. + * User clicked the explore rooms button in the context menu of a space + * in Element Web/Desktop. + */ + WebSpaceContextMenuExploreRoomsItem, + + /** + * User clicked the home button in the context menu of a space in + * Element Web/Desktop. + */ + WebSpaceContextMenuHomeItem, + + /** + * User clicked the new room button in the context menu of a space in + * Element Web/Desktop. */ WebSpaceContextMenuNewRoomItem, /** - * User clicked the create room button in the + context menu of the room - * list header in Element Web/Desktop. + * User clicked the new room button in the context menu on the space + * home in Element Web/Desktop. */ WebSpaceHomeCreateRoomButton, + /** + * User clicked the back button on a Thread view going back to the + * Threads Panel of Element Web/Desktop. + */ + WebThreadViewBackButton, + + /** + * User selected a thread in the Threads panel in Element Web/Desktop. + */ + WebThreadsPanelThreadItem, + /** * User clicked the theme toggle button in the user menu of Element * Web/Desktop. diff --git a/vector/src/main/java/im/vector/app/features/analytics/plan/MobileScreen.kt b/vector/src/main/java/im/vector/app/features/analytics/plan/MobileScreen.kt index 758a0540bf..33976cb4cc 100644 --- a/vector/src/main/java/im/vector/app/features/analytics/plan/MobileScreen.kt +++ b/vector/src/main/java/im/vector/app/features/analytics/plan/MobileScreen.kt @@ -225,6 +225,11 @@ data class MobileScreen( */ SwitchDirectory, + /** + * Screen that displays list of threads for a room + */ + ThreadList, + /** * A screen that shows information about a room member. */ diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index 1a40018526..67fb595378 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -119,7 +119,8 @@ 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.FragmentTimelineBinding -import im.vector.app.features.analytics.plan.Composer +import im.vector.app.features.analytics.extensions.toAnalyticsInteraction +import im.vector.app.features.analytics.plan.Interaction import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.attachments.AttachmentTypeSelectorView import im.vector.app.features.attachments.AttachmentsHelper @@ -1505,9 +1506,6 @@ class TimelineFragment @Inject constructor( return } if (text.isNotBlank()) { - withState(messageComposerViewModel) { state -> - analyticsTracker.capture(Composer(isThreadTimeLine(), isEditing = state.sendMode is SendMode.Edit, isReply = state.sendMode is SendMode.Reply)) - } // We collapse ASAP, if not there will be a slight annoying delay views.composerLayout.collapse(true) lockSendButton = true @@ -2204,7 +2202,7 @@ class TimelineFragment @Inject constructor( } is EventSharedAction.ReplyInThread -> { if (withState(messageComposerViewModel) { it.isVoiceMessageIdle }) { - navigateToThreadTimeline(action.eventId) + navigateToThreadTimeline(action.eventId, action.startsThread) } else { requireActivity().toast(R.string.error_voice_message_cannot_reply_or_edit) } @@ -2363,9 +2361,11 @@ class TimelineFragment @Inject constructor( * using the ThreadsActivity */ - private fun navigateToThreadTimeline(rootThreadEventId: String) { + private fun navigateToThreadTimeline(rootThreadEventId: String, startsThread: Boolean = false) { + analyticsTracker.capture(Interaction.Name.MobileRoomThreadSummaryItem.toAnalyticsInteraction()) context?.let { val roomThreadDetailArgs = ThreadTimelineArgs( + startsThread = startsThread, roomId = timelineArgs.roomId, displayName = timelineViewModel.getRoomSummary()?.displayName, avatarUrl = timelineViewModel.getRoomSummary()?.avatarUrl, @@ -2381,6 +2381,7 @@ class TimelineFragment @Inject constructor( */ private fun navigateToThreadList() { + analyticsTracker.capture(Interaction.Name.MobileRoomThreadListButton.toAnalyticsInteraction()) context?.let { val roomThreadDetailArgs = ThreadTimelineArgs( roomId = timelineArgs.roomId, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt index 6adf248af9..a07d01fed5 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt @@ -27,6 +27,7 @@ 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.features.analytics.AnalyticsTracker +import im.vector.app.features.analytics.extensions.toAnalyticsComposer import im.vector.app.features.analytics.extensions.toAnalyticsJoinedRoom import im.vector.app.features.attachments.toContentAttachmentData import im.vector.app.features.command.CommandParser @@ -188,6 +189,9 @@ class MessageComposerViewModel @AssistedInject constructor( private fun handleSendMessage(action: MessageComposerAction.SendMessage) { withState { state -> + analyticsTracker.capture(state.toAnalyticsComposer()).also { + setState { copy(startsThread = false) } + } when (state.sendMode) { is SendMode.Regular -> { when (val slashCommandResult = commandParser.parseSlashCommand( diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt index f90f3975c6..95553eb1cd 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt @@ -19,6 +19,7 @@ package im.vector.app.features.home.room.detail.composer import com.airbnb.mvrx.MavericksState import im.vector.app.features.home.room.detail.arguments.TimelineArgs import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent /** @@ -62,6 +63,7 @@ data class MessageComposerViewState( val canSendMessage: CanSendStatus = CanSendStatus.Allowed, val isSendButtonVisible: Boolean = false, val rootThreadEventId: String? = null, + val startsThread: Boolean = false, val sendMode: SendMode = SendMode.Regular("", false), val voiceRecordingUiState: VoiceMessageRecorderView.RecordingUiState = VoiceMessageRecorderView.RecordingUiState.Idle ) : MavericksState { @@ -80,6 +82,7 @@ data class MessageComposerViewState( constructor(args: TimelineArgs) : this( roomId = args.roomId, + startsThread = args.threadTimelineArgs?.startsThread.orFalse(), rootThreadEventId = args.threadTimelineArgs?.rootThreadEventId) fun isInThreadTimeline(): Boolean = rootThreadEventId != null diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt index 048a4754f5..5f12c2f174 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt @@ -48,7 +48,7 @@ sealed class EventSharedAction(@StringRes val titleRes: Int, data class Reply(val eventId: String) : EventSharedAction(R.string.reply, R.drawable.ic_reply) - data class ReplyInThread(val eventId: String) : + data class ReplyInThread(val eventId: String, val startsThread: Boolean) : EventSharedAction(R.string.reply_in_thread, R.drawable.ic_reply_in_thread) object ViewInRoom : diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index 745cb0c731..20cf0b46fd 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -61,6 +61,7 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent import org.matrix.android.sdk.api.session.room.timeline.hasBeenEdited import org.matrix.android.sdk.api.session.room.timeline.isPoll +import org.matrix.android.sdk.api.session.room.timeline.isRootThread import org.matrix.android.sdk.api.session.room.timeline.isSticker import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.unwrap @@ -328,7 +329,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted } if (canReplyInThread(timelineEvent, messageContent, actionPermissions)) { - add(EventSharedAction.ReplyInThread(eventId)) + add(EventSharedAction.ReplyInThread(eventId, !timelineEvent.isRootThread())) } if (canViewInRoom(timelineEvent, messageContent, actionPermissions)) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/threads/ThreadsActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/threads/ThreadsActivity.kt index fc76535c4c..726138ed93 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/threads/ThreadsActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/threads/ThreadsActivity.kt @@ -26,6 +26,8 @@ import im.vector.app.core.extensions.addFragmentToBackstack import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivityThreadsBinding +import im.vector.app.features.analytics.extensions.toAnalyticsInteraction +import im.vector.app.features.analytics.plan.Interaction import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.TimelineFragment import im.vector.app.features.home.room.detail.arguments.TimelineArgs @@ -92,6 +94,7 @@ class ThreadsActivity : VectorBaseActivity() { * One usage of that is from the Threads Activity */ fun navigateToThreadTimeline(threadTimelineArgs: ThreadTimelineArgs) { + analyticsTracker.capture(Interaction.Name.MobileThreadListThreadItem.toAnalyticsInteraction()) val commonOption: (FragmentTransaction) -> Unit = { it.setCustomAnimations( R.anim.animation_slide_in_right, diff --git a/vector/src/main/java/im/vector/app/features/home/room/threads/arguments/ThreadTimelineArgs.kt b/vector/src/main/java/im/vector/app/features/home/room/threads/arguments/ThreadTimelineArgs.kt index aadad3d97c..d3a80811ea 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/threads/arguments/ThreadTimelineArgs.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/threads/arguments/ThreadTimelineArgs.kt @@ -26,5 +26,6 @@ data class ThreadTimelineArgs( val displayName: String?, val avatarUrl: String?, val roomEncryptionTrustLevel: RoomEncryptionTrustLevel?, - val rootThreadEventId: String? = null + val rootThreadEventId: String? = null, + val startsThread: Boolean = false ) : Parcelable diff --git a/vector/src/main/java/im/vector/app/features/home/room/threads/list/viewmodel/ThreadListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/threads/list/viewmodel/ThreadListViewModel.kt index d68e0a3248..8da9d83e10 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/threads/list/viewmodel/ThreadListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/threads/list/viewmodel/ThreadListViewModel.kt @@ -25,6 +25,9 @@ import dagger.assisted.AssistedInject import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel +import im.vector.app.features.analytics.AnalyticsTracker +import im.vector.app.features.analytics.extensions.toAnalyticsInteraction +import im.vector.app.features.analytics.plan.Interaction import im.vector.app.features.home.room.threads.list.views.ThreadListFragment import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map @@ -34,6 +37,7 @@ import org.matrix.android.sdk.api.session.threads.ThreadTimelineEvent import org.matrix.android.sdk.flow.flow class ThreadListViewModel @AssistedInject constructor(@Assisted val initialState: ThreadListViewState, + private val analyticsTracker: AnalyticsTracker, private val session: Session) : VectorViewModel(initialState) { @@ -113,9 +117,10 @@ class ThreadListViewModel @AssistedInject constructor(@Assisted val initialState } } - fun canHomeserverUseThreading() = session.getHomeServerCapabilities().canUseThreading + fun canHomeserverUseThreading() = session.getHomeServerCapabilities().canUseThreading fun applyFiltering(shouldFilterThreads: Boolean) { + analyticsTracker.capture(Interaction.Name.MobileThreadListFilterItem.toAnalyticsInteraction()) setState { copy(shouldFilterThreads = shouldFilterThreads) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/threads/list/views/ThreadListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/threads/list/views/ThreadListFragment.kt index 949778629b..d5659efa49 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/threads/list/views/ThreadListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/threads/list/views/ThreadListFragment.kt @@ -30,6 +30,7 @@ import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentThreadListBinding +import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.timeline.animation.TimelineItemAnimator import im.vector.app.features.home.room.threads.ThreadsActivity @@ -62,6 +63,7 @@ class ThreadListFragment @Inject constructor( override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + analyticsScreenName = MobileScreen.ScreenName.ThreadList } override fun onOptionsItemSelected(item: MenuItem): Boolean {