From 24d28925471bb5cb8bdb53b424395b5b67c75959 Mon Sep 17 00:00:00 2001 From: Cristhian Escobar Date: Sun, 18 Aug 2024 00:15:23 -0500 Subject: [PATCH] Analytics Tracker --- .../java/io/newm/DebugNewmApplication.kt | 6 - .../app-newm/src/main/java/io/newm/Logout.kt | 17 +- .../src/main/java/io/newm/NewmApplication.kt | 46 +++--- .../java/io/newm/di/android/Dependencies.kt | 9 +- .../utils/AndroidNewmAppAnalyticsTracker.kt | 4 +- .../io/newm/utils/AndroidNewmAppLogger.kt | 2 +- .../utils/AppForegroundBackgroundTracker.kt | 54 +++++++ .../CreateAccountScreenPresenter.kt | 13 +- .../screen/login/LoginScreenPresenter.kt | 11 +- .../ResetPasswordScreenPresenter.kt | 149 +++++++++++------- .../screen/welcome/WelcomeScreenPresenter.kt | 20 ++- .../kotlin/io.newm.shared/di/Koin.kt | 2 +- .../analytics}/AppAnalyticsTracker.kt | 2 +- .../analytics}/NewmAppAnalyticsTracker.kt | 2 +- 14 files changed, 226 insertions(+), 111 deletions(-) create mode 100644 android/app-newm/src/main/java/io/newm/utils/AppForegroundBackgroundTracker.kt rename shared/src/commonMain/kotlin/io.newm.shared/{ => public/analytics}/AppAnalyticsTracker.kt (98%) rename shared/src/commonMain/kotlin/io.newm.shared/{ => public/analytics}/NewmAppAnalyticsTracker.kt (97%) diff --git a/android/app-newm/src/debug/java/io/newm/DebugNewmApplication.kt b/android/app-newm/src/debug/java/io/newm/DebugNewmApplication.kt index 16d82447..2ba3c3a9 100644 --- a/android/app-newm/src/debug/java/io/newm/DebugNewmApplication.kt +++ b/android/app-newm/src/debug/java/io/newm/DebugNewmApplication.kt @@ -22,12 +22,6 @@ class DebugNewmApplication : NewmApplication() { addPlugin(CrashReporterPlugin.getInstance()) addPlugin(DatabasesFlipperPlugin(this@DebugNewmApplication)) addPlugin(NavigationFlipperPlugin.getInstance()) - addPlugin( - SharedPreferencesFlipperPlugin( - this@DebugNewmApplication, - "newm_encrypted_shared_prefs" - ) - ) }.start() super.onCreate() diff --git a/android/app-newm/src/main/java/io/newm/Logout.kt b/android/app-newm/src/main/java/io/newm/Logout.kt index 07ca1a32..0d326107 100644 --- a/android/app-newm/src/main/java/io/newm/Logout.kt +++ b/android/app-newm/src/main/java/io/newm/Logout.kt @@ -9,7 +9,6 @@ import io.newm.shared.public.usecases.LoginUseCase import io.newm.shared.public.usecases.UserSessionUseCase import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent @@ -21,18 +20,20 @@ class Logout( private val logger: NewmAppLogger ) { - fun signOutUser() = try { + fun signOutUser() { scope.launch { - loginUseCase.logout() + try { + loginUseCase.logout() + restartApp.run() + logger.info("Logout", "Logout successful") + } catch (e: Exception) { + logger.error("Logout", "Logout failed", e) + } } - restartApp.run() - logger.info("Logout", "Logout successful") - } catch (e: Exception) { - logger.error("Logout", "Logout failed", e) } fun register() { - GlobalScope.launch { + scope.launch { userSessionUseCase.isLoggedInFlow().collect { isLoggedIn -> if (!isLoggedIn) { logger.debug("Logout", "User is not logged in") diff --git a/android/app-newm/src/main/java/io/newm/NewmApplication.kt b/android/app-newm/src/main/java/io/newm/NewmApplication.kt index 467728e1..da2d6849 100644 --- a/android/app-newm/src/main/java/io/newm/NewmApplication.kt +++ b/android/app-newm/src/main/java/io/newm/NewmApplication.kt @@ -1,21 +1,22 @@ package io.newm import android.app.Application -import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.viewModelScope import coil.ImageLoader import coil.ImageLoaderFactory import com.google.android.recaptcha.Recaptcha +import com.google.firebase.FirebaseApp import io.newm.BuildConfig.* import io.newm.di.android.androidModules import io.newm.di.android.viewModule import io.newm.feature.login.screen.authproviders.RecaptchaClientProvider -import io.newm.shared.NewmAppAnalyticsTracker import io.newm.shared.NewmAppLogger import io.newm.shared.config.NewmSharedBuildConfig import io.newm.shared.di.initKoin -import io.newm.shared.public.usecases.ForceAppUpdateUseCase +import io.newm.shared.public.analytics.NewmAppAnalyticsTracker import io.newm.utils.AndroidNewmAppAnalyticsTracker import io.newm.utils.AndroidNewmAppLogger +import io.newm.utils.AppForegroundBackgroundTracker import io.newm.utils.ForceAppUpdateViewModel import io.newm.utils.NewmImageLoaderFactory import io.sentry.Hint @@ -24,41 +25,37 @@ import io.sentry.SentryLevel import io.sentry.SentryOptions import io.sentry.android.core.SentryAndroid import io.sentry.android.core.SentryAndroidOptions -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.koin.android.ext.android.inject import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidLogger import org.koin.core.logger.Level - open class NewmApplication : Application(), ImageLoaderFactory { - private val logout: Logout by inject() - private val logger: NewmAppLogger by inject() - private val buildConfig: NewmSharedBuildConfig by inject() - private val recaptchaClientProvider: RecaptchaClientProvider by inject() private val analyticsTracker: NewmAppAnalyticsTracker by inject() - private val imageLoaderFactory by lazy { NewmImageLoaderFactory(this) } private val config: NewmSharedBuildConfig by inject() private val forceAppUpdateViewModel: ForceAppUpdateViewModel by inject() + private val imageLoaderFactory by lazy { NewmImageLoaderFactory(this@NewmApplication) } + private val logger: NewmAppLogger by inject() + private val logout: Logout by inject() + private val recaptchaClientProvider: RecaptchaClientProvider by inject() override fun onCreate() { super.onCreate() initKoin() + FirebaseApp.initializeApp(this) logout.register() bindClientImplementations() initializeRecaptchaClient() } private fun initializeRecaptchaClient() { - CoroutineScope(Dispatchers.Default).launch { - Recaptcha.getClient(this@NewmApplication, buildConfig.recaptchaSiteKey, timeout = 50000L) + forceAppUpdateViewModel.viewModelScope.launch { + Recaptcha.getClient(this@NewmApplication, config.recaptchaSiteKey, timeout = 50000L) .onSuccess { client -> recaptchaClientProvider.setRecaptchaClient(client) forceAppUpdateViewModel.checkForUpdates(currentVersion = BuildConfig.VERSION_NAME) - }.onFailure { exception -> logger.error( tag = "RecaptchaClient", @@ -82,22 +79,25 @@ open class NewmApplication : Application(), ImageLoaderFactory { } private fun bindClientImplementations() { - SentryAndroid.init( - this - ) { options: SentryAndroidOptions -> + setupSentry() + setupLogger() + registerActivityLifecycleCallbacks(AppForegroundBackgroundTracker(analyticsTracker, logger)) + } + + private fun setupSentry() { + SentryAndroid.init(this) { options: SentryAndroidOptions -> options.dsn = config.androidSentryDSN options.environment = if (DEBUG) "development" else "production" options.beforeSend = SentryOptions.BeforeSendCallback { event: SentryEvent, hint: Hint -> - if (SentryLevel.DEBUG == event.level) { - null - } else { - event - } + if (SentryLevel.DEBUG == event.level) null else event } } - analyticsTracker.setClientAnalyticsTracker(AndroidNewmAppAnalyticsTracker(logger)) + } + + private fun setupLogger() { logger.setClientLogger(AndroidNewmAppLogger(analyticsTracker)) + analyticsTracker.setClientAnalyticsTracker(AndroidNewmAppAnalyticsTracker(logger)) } override fun newImageLoader(): ImageLoader { diff --git a/android/app-newm/src/main/java/io/newm/di/android/Dependencies.kt b/android/app-newm/src/main/java/io/newm/di/android/Dependencies.kt index fc266451..a64aca0e 100644 --- a/android/app-newm/src/main/java/io/newm/di/android/Dependencies.kt +++ b/android/app-newm/src/main/java/io/newm/di/android/Dependencies.kt @@ -32,8 +32,8 @@ val viewModule = module { viewModel { params -> MusicPlayerViewModel(params.get(), params.get(), get(), get()) } single { RecaptchaClientProvider() } - factory { params -> CreateAccountScreenPresenter(params.get(), get(), get(), get(), get()) } - factory { params -> LoginScreenPresenter(params.get(), get(), get(), get()) } + factory { params -> CreateAccountScreenPresenter(params.get(), get(), get(), get(), get(), get()) } + factory { params -> LoginScreenPresenter(params.get(), get(), get(), get(), get()) } factory { params -> ResetPasswordScreenPresenter( params.get(), @@ -42,7 +42,7 @@ val viewModule = module { get(), get(), get() - ) + , get()) } single { val sharedBuildConfig = get() @@ -65,7 +65,8 @@ val viewModule = module { recaptchaClientProvider = get(), loginUseCase = get(), activityResultContract = ActivityResultContracts.StartActivityForResult(), - logger = get() + logger = get(), + analyticsTracker = get() ) } factory { params -> diff --git a/android/app-newm/src/main/java/io/newm/utils/AndroidNewmAppAnalyticsTracker.kt b/android/app-newm/src/main/java/io/newm/utils/AndroidNewmAppAnalyticsTracker.kt index 4b0438b7..4b558bdd 100644 --- a/android/app-newm/src/main/java/io/newm/utils/AndroidNewmAppAnalyticsTracker.kt +++ b/android/app-newm/src/main/java/io/newm/utils/AndroidNewmAppAnalyticsTracker.kt @@ -5,13 +5,13 @@ import com.google.firebase.analytics.FirebaseAnalytics import com.google.firebase.analytics.ktx.analytics import com.google.firebase.analytics.logEvent import com.google.firebase.ktx.Firebase -import io.newm.shared.AppAnalyticsTracker import io.newm.shared.NewmAppLogger +import io.newm.shared.public.analytics.AppAnalyticsTracker /** * Implementation of [AppAnalyticsTracker] for Android using Firebase Analytics. */ -class AndroidNewmAppAnalyticsTracker(val logger: NewmAppLogger) : AppAnalyticsTracker { +internal class AndroidNewmAppAnalyticsTracker(val logger: NewmAppLogger) : AppAnalyticsTracker { private val firebaseAnalytics = Firebase.analytics diff --git a/android/app-newm/src/main/java/io/newm/utils/AndroidNewmAppLogger.kt b/android/app-newm/src/main/java/io/newm/utils/AndroidNewmAppLogger.kt index ff169d6d..62049733 100644 --- a/android/app-newm/src/main/java/io/newm/utils/AndroidNewmAppLogger.kt +++ b/android/app-newm/src/main/java/io/newm/utils/AndroidNewmAppLogger.kt @@ -2,7 +2,7 @@ package io.newm.utils import android.util.Log import io.newm.shared.AppLogger -import io.newm.shared.NewmAppAnalyticsTracker +import io.newm.shared.public.analytics.NewmAppAnalyticsTracker import io.sentry.Sentry import io.sentry.protocol.User diff --git a/android/app-newm/src/main/java/io/newm/utils/AppForegroundBackgroundTracker.kt b/android/app-newm/src/main/java/io/newm/utils/AppForegroundBackgroundTracker.kt new file mode 100644 index 00000000..c15b5bd9 --- /dev/null +++ b/android/app-newm/src/main/java/io/newm/utils/AppForegroundBackgroundTracker.kt @@ -0,0 +1,54 @@ +package io.newm.utils + +import android.app.Activity +import android.app.Application +import android.os.Bundle +import io.newm.shared.NewmAppLogger +import io.newm.shared.public.analytics.NewmAppAnalyticsTracker + +class AppForegroundBackgroundTracker( + private val analyticsTracker: NewmAppAnalyticsTracker, + private val logger: NewmAppLogger +) : Application.ActivityLifecycleCallbacks { + + private val TAG: String = AppForegroundBackgroundTracker::class.java.simpleName + private var activityReferences = 0 + private var isActivityChangingConfigurations = false + + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { + if (activityReferences == 0 && !isActivityChangingConfigurations) { + // Track initial app launch when first activity is created + try { + analyticsTracker.trackAppLaunch() + } catch (e: Exception) { + logger.error(TAG, "Error tracking app launch", e) + } + } + activityReferences++ + } + + override fun onActivityStarted(activity: Activity) { + activityReferences++ + } + + override fun onActivityResumed(activity: Activity) {} + + override fun onActivityPaused(activity: Activity) {} + + override fun onActivityStopped(activity: Activity) { + activityReferences-- + isActivityChangingConfigurations = activity.isChangingConfigurations + if (activityReferences == 0 && !isActivityChangingConfigurations) { + // Track app closure when all activities are stopped and none are changing configurations + try { + analyticsTracker.trackAppClose() + } catch (e: Exception) { + logger.error(TAG, "Error tracking app close", e) + } + } + } + + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {} + + override fun onActivityDestroyed(activity: Activity) {} +} \ No newline at end of file diff --git a/android/features/login/src/main/java/io/newm/feature/login/screen/createaccount/CreateAccountScreenPresenter.kt b/android/features/login/src/main/java/io/newm/feature/login/screen/createaccount/CreateAccountScreenPresenter.kt index 121ad425..e900633c 100644 --- a/android/features/login/src/main/java/io/newm/feature/login/screen/createaccount/CreateAccountScreenPresenter.kt +++ b/android/features/login/src/main/java/io/newm/feature/login/screen/createaccount/CreateAccountScreenPresenter.kt @@ -17,6 +17,7 @@ import io.newm.feature.login.screen.password.ConfirmPasswordState import io.newm.feature.login.screen.password.VerificationCodeState import io.newm.feature.login.screen.password.PasswordState import io.newm.shared.NewmAppLogger +import io.newm.shared.public.analytics.NewmAppAnalyticsTracker import io.newm.shared.public.usecases.LoginUseCase import io.newm.shared.public.usecases.SignupUseCase import kotlinx.coroutines.launch @@ -26,7 +27,8 @@ class CreateAccountScreenPresenter( private val signupUseCase: SignupUseCase, private val loginUseCase: LoginUseCase, private val recaptchaClientProvider: RecaptchaClientProvider, - private val appLogger: NewmAppLogger + private val appLogger: NewmAppLogger, + private val analyticsTracker: NewmAppAnalyticsTracker ) : Presenter { @Composable @@ -48,6 +50,7 @@ class CreateAccountScreenPresenter( return when (step) { Step.EmailAndPassword -> { + analyticsTracker.trackScreenView("Create Account") EmailAndPasswordUiState( emailState = userEmail, passwordState = password, @@ -57,6 +60,7 @@ class CreateAccountScreenPresenter( ) { event -> when (event) { SignupFormUiEvent.Next -> { + analyticsTracker.trackButtonInteraction("Next") require(emailAndPasswordValid) { "Email and password - next button should not be enabled if any of the fields are invalid" } @@ -87,6 +91,7 @@ class CreateAccountScreenPresenter( } Step.EmailVerification -> { + analyticsTracker.trackScreenView("Email Verification") EmailVerificationUiState( verificationCode = verificationCode, nextButtonEnabled = verificationCode.isValid, @@ -97,6 +102,7 @@ class CreateAccountScreenPresenter( require(emailAndPasswordValid && verificationCode.isValid) { "Email verification - next button should not be enabled if any of the fields are invalid" } + analyticsTracker.trackButtonInteraction("Next") coroutineScope.launch { step = Step.Loading @@ -129,7 +135,10 @@ class CreateAccountScreenPresenter( } } - Step.Loading -> CreateAccountUiState.Loading + Step.Loading -> { + analyticsTracker.trackScreenView("Loading") + CreateAccountUiState.Loading + } } } } diff --git a/android/features/login/src/main/java/io/newm/feature/login/screen/login/LoginScreenPresenter.kt b/android/features/login/src/main/java/io/newm/feature/login/screen/login/LoginScreenPresenter.kt index bb14ec3d..3c4ea488 100644 --- a/android/features/login/src/main/java/io/newm/feature/login/screen/login/LoginScreenPresenter.kt +++ b/android/features/login/src/main/java/io/newm/feature/login/screen/login/LoginScreenPresenter.kt @@ -16,15 +16,16 @@ import io.newm.feature.login.screen.authproviders.RecaptchaClientProvider import io.newm.feature.login.screen.email.EmailState import io.newm.feature.login.screen.password.PasswordState import io.newm.shared.NewmAppLogger +import io.newm.shared.public.analytics.NewmAppAnalyticsTracker import io.newm.shared.public.usecases.LoginUseCase import kotlinx.coroutines.launch -import kotlin.math.log class LoginScreenPresenter( private val navigator: Navigator, private val loginUseCase: LoginUseCase, private val recaptchaClientProvider: RecaptchaClientProvider, - private val logger: NewmAppLogger + private val logger: NewmAppLogger, + private val analyticsTracker: NewmAppAnalyticsTracker ) : Presenter { @Composable override fun present(): LoginScreenUiState { @@ -47,6 +48,7 @@ class LoginScreenPresenter( eventSink = { event -> when (event) { LoginUiEvent.OnLoginClick -> { + analyticsTracker.trackButtonInteraction("Login") coroutineScope.launch { errorMessage = null @@ -73,7 +75,10 @@ class LoginScreenPresenter( } } - LoginUiEvent.ForgotPasswordClick -> navigator.goTo(ResetPasswordScreen(email.text)) + LoginUiEvent.ForgotPasswordClick -> { + analyticsTracker.trackButtonInteraction("Forgot your password password?") + navigator.goTo(ResetPasswordScreen(email.text)) + } } } ) diff --git a/android/features/login/src/main/java/io/newm/feature/login/screen/resetpassword/ResetPasswordScreenPresenter.kt b/android/features/login/src/main/java/io/newm/feature/login/screen/resetpassword/ResetPasswordScreenPresenter.kt index 66a5b7e5..f5b13a8c 100644 --- a/android/features/login/src/main/java/io/newm/feature/login/screen/resetpassword/ResetPasswordScreenPresenter.kt +++ b/android/features/login/src/main/java/io/newm/feature/login/screen/resetpassword/ResetPasswordScreenPresenter.kt @@ -1,6 +1,5 @@ package io.newm.feature.login.screen.resetpassword -import android.util.Log import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -21,6 +20,7 @@ import io.newm.feature.login.screen.resetpassword.ResetPasswordUiEvent.EnterEmai import io.newm.feature.login.screen.resetpassword.ResetPasswordUiEvent.EnterNewPasswordUiEvent import io.newm.feature.login.screen.resetpassword.ResetPasswordUiEvent.EnterVerificationCodeUiEvent import io.newm.shared.NewmAppLogger +import io.newm.shared.public.analytics.NewmAppAnalyticsTracker import io.newm.shared.public.usecases.LoginUseCase import io.newm.shared.public.usecases.ResetPasswordUseCase import io.newm.shared.public.usecases.SignupUseCase @@ -38,7 +38,8 @@ class ResetPasswordScreenPresenter( private val loginUseCase: LoginUseCase, private val resetPasswordUseCase: ResetPasswordUseCase, private val recaptchaClientProvider: RecaptchaClientProvider, - private val logger: NewmAppLogger + private val logger: NewmAppLogger, + private val analyticsTracker: NewmAppAnalyticsTracker ) : Presenter { @Composable override fun present(): ResetPasswordScreenUiState { @@ -52,51 +53,74 @@ class ResetPasswordScreenPresenter( val coroutineScope = rememberCoroutineScope() return when (step) { - ResetPasswordStep.EnterEmail -> ResetPasswordScreenUiState.EnterEmail( - email = email, - errorMessage = errorMessage, - isLoading = isLoading, - submitButtonEnabled = email.isValid && !isLoading, - eventSink = { event -> - when (event) { - EnterEmailUiEvent.OnSubmit -> { - errorMessage = null - isLoading = true - coroutineScope.launch { - try { - recaptchaClientProvider.get().execute(RecaptchaAction.custom("auth_code")).onSuccess { token -> - signupUseCase.requestEmailConfirmationCode(email.text, humanVerificationCode = token, mustExists = true) - step = ResetPasswordStep.EnterVerificationCode - }.onFailure { - logger.error("ResetPasswordScreenPresenter", "Human verification error", it) + ResetPasswordStep.EnterEmail -> { + ResetPasswordScreenUiState.EnterEmail( + email = email, + errorMessage = errorMessage, + isLoading = isLoading, + submitButtonEnabled = email.isValid && !isLoading, + eventSink = { event -> + when (event) { + EnterEmailUiEvent.OnSubmit -> { + analyticsTracker.trackScreenView("Enter Email") + analyticsTracker.trackButtonInteraction("Continue") + errorMessage = null + isLoading = true + coroutineScope.launch { + try { + recaptchaClientProvider.get() + .execute(RecaptchaAction.custom("auth_code")) + .onSuccess { token -> + signupUseCase.requestEmailConfirmationCode( + email.text, + humanVerificationCode = token, + mustExists = true + ) + step = ResetPasswordStep.EnterVerificationCode + }.onFailure { + logger.error( + "ResetPasswordScreenPresenter", + "Human verification error", + it + ) + } + } catch (e: Throwable) { + logger.error( + "ResetPasswordScreenPresenter", + "Requesting email confirmation code failed", + e + ) + errorMessage = e.message } - } catch (e: Throwable) { - logger.error("ResetPasswordScreenPresenter", "Requesting email confirmation code failed", e) - errorMessage = e.message - } - isLoading = false + isLoading = false + } } } - } - }, - ) + }, + ) + } - ResetPasswordStep.EnterVerificationCode -> ResetPasswordScreenUiState.EnterVerificationCode( - code = authCode, - errorMessage = errorMessage, - isLoading = isLoading, - submitButtonEnabled = authCode.isValid && !isLoading, - eventSink = { event -> - when (event) { - EnterVerificationCodeUiEvent.OnSubmit -> { - step = ResetPasswordStep.EnterNewPassword + ResetPasswordStep.EnterVerificationCode -> { + analyticsTracker.trackScreenView("Enter Verification Code") + ResetPasswordScreenUiState.EnterVerificationCode( + code = authCode, + errorMessage = errorMessage, + isLoading = isLoading, + submitButtonEnabled = authCode.isValid && !isLoading, + eventSink = { event -> + when (event) { + EnterVerificationCodeUiEvent.OnSubmit -> { + analyticsTracker.trackButtonInteraction("Continue") + step = ResetPasswordStep.EnterNewPassword + } } - } - }, - ) + }, + ) + } ResetPasswordStep.EnterNewPassword -> { + analyticsTracker.trackScreenView("Enter New Password") val submitEnabled = password.isValid && passwordConfirmation.isValid && !isLoading ResetPasswordScreenUiState.EnterNewPassword( @@ -108,28 +132,45 @@ class ResetPasswordScreenPresenter( eventSink = { event -> when (event) { EnterNewPasswordUiEvent.OnSubmit -> { + analyticsTracker.trackButtonInteraction("Confirm") errorMessage = null isLoading = true coroutineScope.launch { try { - recaptchaClientProvider.get().execute(RecaptchaAction.custom("password_reset")).onSuccess { token -> - resetPasswordUseCase.resetPassword( - email = email.text, - code = authCode.text, - newPassword = password.text, - confirmPassword = passwordConfirmation.text, - humanVerificationCode = token + recaptchaClientProvider.get() + .execute(RecaptchaAction.custom("password_reset")) + .onSuccess { token -> + resetPasswordUseCase.resetPassword( + email = email.text, + code = authCode.text, + newPassword = password.text, + confirmPassword = passwordConfirmation.text, + humanVerificationCode = token + ) + errorMessage = "Password reset successfully" + recaptchaClientProvider.get() + .execute(RecaptchaAction.LOGIN) + .onSuccess { newToken -> + loginUseCase.logIn( + email.text, + password.text, + humanVerificationCode = newToken + ) + navigator.goTo(HomeScreen) + } + }.onFailure { + logger.error( + "ResetPasswordScreenPresenter", + "Human verification error", + it ) - errorMessage = "Password reset successfully" - recaptchaClientProvider.get().execute(RecaptchaAction.LOGIN).onSuccess { newToken -> - loginUseCase.logIn(email.text, password.text, humanVerificationCode = newToken) - navigator.goTo(HomeScreen) - } - }.onFailure { - logger.error("ResetPasswordScreenPresenter", "Human verification error", it) } } catch (e: Throwable) { - logger.error("ResetPasswordScreenPresenter", "Resetting password failed", e) + logger.error( + "ResetPasswordScreenPresenter", + "Resetting password failed", + e + ) errorMessage = e.message } diff --git a/android/features/login/src/main/java/io/newm/feature/login/screen/welcome/WelcomeScreenPresenter.kt b/android/features/login/src/main/java/io/newm/feature/login/screen/welcome/WelcomeScreenPresenter.kt index 687a658d..c48d3825 100644 --- a/android/features/login/src/main/java/io/newm/feature/login/screen/welcome/WelcomeScreenPresenter.kt +++ b/android/features/login/src/main/java/io/newm/feature/login/screen/welcome/WelcomeScreenPresenter.kt @@ -25,9 +25,9 @@ import io.newm.feature.login.screen.authproviders.RecaptchaClientProvider import io.newm.feature.login.screen.authproviders.google.GoogleSignInLauncher import io.newm.feature.login.screen.createaccount.CreateAccountScreen import io.newm.shared.NewmAppLogger +import io.newm.shared.public.analytics.NewmAppAnalyticsTracker import io.newm.shared.public.models.error.KMMException import io.newm.shared.public.usecases.LoginUseCase -import kotlin.math.log class WelcomeScreenPresenter( private val navigator: Navigator, @@ -35,7 +35,8 @@ class WelcomeScreenPresenter( private val googleSignInLauncher: GoogleSignInLauncher, private val activityResultContract: ActivityResultContract, private val recaptchaClientProvider: RecaptchaClientProvider, - private val logger: NewmAppLogger + private val logger: NewmAppLogger, + private val analyticsTracker: NewmAppAnalyticsTracker ) : Presenter { @@ -45,9 +46,18 @@ class WelcomeScreenPresenter( return WelcomeScreenUiState { event -> when (event) { - WelcomeScreenUiEvent.CreateAccountClicked -> navigator.goTo(CreateAccountScreen) - WelcomeScreenUiEvent.LoginClicked -> navigator.goTo(LoginScreen) - WelcomeScreenUiEvent.OnGoogleSignInClicked -> launchGoogleSignIn() + WelcomeScreenUiEvent.CreateAccountClicked -> { + analyticsTracker.trackButtonInteraction("Create account") + navigator.goTo(CreateAccountScreen) + } + WelcomeScreenUiEvent.LoginClicked -> { + analyticsTracker.trackButtonInteraction("Login with email") + navigator.goTo(LoginScreen) + } + WelcomeScreenUiEvent.OnGoogleSignInClicked -> { + analyticsTracker.trackButtonInteraction("Login with Google") + launchGoogleSignIn() + } } } } diff --git a/shared/src/commonMain/kotlin/io.newm.shared/di/Koin.kt b/shared/src/commonMain/kotlin/io.newm.shared/di/Koin.kt index f318ddd3..d4c103c7 100644 --- a/shared/src/commonMain/kotlin/io.newm.shared/di/Koin.kt +++ b/shared/src/commonMain/kotlin/io.newm.shared/di/Koin.kt @@ -1,7 +1,6 @@ package io.newm.shared.di import io.ktor.client.engine.HttpClientEngine -import io.newm.shared.NewmAppAnalyticsTracker import io.newm.shared.NewmAppLogger import io.newm.shared.config.NewmSharedBuildConfig import io.newm.shared.config.NewmSharedBuildConfigImpl @@ -40,6 +39,7 @@ import io.newm.shared.internal.services.cache.WalletConnectionCacheService import io.newm.shared.internal.services.network.NFTNetworkService import io.newm.shared.internal.services.network.WalletConnectionNetworkService import io.newm.shared.internal.store.NftTrackStore +import io.newm.shared.public.analytics.NewmAppAnalyticsTracker import io.newm.shared.public.usecases.ChangePasswordUseCase import io.newm.shared.public.usecases.ConnectWalletUseCase import io.newm.shared.public.usecases.DisconnectWalletUseCase diff --git a/shared/src/commonMain/kotlin/io.newm.shared/AppAnalyticsTracker.kt b/shared/src/commonMain/kotlin/io.newm.shared/public/analytics/AppAnalyticsTracker.kt similarity index 98% rename from shared/src/commonMain/kotlin/io.newm.shared/AppAnalyticsTracker.kt rename to shared/src/commonMain/kotlin/io.newm.shared/public/analytics/AppAnalyticsTracker.kt index fc53488b..8cf1ef62 100644 --- a/shared/src/commonMain/kotlin/io.newm.shared/AppAnalyticsTracker.kt +++ b/shared/src/commonMain/kotlin/io.newm.shared/public/analytics/AppAnalyticsTracker.kt @@ -1,4 +1,4 @@ -package io.newm.shared +package io.newm.shared.public.analytics /** * Interface for tracking analytics events in the application. diff --git a/shared/src/commonMain/kotlin/io.newm.shared/NewmAppAnalyticsTracker.kt b/shared/src/commonMain/kotlin/io.newm.shared/public/analytics/NewmAppAnalyticsTracker.kt similarity index 97% rename from shared/src/commonMain/kotlin/io.newm.shared/NewmAppAnalyticsTracker.kt rename to shared/src/commonMain/kotlin/io.newm.shared/public/analytics/NewmAppAnalyticsTracker.kt index 8885680e..a9df8896 100644 --- a/shared/src/commonMain/kotlin/io.newm.shared/NewmAppAnalyticsTracker.kt +++ b/shared/src/commonMain/kotlin/io.newm.shared/public/analytics/NewmAppAnalyticsTracker.kt @@ -1,4 +1,4 @@ -package io.newm.shared +package io.newm.shared.public.analytics class NewmAppAnalyticsTracker : AppAnalyticsTracker {