From 2aa8831a4077164c860954424b017296546af0fd Mon Sep 17 00:00:00 2001 From: "Wei.He" Date: Fri, 16 Feb 2024 16:37:09 +0800 Subject: [PATCH] feat(analytics): Add :core:analytics module Implement Firebase --- app/build.gradle.kts | 4 +- app/google-services.json | 86 +++++++++++++++++++ .../java/com/wei/picquest/MainActivity.kt | 7 +- build-logic/convention/build.gradle.kts | 14 +++ ...roidApplicationFirebaseConventionPlugin.kt | 38 ++++++++ build.gradle.kts | 3 + core/analytics/.gitignore | 1 + core/analytics/build.gradle.kts | 16 ++++ .../core/analytics/AnalyticsModule.kt | 13 +++ core/analytics/src/main/AndroidManifest.xml | 4 + .../picquest/core/analytics/AnalyticsEvent.kt | 42 +++++++++ .../core/analytics/AnalyticsHelper.kt | 9 ++ .../core/analytics/NoOpAnalyticsHelper.kt | 8 ++ .../core/analytics/StubAnalyticsHelper.kt | 18 ++++ .../wei/picquest/core/analytics/UiHelpers.kt | 13 +++ .../core/analytics/AnalyticsModule.kt | 26 ++++++ .../core/analytics/FirebaseAnalyticsHelper.kt | 26 ++++++ core/designsystem/build.gradle.kts | 1 + .../designsystem/ui/AnalyticsExtensions.kt | 34 ++++++++ .../contactme/contactme/ContactMeScreen.kt | 2 + .../picquest/feature/home/home/HomeScreen.kt | 2 + .../photo/photolibrary/PhotoLibraryScreen.kt | 19 +++- .../photo/photosearch/PhotoSearchScreen.kt | 2 + .../video/videolibrary/VideoLibraryScreen.kt | 3 + .../videolibrary/VideoLibraryViewModel.kt | 2 +- .../video/videosearch/VideoSearchScreen.kt | 2 + gradle/libs.versions.toml | 22 ++++- settings.gradle.kts | 1 + 28 files changed, 411 insertions(+), 7 deletions(-) create mode 100644 app/google-services.json create mode 100644 build-logic/convention/src/main/kotlin/AndroidApplicationFirebaseConventionPlugin.kt create mode 100644 core/analytics/.gitignore create mode 100644 core/analytics/build.gradle.kts create mode 100644 core/analytics/src/demo/java/com/wei/picquest/core/analytics/AnalyticsModule.kt create mode 100644 core/analytics/src/main/AndroidManifest.xml create mode 100644 core/analytics/src/main/java/com/wei/picquest/core/analytics/AnalyticsEvent.kt create mode 100644 core/analytics/src/main/java/com/wei/picquest/core/analytics/AnalyticsHelper.kt create mode 100644 core/analytics/src/main/java/com/wei/picquest/core/analytics/NoOpAnalyticsHelper.kt create mode 100644 core/analytics/src/main/java/com/wei/picquest/core/analytics/StubAnalyticsHelper.kt create mode 100644 core/analytics/src/main/java/com/wei/picquest/core/analytics/UiHelpers.kt create mode 100644 core/analytics/src/prod/java/com/wei/picquest/core/analytics/AnalyticsModule.kt create mode 100644 core/analytics/src/prod/java/com/wei/picquest/core/analytics/FirebaseAnalyticsHelper.kt create mode 100644 core/designsystem/src/main/java/com/wei/picquest/core/designsystem/ui/AnalyticsExtensions.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 677acaa..3027a76 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -3,6 +3,7 @@ import com.wei.picquest.PqBuildType plugins { alias(libs.plugins.pq.android.application) alias(libs.plugins.pq.android.application.compose) + alias(libs.plugins.pq.android.application.firebase) alias(libs.plugins.pq.android.application.flavors) alias(libs.plugins.pq.android.hilt) alias(libs.plugins.roborazzi) @@ -72,6 +73,7 @@ dependencies { implementation(projects.feature.photo) implementation(projects.feature.video) + implementation(projects.core.analytics) implementation(projects.core.common) implementation(projects.core.data) implementation(projects.core.datastore) @@ -115,4 +117,4 @@ dependencies { androidTestImplementation(libs.accompanist.testharness) androidTestImplementation(libs.androidx.navigation.testing) androidTestImplementation(libs.hilt.android.testing) -} \ No newline at end of file +} diff --git a/app/google-services.json b/app/google-services.json new file mode 100644 index 0000000..98f6830 --- /dev/null +++ b/app/google-services.json @@ -0,0 +1,86 @@ +{ + "project_info": { + "project_number": "YourProjectId", + "project_id": "abc", + "storage_bucket": "abc" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "Your:App:Id", + "android_client_info": { + "package_name": "com.wei.picquest" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "APlaceholderAPIKeyWith-ThirtyNineCharsX" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "Your:App:Id", + "android_client_info": { + "package_name": "com.wei.picquest.debug" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "APlaceholderAPIKeyWith-ThirtyNineCharsX" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "Your:App:Id", + "android_client_info": { + "package_name": "com.wei.picquest.demo" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "APlaceholderAPIKeyWith-ThirtyNineCharsX" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "Your:App:Id", + "android_client_info": { + "package_name": "com.wei.picquest.demo.debug" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "APlaceholderAPIKeyWith-ThirtyNineCharsX" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/src/main/java/com/wei/picquest/MainActivity.kt b/app/src/main/java/com/wei/picquest/MainActivity.kt index b499992..5aa84cc 100644 --- a/app/src/main/java/com/wei/picquest/MainActivity.kt +++ b/app/src/main/java/com/wei/picquest/MainActivity.kt @@ -21,6 +21,8 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.google.accompanist.adaptive.calculateDisplayFeatures +import com.wei.picquest.core.analytics.AnalyticsHelper +import com.wei.picquest.core.analytics.LocalAnalyticsHelper import com.wei.picquest.core.data.utils.NetworkMonitor import com.wei.picquest.core.designsystem.theme.PqTheme import com.wei.picquest.core.manager.SnackbarManager @@ -40,6 +42,9 @@ class MainActivity : ComponentActivity() { @Inject lateinit var networkMonitor: NetworkMonitor + @Inject + lateinit var analyticsHelper: AnalyticsHelper + private val viewModel: MainActivityViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { @@ -95,7 +100,7 @@ class MainActivity : ComponentActivity() { onDispose {} } - CompositionLocalProvider { + CompositionLocalProvider(LocalAnalyticsHelper provides analyticsHelper) { PqTheme(darkTheme = darkTheme) { PqApp( networkMonitor = networkMonitor, diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index f6df36a..88d991d 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -20,10 +20,20 @@ tasks.withType().configureEach { dependencies { compileOnly(libs.android.gradlePlugin) + compileOnly(libs.android.tools.common) + compileOnly(libs.firebase.crashlytics.gradlePlugin) + compileOnly(libs.firebase.performance.gradlePlugin) compileOnly(libs.kotlin.gradlePlugin) compileOnly(libs.ksp.gradlePlugin) } +tasks { + validatePlugins { + failOnWarning.set(true) + enableStricterValidation.set(true) + } +} + gradlePlugin { plugins { register("androidApplicationCompose") { @@ -54,6 +64,10 @@ gradlePlugin { id = "pq.android.hilt" implementationClass = "AndroidHiltConventionPlugin" } + register("androidFirebase") { + id = "pq.android.application.firebase" + implementationClass = "AndroidApplicationFirebaseConventionPlugin" + } register("androidFlavors") { id = "pq.android.application.flavors" implementationClass = "AndroidApplicationFlavorsConventionPlugin" diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationFirebaseConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationFirebaseConventionPlugin.kt new file mode 100644 index 0000000..48d84f6 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidApplicationFirebaseConventionPlugin.kt @@ -0,0 +1,38 @@ +import com.android.build.api.dsl.ApplicationExtension +import com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsExtension +import com.wei.picquest.libs +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.dependencies + +class AndroidApplicationFirebaseConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("com.google.gms.google-services") + apply("com.google.firebase.firebase-perf") + apply("com.google.firebase.crashlytics") + } + + dependencies { + val bom = libs.findLibrary("firebase-bom").get() + add("implementation", platform(bom)) + "implementation"(libs.findLibrary("firebase.analytics").get()) + "implementation"(libs.findLibrary("firebase.performance").get()) + "implementation"(libs.findLibrary("firebase.crashlytics").get()) + } + + extensions.configure { + buildTypes.configureEach { + // Disable the Crashlytics mapping file upload. This feature should only be + // enabled if a Firebase backend is available and configured in + // google-services.json. + configure { + mappingFileUploadEnabled = false + } + } + } + } + } +} diff --git a/build.gradle.kts b/build.gradle.kts index cc82128..6ec27de 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,6 +11,9 @@ plugins { alias(libs.plugins.android.library) apply false alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.navigation.safeargs.kotlin) apply false + alias(libs.plugins.firebase.crashlytics) apply false + alias(libs.plugins.firebase.perf) apply false + alias(libs.plugins.gms) apply false alias(libs.plugins.hilt) apply false alias(libs.plugins.kotlin.jvm) apply false alias(libs.plugins.kotlin.serialization) apply false diff --git a/core/analytics/.gitignore b/core/analytics/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/core/analytics/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/analytics/build.gradle.kts b/core/analytics/build.gradle.kts new file mode 100644 index 0000000..6fe20bf --- /dev/null +++ b/core/analytics/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + alias(libs.plugins.pq.android.library) + alias(libs.plugins.pq.android.library.compose) + alias(libs.plugins.pq.android.hilt) +} + +android { + namespace = "com.wei.picquest.core.analytics" +} + +dependencies { + implementation(libs.androidx.compose.runtime) + + prodImplementation(platform(libs.firebase.bom)) + prodImplementation(libs.firebase.analytics) +} diff --git a/core/analytics/src/demo/java/com/wei/picquest/core/analytics/AnalyticsModule.kt b/core/analytics/src/demo/java/com/wei/picquest/core/analytics/AnalyticsModule.kt new file mode 100644 index 0000000..dd86985 --- /dev/null +++ b/core/analytics/src/demo/java/com/wei/picquest/core/analytics/AnalyticsModule.kt @@ -0,0 +1,13 @@ +package com.wei.picquest.core.analytics + +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@Module +@InstallIn(SingletonComponent::class) +internal abstract class AnalyticsModule { + @Binds + abstract fun bindsAnalyticsHelper(analyticsHelperImpl: StubAnalyticsHelper): AnalyticsHelper +} diff --git a/core/analytics/src/main/AndroidManifest.xml b/core/analytics/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a5918e6 --- /dev/null +++ b/core/analytics/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/analytics/src/main/java/com/wei/picquest/core/analytics/AnalyticsEvent.kt b/core/analytics/src/main/java/com/wei/picquest/core/analytics/AnalyticsEvent.kt new file mode 100644 index 0000000..e745775 --- /dev/null +++ b/core/analytics/src/main/java/com/wei/picquest/core/analytics/AnalyticsEvent.kt @@ -0,0 +1,42 @@ +package com.wei.picquest.core.analytics + +/** + * Represents an analytics event. + * + * @param type - the event type. Wherever possible use one of the standard + * event `Types`, however, if there is no suitable event type already defined, a custom event can be + * defined as long as it is configured in your backend analytics system (for example, by creating a + * Firebase Analytics custom event). + * + * @param extras - list of parameters which supply additional context to the event. See `Param`. + */ +data class AnalyticsEvent( + val type: String, + val extras: List = emptyList(), +) { + // Standard analytics types. + class Types { + companion object { + const val SCREEN_VIEW = "screen_view" // (extras: SCREEN_NAME) + } + } + + /** + * A key-value pair used to supply extra context to an analytics event. + * + * @param key - the parameter key. Wherever possible use one of the standard `ParamKeys`, + * however, if no suitable key is available you can define your own as long as it is configured + * in your backend analytics system (for example, by creating a Firebase Analytics custom + * parameter). + * + * @param value - the parameter value. + */ + data class Param(val key: String, val value: String) + + // Standard parameter keys. + class ParamKeys { + companion object { + const val SCREEN_NAME = "screen_name" + } + } +} diff --git a/core/analytics/src/main/java/com/wei/picquest/core/analytics/AnalyticsHelper.kt b/core/analytics/src/main/java/com/wei/picquest/core/analytics/AnalyticsHelper.kt new file mode 100644 index 0000000..da82cda --- /dev/null +++ b/core/analytics/src/main/java/com/wei/picquest/core/analytics/AnalyticsHelper.kt @@ -0,0 +1,9 @@ +package com.wei.picquest.core.analytics + +/** + * Interface for logging analytics events. See `FirebaseAnalyticsHelper` and + * `StubAnalyticsHelper` for implementations. + */ +interface AnalyticsHelper { + fun logEvent(event: AnalyticsEvent) +} diff --git a/core/analytics/src/main/java/com/wei/picquest/core/analytics/NoOpAnalyticsHelper.kt b/core/analytics/src/main/java/com/wei/picquest/core/analytics/NoOpAnalyticsHelper.kt new file mode 100644 index 0000000..7c4de22 --- /dev/null +++ b/core/analytics/src/main/java/com/wei/picquest/core/analytics/NoOpAnalyticsHelper.kt @@ -0,0 +1,8 @@ +package com.wei.picquest.core.analytics + +/** + * Implementation of AnalyticsHelper which does nothing. Useful for tests and previews. + */ +class NoOpAnalyticsHelper : AnalyticsHelper { + override fun logEvent(event: AnalyticsEvent) = Unit +} diff --git a/core/analytics/src/main/java/com/wei/picquest/core/analytics/StubAnalyticsHelper.kt b/core/analytics/src/main/java/com/wei/picquest/core/analytics/StubAnalyticsHelper.kt new file mode 100644 index 0000000..e5198b6 --- /dev/null +++ b/core/analytics/src/main/java/com/wei/picquest/core/analytics/StubAnalyticsHelper.kt @@ -0,0 +1,18 @@ +package com.wei.picquest.core.analytics + +import timber.log.Timber +import javax.inject.Inject +import javax.inject.Singleton + +/** + * An implementation of AnalyticsHelper just writes the events to logcat. Used in builds where no + * analytics events should be sent to a backend. + */ +@Singleton +internal class StubAnalyticsHelper +@Inject +constructor() : AnalyticsHelper { + override fun logEvent(event: AnalyticsEvent) { + Timber.d("StubAnalyticsHelper, Received analytics event: $event") + } +} diff --git a/core/analytics/src/main/java/com/wei/picquest/core/analytics/UiHelpers.kt b/core/analytics/src/main/java/com/wei/picquest/core/analytics/UiHelpers.kt new file mode 100644 index 0000000..c1f4294 --- /dev/null +++ b/core/analytics/src/main/java/com/wei/picquest/core/analytics/UiHelpers.kt @@ -0,0 +1,13 @@ +package com.wei.picquest.core.analytics + +import androidx.compose.runtime.staticCompositionLocalOf + +/** + * Global key used to obtain access to the AnalyticsHelper through a CompositionLocal. + */ +val LocalAnalyticsHelper = + staticCompositionLocalOf { + // Provide a default AnalyticsHelper which does nothing. This is so that tests and previews + // do not have to provide one. For real app builds provide a different implementation. + NoOpAnalyticsHelper() + } diff --git a/core/analytics/src/prod/java/com/wei/picquest/core/analytics/AnalyticsModule.kt b/core/analytics/src/prod/java/com/wei/picquest/core/analytics/AnalyticsModule.kt new file mode 100644 index 0000000..4be0987 --- /dev/null +++ b/core/analytics/src/prod/java/com/wei/picquest/core/analytics/AnalyticsModule.kt @@ -0,0 +1,26 @@ +package com.wei.picquest.core.analytics + +import com.google.firebase.analytics.FirebaseAnalytics +import com.google.firebase.analytics.ktx.analytics +import com.google.firebase.ktx.Firebase +import dagger.Binds +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal abstract class AnalyticsModule { + @Binds + abstract fun bindsAnalyticsHelper(analyticsHelperImpl: FirebaseAnalyticsHelper): AnalyticsHelper + + companion object { + @Provides + @Singleton + fun provideFirebaseAnalytics(): FirebaseAnalytics { + return Firebase.analytics + } + } +} diff --git a/core/analytics/src/prod/java/com/wei/picquest/core/analytics/FirebaseAnalyticsHelper.kt b/core/analytics/src/prod/java/com/wei/picquest/core/analytics/FirebaseAnalyticsHelper.kt new file mode 100644 index 0000000..8d4021e --- /dev/null +++ b/core/analytics/src/prod/java/com/wei/picquest/core/analytics/FirebaseAnalyticsHelper.kt @@ -0,0 +1,26 @@ +package com.wei.picquest.core.analytics + +import com.google.firebase.analytics.FirebaseAnalytics +import com.google.firebase.analytics.ktx.logEvent +import javax.inject.Inject + +/** + * Implementation of `AnalyticsHelper` which logs events to a Firebase backend. + */ +internal class FirebaseAnalyticsHelper +@Inject +constructor( + private val firebaseAnalytics: FirebaseAnalytics, +) : AnalyticsHelper { + override fun logEvent(event: AnalyticsEvent) { + firebaseAnalytics.logEvent(event.type) { + for (extra in event.extras) { + // Truncate parameter keys and values according to firebase maximum length values. + param( + key = extra.key.take(40), + value = extra.value.take(100), + ) + } + } + } +} diff --git a/core/designsystem/build.gradle.kts b/core/designsystem/build.gradle.kts index 1d1b5e5..81c558c 100644 --- a/core/designsystem/build.gradle.kts +++ b/core/designsystem/build.gradle.kts @@ -16,6 +16,7 @@ android { dependencies { androidTestImplementation(projects.core.testing) + api(projects.core.analytics) // Material Design 3 api(libs.androidx.compose.material3) api(libs.androidx.compose.material.iconsExtended) diff --git a/core/designsystem/src/main/java/com/wei/picquest/core/designsystem/ui/AnalyticsExtensions.kt b/core/designsystem/src/main/java/com/wei/picquest/core/designsystem/ui/AnalyticsExtensions.kt new file mode 100644 index 0000000..df7d263 --- /dev/null +++ b/core/designsystem/src/main/java/com/wei/picquest/core/designsystem/ui/AnalyticsExtensions.kt @@ -0,0 +1,34 @@ +package com.wei.picquest.core.designsystem.ui + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import com.wei.picquest.core.analytics.AnalyticsEvent +import com.wei.picquest.core.analytics.AnalyticsHelper +import com.wei.picquest.core.analytics.LocalAnalyticsHelper + +/** + * Classes and functions associated with analytics events for the UI. + */ +fun AnalyticsHelper.logScreenView(screenName: String) { + logEvent( + AnalyticsEvent( + type = AnalyticsEvent.Types.SCREEN_VIEW, + extras = + listOf( + AnalyticsEvent.Param(AnalyticsEvent.ParamKeys.SCREEN_NAME, screenName), + ), + ), + ) +} + +/** + * A side-effect which records a screen view event. + */ +@Composable +fun TrackScreenViewEvent( + screenName: String, + analyticsHelper: AnalyticsHelper = LocalAnalyticsHelper.current, +) = DisposableEffect(Unit) { + analyticsHelper.logScreenView(screenName) + onDispose {} +} diff --git a/feature/contactme/src/main/java/com/wei/picquest/feature/contactme/contactme/ContactMeScreen.kt b/feature/contactme/src/main/java/com/wei/picquest/feature/contactme/contactme/ContactMeScreen.kt index 1142362..9f02ee6 100644 --- a/feature/contactme/src/main/java/com/wei/picquest/feature/contactme/contactme/ContactMeScreen.kt +++ b/feature/contactme/src/main/java/com/wei/picquest/feature/contactme/contactme/ContactMeScreen.kt @@ -55,6 +55,7 @@ import com.wei.picquest.core.designsystem.ui.DeviceLandscapePreviews import com.wei.picquest.core.designsystem.ui.DevicePortraitPreviews import com.wei.picquest.core.designsystem.ui.PqContentType import com.wei.picquest.core.designsystem.ui.PqNavigationType +import com.wei.picquest.core.designsystem.ui.TrackScreenViewEvent import com.wei.picquest.feature.contactme.R import com.wei.picquest.feature.contactme.contactme.ui.DecorativeBackgroundText import com.wei.picquest.feature.contactme.contactme.ui.ProfileProperty @@ -181,6 +182,7 @@ internal fun ContactMeScreen( } } } + TrackScreenViewEvent(screenName = "ContactMe") } @Composable diff --git a/feature/home/src/main/java/com/wei/picquest/feature/home/home/HomeScreen.kt b/feature/home/src/main/java/com/wei/picquest/feature/home/home/HomeScreen.kt index 5642ba7..3fbeb0d 100644 --- a/feature/home/src/main/java/com/wei/picquest/feature/home/home/HomeScreen.kt +++ b/feature/home/src/main/java/com/wei/picquest/feature/home/home/HomeScreen.kt @@ -26,6 +26,7 @@ import com.wei.picquest.core.designsystem.component.FunctionalityNotAvailablePop import com.wei.picquest.core.designsystem.component.ThemePreviews import com.wei.picquest.core.designsystem.theme.PqTheme import com.wei.picquest.core.designsystem.theme.SPACING_MEDIUM +import com.wei.picquest.core.designsystem.ui.TrackScreenViewEvent import com.wei.picquest.feature.home.R /** @@ -114,6 +115,7 @@ internal fun HomeScreen( } } } + TrackScreenViewEvent(screenName = "Home") } @ThemePreviews diff --git a/feature/photo/src/main/java/com/wei/picquest/feature/photo/photolibrary/PhotoLibraryScreen.kt b/feature/photo/src/main/java/com/wei/picquest/feature/photo/photolibrary/PhotoLibraryScreen.kt index e1fdd32..e0807fa 100644 --- a/feature/photo/src/main/java/com/wei/picquest/feature/photo/photolibrary/PhotoLibraryScreen.kt +++ b/feature/photo/src/main/java/com/wei/picquest/feature/photo/photolibrary/PhotoLibraryScreen.kt @@ -61,6 +61,7 @@ import com.wei.picquest.core.designsystem.theme.PqTheme import com.wei.picquest.core.designsystem.theme.SPACING_LARGE import com.wei.picquest.core.designsystem.theme.SPACING_MEDIUM import com.wei.picquest.core.designsystem.theme.SPACING_SMALL +import com.wei.picquest.core.designsystem.ui.TrackScreenViewEvent import com.wei.picquest.core.model.data.ImageDetail import com.wei.picquest.feature.photo.R import com.wei.picquest.feature.photo.photolibrary.component.LayoutSwitchWarningDialog @@ -101,14 +102,24 @@ internal fun PhotoLibraryRoute( navController: NavController, viewModel: PhotoLibraryViewModel = hiltViewModel(), ) { + val photoSearchQuery = viewModel.photoSearchQuery val lazyPagingItems = viewModel.imagesState.collectAsLazyPagingItems() val uiStates: PhotoLibraryViewState by viewModel.states.collectAsStateWithLifecycle() Surface(modifier = Modifier.fillMaxSize()) { Box { when (uiStates.layoutType) { - LayoutType.LIST -> PhotoLibraryListScreen(lazyPagingItems = lazyPagingItems) - LayoutType.GRID -> PhotoLibraryGridScreen(lazyPagingItems = lazyPagingItems) + LayoutType.LIST -> + PhotoLibraryListScreen( + photoSearchQuery = photoSearchQuery, + lazyPagingItems = lazyPagingItems, + ) + + LayoutType.GRID -> + PhotoLibraryGridScreen( + photoSearchQuery = photoSearchQuery, + lazyPagingItems = lazyPagingItems, + ) } TopBarActions( @@ -195,6 +206,7 @@ fun SwitchLayoutButton( @Composable fun PhotoLibraryListScreen( + photoSearchQuery: String = "", lazyPagingItems: LazyPagingItems, isPreview: Boolean = false, withTopSpacer: Boolean = true, @@ -221,10 +233,12 @@ fun PhotoLibraryListScreen( } } } + TrackScreenViewEvent(screenName = "PhotoLibraryList, $photoSearchQuery") } @Composable fun PhotoLibraryGridScreen( + photoSearchQuery: String = "", lazyPagingItems: LazyPagingItems, withTopSpacer: Boolean = true, withBottomSpacer: Boolean = true, @@ -260,6 +274,7 @@ fun PhotoLibraryGridScreen( } } } + TrackScreenViewEvent(screenName = "PhotoLibraryGrid, $photoSearchQuery") } @Composable diff --git a/feature/photo/src/main/java/com/wei/picquest/feature/photo/photosearch/PhotoSearchScreen.kt b/feature/photo/src/main/java/com/wei/picquest/feature/photo/photosearch/PhotoSearchScreen.kt index 9e242ca..8c0c3fe 100644 --- a/feature/photo/src/main/java/com/wei/picquest/feature/photo/photosearch/PhotoSearchScreen.kt +++ b/feature/photo/src/main/java/com/wei/picquest/feature/photo/photosearch/PhotoSearchScreen.kt @@ -55,6 +55,7 @@ import com.wei.picquest.core.designsystem.icon.PqIcons import com.wei.picquest.core.designsystem.theme.PqTheme import com.wei.picquest.core.designsystem.theme.SPACING_LARGE import com.wei.picquest.core.designsystem.theme.SPACING_SMALL +import com.wei.picquest.core.designsystem.ui.TrackScreenViewEvent import com.wei.picquest.feature.photo.R import com.wei.picquest.feature.photo.photolibrary.navigation.navigateToPhotoLibrary @@ -163,6 +164,7 @@ internal fun PhotoSearchScreen( } } } + TrackScreenViewEvent(screenName = "PhotoSearch") } @Composable diff --git a/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/VideoLibraryScreen.kt b/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/VideoLibraryScreen.kt index ef72226..e93d854 100644 --- a/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/VideoLibraryScreen.kt +++ b/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/VideoLibraryScreen.kt @@ -33,6 +33,7 @@ import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems import com.wei.picquest.core.designsystem.component.ThemePreviews import com.wei.picquest.core.designsystem.theme.PqTheme +import com.wei.picquest.core.designsystem.ui.TrackScreenViewEvent import com.wei.picquest.core.model.data.VideoDetail import com.wei.picquest.core.model.data.VideoDetailSize import com.wei.picquest.core.model.data.VideoStreams @@ -80,6 +81,7 @@ internal fun VideoLibraryRoute( navController: NavController, viewModel: VideoLibraryViewModel = hiltViewModel(), ) { + val videoSearchQuery = viewModel.videoSearchQuery val lazyPagingItems = viewModel.videosState.collectAsLazyPagingItems() val isInPiPMode = LocalContext.current.isInPictureInPictureMode @@ -110,6 +112,7 @@ internal fun VideoLibraryRoute( exoPlayer = exoPlayer, isPlayerReady = isPlayerReady, ) + TrackScreenViewEvent(screenName = "VideoLibrary, $videoSearchQuery") } } diff --git a/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/VideoLibraryViewModel.kt b/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/VideoLibraryViewModel.kt index 26e2c74..4a55bdf 100644 --- a/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/VideoLibraryViewModel.kt +++ b/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/VideoLibraryViewModel.kt @@ -25,7 +25,7 @@ class VideoLibraryViewModel @Inject constructor( private val videoLibraryArgs: VideoLibraryArgs = VideoLibraryArgs(savedStateHandle) - private val videoSearchQuery = videoLibraryArgs.videoSearchQuery + val videoSearchQuery = videoLibraryArgs.videoSearchQuery private val _videosState: MutableStateFlow> = MutableStateFlow(value = PagingData.empty()) diff --git a/feature/video/src/main/java/com/wei/picquest/feature/video/videosearch/VideoSearchScreen.kt b/feature/video/src/main/java/com/wei/picquest/feature/video/videosearch/VideoSearchScreen.kt index a4899ce..8c9c631 100644 --- a/feature/video/src/main/java/com/wei/picquest/feature/video/videosearch/VideoSearchScreen.kt +++ b/feature/video/src/main/java/com/wei/picquest/feature/video/videosearch/VideoSearchScreen.kt @@ -55,6 +55,7 @@ import com.wei.picquest.core.designsystem.icon.PqIcons import com.wei.picquest.core.designsystem.theme.PqTheme import com.wei.picquest.core.designsystem.theme.SPACING_LARGE import com.wei.picquest.core.designsystem.theme.SPACING_SMALL +import com.wei.picquest.core.designsystem.ui.TrackScreenViewEvent import com.wei.picquest.feature.video.R import com.wei.picquest.feature.video.videolibrary.navigation.navigateToVideoLibrary @@ -163,6 +164,7 @@ internal fun VideoSearchScreen( } } } + TrackScreenViewEvent(screenName = "VideoSearch") } @Composable diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 798f7b3..2d43016 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -20,6 +20,11 @@ androidxActivity = "1.9.0-alpha01" androidxBrowser = "1.7.0" androidx-window = "1.3.0-alpha01" androidGradlePlugin = "8.1.0-rc01" +androidTools = "31.2.0" +firebaseBom = "32.4.0" +firebaseCrashlyticsPlugin = "2.9.9" +firebasePerfPlugin = "1.4.2" +gmsPlugin = "4.4.0" kotlin = "1.9.10" navigationSafeArgsKotlin = "2.7.7" androidxComposeCompiler = "1.5.3" @@ -35,7 +40,6 @@ coil = "2.5.0" accompanist = "0.32.0" ksp = "1.9.10-1.0.13" secrets = "2.0.1" -lint = "31.2.0" robolectric = "4.11.1" roborazzi = "1.9.0" paging = "3.2.1" @@ -158,7 +162,14 @@ accompanist-adaptive = { module = "com.google.accompanist:accompanist-adaptive", accompanist-testharness = { group = "com.google.accompanist", name = "accompanist-testharness", version.ref = "accompanist" } # lint -lint-api = { group = "com.android.tools.lint", name = "lint-api", version.ref = "lint" } +lint-api = { group = "com.android.tools.lint", name = "lint-api", version.ref = "androidTools" } + +# firebase +firebase-analytics = { group = "com.google.firebase", name = "firebase-analytics-ktx" } +firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebaseBom" } +firebase-cloud-messaging = { group = "com.google.firebase", name = "firebase-messaging-ktx" } +firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics-ktx" } +firebase-performance = { group = "com.google.firebase", name = "firebase-perf-ktx" } # robolectric - Android Unit Testing Framework robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" } @@ -182,6 +193,9 @@ leakcanary = { group = "com.squareup.leakcanary", name = "leakcanary-android", v # Dependencies of the included build-logic android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } +android-tools-common = { group = "com.android.tools", name = "common", version.ref = "androidTools" } +firebase-crashlytics-gradlePlugin = { group = "com.google.firebase", name = "firebase-crashlytics-gradle", version.ref = "firebaseCrashlyticsPlugin" } +firebase-performance-gradlePlugin = { group = "com.google.firebase", name = "perf-plugin", version.ref = "firebasePerfPlugin" } kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } ksp-gradlePlugin = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } @@ -189,6 +203,9 @@ ksp-gradlePlugin = { group = "com.google.devtools.ksp", name = "com.google.devto android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" } android-test = { id = "com.android.test", version.ref = "androidGradlePlugin" } +firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebaseCrashlyticsPlugin" } +firebase-perf = { id = "com.google.firebase.firebase-perf", version.ref = "firebasePerfPlugin" } +gms = { id = "com.google.gms.google-services", version.ref = "gmsPlugin" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } navigation-safeargs-kotlin = { id = "androidx.navigation.safeargs.kotlin", version.ref = "navigationSafeArgsKotlin" } hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } @@ -203,6 +220,7 @@ roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" } # Plugins defined by this project pq-android-application = { id = "pq.android.application", version = "unspecified" } pq-android-application-compose = { id = "pq.android.application.compose", version = "unspecified" } +pq-android-application-firebase = { id = "pq.android.application.firebase", version = "unspecified" } pq-android-application-flavors = { id = "pq.android.application.flavors", version = "unspecified" } pq-android-feature = { id = "pq.android.feature", version = "unspecified" } pq-android-hilt = { id = "pq.android.hilt", version = "unspecified" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 3736e5d..5f1727f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,6 +19,7 @@ rootProject.name = "picquest" enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") include(":app") +include(":core:analytics") include(":core:designsystem") include(":core:testing") include(":core:common")