diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/troubleshoot/TroubleshootNotificationsNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/troubleshoot/TroubleshootNotificationsNode.kt index 1ac7c0c079..833f512c81 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/troubleshoot/TroubleshootNotificationsNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/troubleshoot/TroubleshootNotificationsNode.kt @@ -23,17 +23,21 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import dagger.assisted.Assisted import dagger.assisted.AssistedInject +import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.anvilannotations.ContributesNode import io.element.android.libraries.di.SessionScope +import io.element.android.services.analytics.api.ScreenTracker @ContributesNode(SessionScope::class) class TroubleshootNotificationsNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, private val presenter: TroubleshootNotificationsPresenter, + private val screenTracker: ScreenTracker, ) : Node(buildContext, plugins = plugins) { @Composable override fun View(modifier: Modifier) { + screenTracker.TrackScreen(this, MobileScreen.ScreenName.NotificationTroubleshoot) val state = presenter.present() TroubleshootNotificationsView( state = state, diff --git a/services/analytics/api/build.gradle.kts b/services/analytics/api/build.gradle.kts index 28b871a659..361ad09162 100644 --- a/services/analytics/api/build.gradle.kts +++ b/services/analytics/api/build.gradle.kts @@ -14,7 +14,7 @@ * limitations under the License. */ plugins { - id("io.element.android-library") + id("io.element.android-compose-library") } android { @@ -23,6 +23,8 @@ android { dependencies { api(projects.services.analyticsproviders.api) + api(projects.services.toolbox.api) + api(libs.appyx.core) implementation(libs.coroutines.core) implementation(projects.libraries.matrix.api) implementation(projects.libraries.core) diff --git a/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/ScreenTracker.kt b/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/ScreenTracker.kt new file mode 100644 index 0000000000..b2f1e3ab6d --- /dev/null +++ b/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/ScreenTracker.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 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 io.element.android.services.analytics.api + +import androidx.compose.runtime.Composable +import com.bumble.appyx.core.node.Node +import im.vector.app.features.analytics.plan.MobileScreen + +interface ScreenTracker { + @Composable + fun TrackScreen( + node: Node, + screen: MobileScreen.ScreenName, + ) +} diff --git a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultScreenTracker.kt b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultScreenTracker.kt new file mode 100644 index 0000000000..970fcd0c95 --- /dev/null +++ b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultScreenTracker.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024 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 io.element.android.services.analytics.impl + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import com.bumble.appyx.core.lifecycle.subscribe +import com.bumble.appyx.core.node.Node +import com.squareup.anvil.annotations.ContributesBinding +import im.vector.app.features.analytics.plan.MobileScreen +import io.element.android.libraries.di.AppScope +import io.element.android.services.analytics.api.AnalyticsService +import io.element.android.services.analytics.api.ScreenTracker +import io.element.android.services.toolbox.api.systemclock.SystemClock +import javax.inject.Inject + +@ContributesBinding(AppScope::class) +class DefaultScreenTracker @Inject constructor( + private val analyticsService: AnalyticsService, + private val systemClock: SystemClock +) : ScreenTracker { + @Composable + override fun TrackScreen( + node: Node, + screen: MobileScreen.ScreenName, + ) { + LaunchedEffect(Unit) { + var startTime = 0L + node.lifecycle.subscribe( + onResume = { + startTime = systemClock.epochMillis() + }, + onPause = { + analyticsService.screen( + screen = MobileScreen( + durationMs = (systemClock.epochMillis() - startTime).toInt(), + screenName = screen + ) + ) + } + ) + } + } +}