diff --git a/app/src/androidTest/java/com/anytypeio/anytype/features/auth/SetupSelectedAccountTest.kt b/app/src/androidTest/java/com/anytypeio/anytype/features/auth/SetupSelectedAccountTest.kt index fbf0be5289..3d66da6891 100644 --- a/app/src/androidTest/java/com/anytypeio/anytype/features/auth/SetupSelectedAccountTest.kt +++ b/app/src/androidTest/java/com/anytypeio/anytype/features/auth/SetupSelectedAccountTest.kt @@ -14,6 +14,7 @@ import com.anytypeio.anytype.domain.block.repo.BlockRepository import com.anytypeio.anytype.domain.config.ConfigStorage import com.anytypeio.anytype.domain.config.FeaturesConfigProvider import com.anytypeio.anytype.domain.device.PathProvider +import com.anytypeio.anytype.CrashReporter import com.anytypeio.anytype.domain.search.ObjectTypesSubscriptionManager import com.anytypeio.anytype.domain.search.RelationsSubscriptionManager import com.anytypeio.anytype.domain.workspace.WorkspaceManager @@ -79,6 +80,9 @@ class SetupSelectedAccountTest { @Mock private lateinit var objectTypesSubscriptionManager: ObjectTypesSubscriptionManager + @Mock + private lateinit var crashReporter: CrashReporter + @Before fun setup() { MockitoAnnotations.openMocks(this) @@ -94,7 +98,8 @@ class SetupSelectedAccountTest { pathProvider = pathProvider, analytics = analytics, objectTypesSubscriptionManager = objectTypesSubscriptionManager, - relationsSubscriptionManager = relationsSubscriptionManager + relationsSubscriptionManager = relationsSubscriptionManager, + crashReporter = crashReporter ) } diff --git a/app/src/main/java/com/anytypeio/anytype/app/AndroidApplication.kt b/app/src/main/java/com/anytypeio/anytype/app/AndroidApplication.kt index d5bd7cc9f0..e90faafe5f 100644 --- a/app/src/main/java/com/anytypeio/anytype/app/AndroidApplication.kt +++ b/app/src/main/java/com/anytypeio/anytype/app/AndroidApplication.kt @@ -3,7 +3,7 @@ package com.anytypeio.anytype.app import android.app.Application import com.amplitude.api.Amplitude import com.anytypeio.anytype.BuildConfig -import com.anytypeio.anytype.SentryCrashReporter +import com.anytypeio.anytype.CrashReporter import com.anytypeio.anytype.analytics.tracker.AmplitudeTracker import com.anytypeio.anytype.di.common.ComponentDependenciesProvider import com.anytypeio.anytype.di.common.ComponentManager @@ -28,7 +28,7 @@ class AndroidApplication : Application(), HasComponentDependencies { lateinit var discoveryManager: MDNSProvider @Inject - lateinit var sentryCrashReporter: SentryCrashReporter + lateinit var crashReporter: CrashReporter @Inject override lateinit var dependencies: ComponentDependenciesProvider @@ -50,7 +50,7 @@ class AndroidApplication : Application(), HasComponentDependencies { main.inject(this) setupAnalytics() setupTimber() - setupSentry() + setupCrashReporter() setupLocalNetworkAddressHandler() } @@ -58,10 +58,8 @@ class AndroidApplication : Application(), HasComponentDependencies { if (BuildConfig.DEBUG) Timber.plant(Timber.DebugTree()) } - private fun setupSentry() { - sentryCrashReporter.init( - withTimber = !BuildConfig.DEBUG - ) + private fun setupCrashReporter() { + crashReporter.init() } private fun setupAnalytics() { diff --git a/app/src/main/java/com/anytypeio/anytype/di/common/ComponentManager.kt b/app/src/main/java/com/anytypeio/anytype/di/common/ComponentManager.kt index d8786009bb..3471524eb4 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/common/ComponentManager.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/common/ComponentManager.kt @@ -81,7 +81,7 @@ import com.anytypeio.anytype.di.feature.sets.PickConditionModule import com.anytypeio.anytype.di.feature.sets.SelectFilterRelationModule import com.anytypeio.anytype.di.feature.sets.viewer.ViewerCardSizeSelectModule import com.anytypeio.anytype.di.feature.sets.viewer.ViewerImagePreviewSelectModule -import com.anytypeio.anytype.di.feature.settings.AboutAppModule +import com.anytypeio.anytype.di.feature.settings.DaggerAboutAppComponent import com.anytypeio.anytype.di.feature.settings.ProfileModule import com.anytypeio.anytype.di.feature.settings.DaggerAppearanceComponent import com.anytypeio.anytype.di.feature.settings.DaggerFilesStorageComponent @@ -812,7 +812,7 @@ class ComponentManager( // Settings val aboutAppComponent = Component { - main.aboutAppComponent().module(AboutAppModule).build() + DaggerAboutAppComponent.factory().create(findComponentDependencies()) } val profileComponent = Component { diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/AuthDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/AuthDI.kt index 33ce71ac1d..fa56ca3c66 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/AuthDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/AuthDI.kt @@ -16,6 +16,7 @@ import com.anytypeio.anytype.domain.auth.repo.AuthRepository import com.anytypeio.anytype.domain.config.ConfigStorage import com.anytypeio.anytype.domain.config.FeaturesConfigProvider import com.anytypeio.anytype.domain.device.PathProvider +import com.anytypeio.anytype.CrashReporter import com.anytypeio.anytype.domain.search.ObjectTypesSubscriptionManager import com.anytypeio.anytype.domain.search.RelationsSubscriptionManager import com.anytypeio.anytype.domain.workspace.WorkspaceManager @@ -254,14 +255,16 @@ object SetupSelectedAccountModule { pathProvider: PathProvider, analytics: Analytics, relationsSubscriptionManager: RelationsSubscriptionManager, - objectTypesSubscriptionManager: ObjectTypesSubscriptionManager + objectTypesSubscriptionManager: ObjectTypesSubscriptionManager, + crashReporter: CrashReporter ): SetupSelectedAccountViewModelFactory { return SetupSelectedAccountViewModelFactory( selectAccount = selectAccount, pathProvider = pathProvider, analytics = analytics, relationsSubscriptionManager = relationsSubscriptionManager, - objectTypesSubscriptionManager = objectTypesSubscriptionManager + objectTypesSubscriptionManager = objectTypesSubscriptionManager, + crashReporter = crashReporter ) } diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/SplashDi.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/SplashDi.kt index 88e942e8e4..a31ae4fab5 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/SplashDi.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/SplashDi.kt @@ -18,6 +18,7 @@ import com.anytypeio.anytype.domain.config.UserSettingsRepository import com.anytypeio.anytype.domain.device.PathProvider import com.anytypeio.anytype.domain.launch.GetDefaultPageType import com.anytypeio.anytype.domain.launch.SetDefaultEditorType +import com.anytypeio.anytype.CrashReporter import com.anytypeio.anytype.domain.misc.UrlBuilder import com.anytypeio.anytype.domain.page.CreateObject import com.anytypeio.anytype.domain.search.ObjectTypesSubscriptionManager @@ -171,4 +172,5 @@ interface SplashDependencies : ComponentDependencies { fun configStorage(): ConfigStorage fun userSettingsRepository(): UserSettingsRepository fun dispatchers(): AppCoroutineDispatchers + fun crashReporter(): CrashReporter } \ No newline at end of file diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/settings/AboutAppDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/settings/AboutAppDI.kt index 2ba57511a9..1d39de0b3a 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/settings/AboutAppDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/settings/AboutAppDI.kt @@ -1,24 +1,30 @@ package com.anytypeio.anytype.di.feature.settings +import androidx.lifecycle.ViewModelProvider import com.anytypeio.anytype.analytics.base.Analytics -import com.anytypeio.anytype.core_utils.di.scope.PerScreen +import com.anytypeio.anytype.di.common.ComponentDependencies import com.anytypeio.anytype.domain.auth.interactor.GetAccount import com.anytypeio.anytype.domain.auth.interactor.GetLibraryVersion import com.anytypeio.anytype.domain.auth.repo.AuthRepository +import com.anytypeio.anytype.domain.config.ConfigStorage import com.anytypeio.anytype.ui.settings.AboutAppFragment import com.anytypeio.anytype.ui_settings.about.AboutAppViewModel +import dagger.Binds +import dagger.Component import dagger.Module import dagger.Provides -import dagger.Subcomponent +import javax.inject.Scope -@Subcomponent(modules = [AboutAppModule::class]) -@PerScreen -interface AboutAppSubComponent { +@Component( + modules = [AboutAppModule::class], + dependencies = [AboutAppDependencies::class] +) +@AboutAppScope +interface AboutAppComponent { - @Subcomponent.Builder - interface Builder { - fun module(module: AboutAppModule): Builder - fun build(): AboutAppSubComponent + @Component.Factory + interface Factory { + fun create(dependencies: AboutAppDependencies): AboutAppComponent } fun inject(fragment: AboutAppFragment) @@ -29,28 +35,35 @@ object AboutAppModule { @JvmStatic @Provides - @PerScreen - fun provideViewModelFactory( - getAccount: GetAccount, - getLibraryVersion: GetLibraryVersion, - analytics: Analytics - ): AboutAppViewModel.Factory = AboutAppViewModel.Factory( - getAccount = getAccount, - getLibraryVersion = getLibraryVersion, - analytics = analytics - ) - - @JvmStatic - @Provides - @PerScreen + @AboutAppScope fun provideGetAccountUseCase( repo: AuthRepository ): GetAccount = GetAccount(repo = repo) @JvmStatic @Provides - @PerScreen + @AboutAppScope fun provideGetVersion( repo: AuthRepository ): GetLibraryVersion = GetLibraryVersion(repo) -} \ No newline at end of file + + @Module + interface Declarations { + + @AboutAppScope + @Binds + fun bindViewModelFactory( + factory: AboutAppViewModel.Factory + ): ViewModelProvider.Factory + } +} + +interface AboutAppDependencies : ComponentDependencies { + fun authRepo(): AuthRepository + fun configStorage(): ConfigStorage + fun analytics(): Analytics +} + +@Scope +@Retention(AnnotationRetention.RUNTIME) +annotation class AboutAppScope \ No newline at end of file diff --git a/app/src/main/java/com/anytypeio/anytype/di/main/CrashReportingModule.kt b/app/src/main/java/com/anytypeio/anytype/di/main/CrashReportingModule.kt index 018501ad1d..a57b18fe32 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/main/CrashReportingModule.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/main/CrashReportingModule.kt @@ -1,8 +1,10 @@ package com.anytypeio.anytype.di.main import android.content.Context +import com.anytypeio.anytype.CrashReporter import com.anytypeio.anytype.SentryCrashReporter import com.anytypeio.anytype.core_utils.tools.AppInfo +import com.anytypeio.anytype.device.BuildProvider import dagger.Module import dagger.Provides import javax.inject.Singleton @@ -15,7 +17,11 @@ object CrashReportingModule { @Singleton fun provideCrashReporter( context: Context, - appInfo: AppInfo - ): SentryCrashReporter = SentryCrashReporter(context, appInfo) - + appInfo: AppInfo, + buildProvider: BuildProvider + ): CrashReporter = SentryCrashReporter( + context = context, + appInfo = appInfo, + withTimber = !buildProvider.isDebug() + ) } \ No newline at end of file diff --git a/app/src/main/java/com/anytypeio/anytype/di/main/MainComponent.kt b/app/src/main/java/com/anytypeio/anytype/di/main/MainComponent.kt index 6438b3707f..7b70c2cd90 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/main/MainComponent.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/main/MainComponent.kt @@ -27,7 +27,7 @@ import com.anytypeio.anytype.di.feature.onboarding.OnboardingMnemonicDependencie import com.anytypeio.anytype.di.feature.onboarding.OnboardingSoulCreationDependencies import com.anytypeio.anytype.di.feature.relations.RelationCreateFromLibraryDependencies import com.anytypeio.anytype.di.feature.relations.RelationEditDependencies -import com.anytypeio.anytype.di.feature.settings.AboutAppSubComponent +import com.anytypeio.anytype.di.feature.settings.AboutAppDependencies import com.anytypeio.anytype.di.feature.settings.ProfileSubComponent import com.anytypeio.anytype.di.feature.settings.AppearanceDependencies import com.anytypeio.anytype.di.feature.settings.FilesStorageDependencies @@ -84,7 +84,8 @@ interface MainComponent : OnboardingAuthDependencies, OnboardingInviteCodeDependencies, OnboardingMnemonicDependencies, - OnboardingSoulCreationDependencies { + OnboardingSoulCreationDependencies, + AboutAppDependencies { fun inject(app: AndroidApplication) @@ -108,7 +109,6 @@ interface MainComponent : //region Settings - fun aboutAppComponent(): AboutAppSubComponent.Builder fun profileComponent(): ProfileSubComponent.Builder fun debugSettingsBuilder(): DebugSettingsSubComponent.Builder fun keychainPhraseComponentBuilder(): KeychainPhraseSubComponent.Builder @@ -212,4 +212,8 @@ private abstract class ComponentDependenciesModule private constructor() { @ComponentDependenciesKey(OnboardingSoulCreationDependencies::class) abstract fun provideOnboardingSoulCreationDependencies(component: MainComponent): ComponentDependencies + @Binds + @IntoMap + @ComponentDependenciesKey(AboutAppDependencies::class) + abstract fun provideAboutAppDependencies(component: MainComponent): ComponentDependencies } \ No newline at end of file diff --git a/app/src/main/java/com/anytypeio/anytype/ui/settings/AboutAppFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/settings/AboutAppFragment.kt index 2d334b07aa..44f8864a7c 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/settings/AboutAppFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/settings/AboutAppFragment.kt @@ -16,7 +16,6 @@ import androidx.compose.runtime.collectAsState import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.capitalize import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight @@ -54,7 +53,8 @@ class AboutAppFragment : BaseBottomSheetComposeFragment() { version = getVersionText(), buildNumber = getBuildNumber(), libraryVersion = vm.libraryVersion.collectAsState().value, - anytypeId = vm.userId.collectAsState().value, + accountId = vm.accountId.collectAsState().value, + analyticsId = vm.analyticsId.collectAsState().value, onMetaClicked = { copyMetaToClipboard() } ) { vm.onExternalLinkClicked(it) @@ -115,7 +115,8 @@ class AboutAppFragment : BaseBottomSheetComposeFragment() { getVersionText(), getBuildNumber(), vm.libraryVersion.value, - vm.userId.value + vm.accountId.value, + vm.analyticsId.value ) ) clipboard.setPrimaryClip(clip) diff --git a/crash-reporting/src/main/java/com/anytypeio/anytype/CrashReporter.kt b/crash-reporting/src/main/java/com/anytypeio/anytype/CrashReporter.kt new file mode 100644 index 0000000000..a303164796 --- /dev/null +++ b/crash-reporting/src/main/java/com/anytypeio/anytype/CrashReporter.kt @@ -0,0 +1,6 @@ +package com.anytypeio.anytype + +interface CrashReporter { + fun init() + fun setUser(userId: String) +} \ No newline at end of file diff --git a/crash-reporting/src/main/java/com/anytypeio/anytype/SentryCrashReporter.kt b/crash-reporting/src/main/java/com/anytypeio/anytype/SentryCrashReporter.kt index bb71e668a9..54641d4e4a 100644 --- a/crash-reporting/src/main/java/com/anytypeio/anytype/SentryCrashReporter.kt +++ b/crash-reporting/src/main/java/com/anytypeio/anytype/SentryCrashReporter.kt @@ -2,18 +2,22 @@ package com.anytypeio.anytype import android.content.Context import com.anytypeio.anytype.core_utils.tools.AppInfo +import io.sentry.Sentry import io.sentry.SentryLevel import io.sentry.android.core.SentryAndroid import io.sentry.android.core.SentryAndroidOptions import io.sentry.android.timber.SentryTimberIntegration +import io.sentry.protocol.User +import java.lang.Exception +import timber.log.Timber class SentryCrashReporter( private val context: Context, private val appInfo: AppInfo, -) { - fun init( - withTimber: Boolean - ) { + private val withTimber: Boolean +) : CrashReporter { + + override fun init() { SentryAndroid.init(context) { options: SentryAndroidOptions -> with(options) { release = appInfo.versionName @@ -29,4 +33,15 @@ class SentryCrashReporter( } } } + + override fun setUser(userId: String) { + try { + val user = User().apply { + id = userId + } + Sentry.setUser(user) + } catch (e: Exception) { + Timber.e(e, "Sentry set user error") + } + } } \ No newline at end of file diff --git a/presentation/build.gradle b/presentation/build.gradle index 874a348e1e..3d0f20f822 100644 --- a/presentation/build.gradle +++ b/presentation/build.gradle @@ -21,6 +21,7 @@ dependencies { implementation project(':library-emojifier') implementation project(':analytics') implementation project(':core-models') + implementation project(':crash-reporting') implementation libs.kotlin implementation libs.coroutinesAndroid diff --git a/presentation/src/main/java/com/anytypeio/anytype/device/BuildProvider.kt b/presentation/src/main/java/com/anytypeio/anytype/device/BuildProvider.kt index 4d42bff69c..bf02998e8e 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/device/BuildProvider.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/device/BuildProvider.kt @@ -1,10 +1,12 @@ package com.anytypeio.anytype.device import android.os.Build +import com.anytypeio.anytype.presentation.BuildConfig interface BuildProvider { fun getManufacturer(): String fun getModel(): String + fun isDebug(): Boolean } class DefaultBuildProvider() : BuildProvider { @@ -15,4 +17,8 @@ class DefaultBuildProvider() : BuildProvider { override fun getModel(): String { return Build.MODEL } + + override fun isDebug(): Boolean { + return BuildConfig.DEBUG + } } \ No newline at end of file diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/auth/account/SetupSelectedAccountViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/auth/account/SetupSelectedAccountViewModel.kt index 3679b81c9d..b3cc93bd30 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/auth/account/SetupSelectedAccountViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/auth/account/SetupSelectedAccountViewModel.kt @@ -14,6 +14,7 @@ import com.anytypeio.anytype.core_models.exceptions.MigrationNeededException import com.anytypeio.anytype.core_utils.common.EventWrapper import com.anytypeio.anytype.domain.auth.interactor.SelectAccount import com.anytypeio.anytype.domain.device.PathProvider +import com.anytypeio.anytype.CrashReporter import com.anytypeio.anytype.domain.search.ObjectTypesSubscriptionManager import com.anytypeio.anytype.domain.search.RelationsSubscriptionManager import com.anytypeio.anytype.presentation.navigation.AppNavigation @@ -31,7 +32,8 @@ class SetupSelectedAccountViewModel( private val pathProvider: PathProvider, private val analytics: Analytics, private val relationsSubscriptionManager: RelationsSubscriptionManager, - private val objectTypesSubscriptionManager: ObjectTypesSubscriptionManager + private val objectTypesSubscriptionManager: ObjectTypesSubscriptionManager, + private val crashReporter: com.anytypeio.anytype.CrashReporter ) : ViewModel(), SupportNavigation> { val isMigrationInProgress = MutableStateFlow(false) @@ -72,6 +74,7 @@ class SetupSelectedAccountViewModel( success = { (analyticsId, status) -> migrationMessageJob.cancel() isMigrationInProgress.value = false + crashReporter.setUser(analyticsId) updateUserProps(analyticsId) sendEvent(startTime) if (status is AccountStatus.PendingDeletion) { diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/auth/account/SetupSelectedAccountViewModelFactory.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/auth/account/SetupSelectedAccountViewModelFactory.kt index bac0512df6..4edfda93cf 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/auth/account/SetupSelectedAccountViewModelFactory.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/auth/account/SetupSelectedAccountViewModelFactory.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModelProvider import com.anytypeio.anytype.analytics.base.Analytics import com.anytypeio.anytype.domain.auth.interactor.SelectAccount import com.anytypeio.anytype.domain.device.PathProvider +import com.anytypeio.anytype.CrashReporter import com.anytypeio.anytype.domain.search.ObjectTypesSubscriptionManager import com.anytypeio.anytype.domain.search.RelationsSubscriptionManager @@ -13,7 +14,8 @@ class SetupSelectedAccountViewModelFactory( private val pathProvider: PathProvider, private val analytics: Analytics, private val relationsSubscriptionManager: RelationsSubscriptionManager, - private val objectTypesSubscriptionManager: ObjectTypesSubscriptionManager + private val objectTypesSubscriptionManager: ObjectTypesSubscriptionManager, + private val crashReporter: com.anytypeio.anytype.CrashReporter ) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") @@ -23,7 +25,8 @@ class SetupSelectedAccountViewModelFactory( pathProvider = pathProvider, analytics = analytics, relationsSubscriptionManager = relationsSubscriptionManager, - objectTypesSubscriptionManager = objectTypesSubscriptionManager + objectTypesSubscriptionManager = objectTypesSubscriptionManager, + crashReporter = crashReporter ) as T } } \ No newline at end of file diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/splash/SplashViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/splash/SplashViewModel.kt index b101ff95cd..01f7d020e0 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/splash/SplashViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/splash/SplashViewModel.kt @@ -19,6 +19,7 @@ import com.anytypeio.anytype.domain.auth.interactor.LaunchWallet import com.anytypeio.anytype.domain.auth.model.AuthStatus import com.anytypeio.anytype.domain.base.BaseUseCase import com.anytypeio.anytype.domain.base.fold +import com.anytypeio.anytype.CrashReporter import com.anytypeio.anytype.domain.page.CreateObject import com.anytypeio.anytype.domain.search.ObjectTypesSubscriptionManager import com.anytypeio.anytype.domain.search.RelationsSubscriptionManager @@ -42,7 +43,8 @@ class SplashViewModel( private val createObject: CreateObject, private val relationsSubscriptionManager: RelationsSubscriptionManager, private val objectTypesSubscriptionManager: ObjectTypesSubscriptionManager, - private val featureToggles: FeatureToggles + private val featureToggles: FeatureToggles, + private val crashReporter: com.anytypeio.anytype.CrashReporter ) : ViewModel() { val commands = MutableSharedFlow(replay = 0) @@ -100,6 +102,7 @@ class SplashViewModel( viewModelScope.launch { launchAccount(BaseUseCase.None).proceed( success = { analyticsId -> + crashReporter.setUser(analyticsId) updateUserProps(analyticsId) val props = Props.empty() sendEvent(startTime, openAccount, props) diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/splash/SplashViewModelFactory.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/splash/SplashViewModelFactory.kt index 8eb491da62..c7c55afa92 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/splash/SplashViewModelFactory.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/splash/SplashViewModelFactory.kt @@ -8,9 +8,7 @@ import com.anytypeio.anytype.domain.auth.interactor.CheckAuthorizationStatus import com.anytypeio.anytype.domain.auth.interactor.GetLastOpenedObject import com.anytypeio.anytype.domain.auth.interactor.LaunchAccount import com.anytypeio.anytype.domain.auth.interactor.LaunchWallet -import com.anytypeio.anytype.domain.launch.GetDefaultPageType -import com.anytypeio.anytype.domain.launch.SetDefaultEditorType -import com.anytypeio.anytype.domain.misc.AppActionManager +import com.anytypeio.anytype.CrashReporter import com.anytypeio.anytype.domain.page.CreateObject import com.anytypeio.anytype.domain.search.ObjectTypesSubscriptionManager import com.anytypeio.anytype.domain.search.RelationsSubscriptionManager @@ -30,7 +28,8 @@ class SplashViewModelFactory @Inject constructor( private val createObject: CreateObject, private val relationsSubscriptionManager: RelationsSubscriptionManager, private val objectTypesSubscriptionManager: ObjectTypesSubscriptionManager, - private val featureToggles: FeatureToggles + private val featureToggles: FeatureToggles, + private val crashReporter: com.anytypeio.anytype.CrashReporter ) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") @@ -44,6 +43,7 @@ class SplashViewModelFactory @Inject constructor( createObject = createObject, relationsSubscriptionManager = relationsSubscriptionManager, objectTypesSubscriptionManager = objectTypesSubscriptionManager, - featureToggles = featureToggles + featureToggles = featureToggles, + crashReporter = crashReporter ) as T } \ No newline at end of file diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/auth/SetupSelectedAccountViewModelTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/auth/SetupSelectedAccountViewModelTest.kt index 570370fb5d..a23392a34f 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/auth/SetupSelectedAccountViewModelTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/auth/SetupSelectedAccountViewModelTest.kt @@ -13,6 +13,7 @@ import com.anytypeio.anytype.domain.block.repo.BlockRepository import com.anytypeio.anytype.domain.config.ConfigStorage import com.anytypeio.anytype.domain.config.FeaturesConfigProvider import com.anytypeio.anytype.domain.device.PathProvider +import com.anytypeio.anytype.CrashReporter import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider import com.anytypeio.anytype.domain.search.ObjectTypesSubscriptionManager import com.anytypeio.anytype.domain.search.RelationsSubscriptionManager @@ -71,6 +72,9 @@ class SetupSelectedAccountViewModelTest { private lateinit var selectAccount: SelectAccount + @Mock + private lateinit var crashReporter: com.anytypeio.anytype.CrashReporter + @Before fun setup() { MockitoAnnotations.openMocks(this) @@ -218,7 +222,8 @@ class SetupSelectedAccountViewModelTest { analytics = analytics, pathProvider = pathProvider, objectTypesSubscriptionManager = objectTypesSubscriptionManager, - relationsSubscriptionManager = relationsSubscriptionManager + relationsSubscriptionManager = relationsSubscriptionManager, + crashReporter = crashReporter ) } } \ No newline at end of file diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/splash/SplashViewModelTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/splash/SplashViewModelTest.kt index 04ca05ae6d..a422134416 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/splash/SplashViewModelTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/splash/SplashViewModelTest.kt @@ -11,6 +11,7 @@ import com.anytypeio.anytype.domain.auth.model.AuthStatus import com.anytypeio.anytype.domain.auth.repo.AuthRepository import com.anytypeio.anytype.domain.base.Either import com.anytypeio.anytype.domain.block.repo.BlockRepository +import com.anytypeio.anytype.CrashReporter import com.anytypeio.anytype.domain.page.CreateObject import com.anytypeio.anytype.domain.search.ObjectTypesSubscriptionManager import com.anytypeio.anytype.domain.search.RelationsSubscriptionManager @@ -68,6 +69,9 @@ class SplashViewModelTest { @Mock lateinit var featureToggles: FeatureToggles + @Mock + private lateinit var crashReporter: com.anytypeio.anytype.CrashReporter + lateinit var vm: SplashViewModel @Before @@ -89,7 +93,8 @@ class SplashViewModelTest { createObject = createObject, relationsSubscriptionManager = relationsSubscriptionManager, objectTypesSubscriptionManager = objectTypesSubscriptionManager, - featureToggles = featureToggles + featureToggles = featureToggles, + crashReporter = crashReporter ) } diff --git a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/about/AboutAppScreen.kt b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/about/AboutAppScreen.kt index 727af444de..14e20b8c60 100644 --- a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/about/AboutAppScreen.kt +++ b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/about/AboutAppScreen.kt @@ -1,5 +1,6 @@ package com.anytypeio.anytype.ui_settings.about +import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -10,11 +11,14 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.anytypeio.anytype.core_ui.foundation.Divider import com.anytypeio.anytype.core_ui.foundation.Dragger import com.anytypeio.anytype.core_ui.views.Caption1Regular +import com.anytypeio.anytype.core_ui.views.Caption2Regular import com.anytypeio.anytype.core_ui.views.Title1 import com.anytypeio.anytype.core_ui.views.UXBody import com.anytypeio.anytype.ui_settings.R @@ -22,7 +26,8 @@ import com.anytypeio.anytype.ui_settings.R @Composable fun AboutAppScreen( libraryVersion: String, - anytypeId: String, + accountId: String, + analyticsId: String, version: String, buildNumber: Int, onMetaClicked: () -> Unit, @@ -41,7 +46,7 @@ fun AboutAppScreen( .align(Alignment.CenterHorizontally) .padding( top = 18.dp, - bottom = 16.dp + bottom = 12.dp ) ) { Text( @@ -72,13 +77,22 @@ fun AboutAppScreen( onExternalLinkClicked(AboutAppViewModel.ExternalLink.PrivacyPolicy) } Divider() + Text( + text = stringResource(R.string.tech_info), + style = Caption1Regular, + color = colorResource(R.color.text_secondary), + modifier = Modifier.padding( + top = 26.dp, + start = 20.dp + ) + ) Box( modifier = Modifier .clickable { onMetaClicked() } .padding( - top = 26.dp, + top = 16.dp, start = 20.dp, end = 20.dp, bottom = 20.dp @@ -90,9 +104,10 @@ fun AboutAppScreen( version, buildNumber, libraryVersion, - anytypeId + accountId, + analyticsId ), - style = Caption1Regular.copy( + style = Caption2Regular.copy( color = colorResource(id = R.color.text_secondary) ) ) @@ -116,6 +131,11 @@ fun Option( text = title, style = UXBody.copy(color = colorResource(id = R.color.text_primary)) ) + Image( + painter = painterResource(id = R.drawable.ic_arrow_forward), + contentDescription = "Arrow Forward", + modifier = Modifier.align(Alignment.CenterEnd) + ) } } @@ -130,4 +150,18 @@ fun Section( style = Caption1Regular.copy(color = colorResource(id = R.color.text_secondary)) ) } +} + +@Preview +@Composable +fun PreviewAboutAppScreen() { + AboutAppScreen( + libraryVersion = "1.0.0", + accountId = "1234567890", + analyticsId = "1234567890", + version = "1.0.0", + buildNumber = 1, + onMetaClicked = {}, + onExternalLinkClicked = {} + ) } \ No newline at end of file diff --git a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/about/AboutAppViewModel.kt b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/about/AboutAppViewModel.kt index 40dc1621ac..7c80678b1f 100644 --- a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/about/AboutAppViewModel.kt +++ b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/about/AboutAppViewModel.kt @@ -9,6 +9,8 @@ import com.anytypeio.anytype.analytics.props.Props import com.anytypeio.anytype.domain.auth.interactor.GetAccount import com.anytypeio.anytype.domain.auth.interactor.GetLibraryVersion import com.anytypeio.anytype.domain.base.BaseUseCase +import com.anytypeio.anytype.domain.config.ConfigStorage +import javax.inject.Inject import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch @@ -16,7 +18,8 @@ import kotlinx.coroutines.launch class AboutAppViewModel( private val getAccount: GetAccount, private val getLibraryVersion: GetLibraryVersion, - private val analytics: Analytics + private val analytics: Analytics, + private val configStorage: ConfigStorage ) : ViewModel() { val navigation = MutableSharedFlow() @@ -40,17 +43,22 @@ class AboutAppViewModel( } val libraryVersion = MutableStateFlow("") - val userId = MutableStateFlow("") + val accountId = MutableStateFlow("") + val analyticsId = MutableStateFlow("") init { viewModelScope.launch { getAccount(BaseUseCase.None).process( failure = {}, success = { acc -> - userId.value = acc.id + accountId.value = acc.id } ) } + viewModelScope.launch { + val config = configStorage.get() + analyticsId.value = config.analytics + } viewModelScope.launch { getLibraryVersion(BaseUseCase.None).process( failure = {}, @@ -92,10 +100,11 @@ class AboutAppViewModel( ) } - class Factory( + class Factory @Inject constructor( private val getAccount: GetAccount, private val getLibraryVersion: GetLibraryVersion, - private val analytics: Analytics + private val analytics: Analytics, + private val configStorage: ConfigStorage ) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") @@ -103,7 +112,8 @@ class AboutAppViewModel( return AboutAppViewModel( getAccount = getAccount, getLibraryVersion = getLibraryVersion, - analytics = analytics + analytics = analytics, + configStorage = configStorage ) as T } } diff --git a/ui-settings/src/main/res/values/strings.xml b/ui-settings/src/main/res/values/strings.xml index c3756ffde7..a60436547b 100644 --- a/ui-settings/src/main/res/values/strings.xml +++ b/ui-settings/src/main/res/values/strings.xml @@ -23,6 +23,7 @@ Pin code Access Wallpaper + Tech info Light Dark @@ -50,8 +51,8 @@ https://anytype.io/terms.pdf https://anytype.io/Anytype_Privacy_Statement.pdf - App version: %1$s\nBuild number: %2$d\nLibrary: %3$s\nUser\u00A0ID:\u00A0%4$s - Device: %1$s\nAndroid version: %2$d\nApp version: %3$s\nBuild number: %4$d\nLibrary: %5$s\nUser\u00A0ID:\u00A0%6$s + App version: %1$s\nBuild number: %2$d\nLibrary version: %3$s\nAccount\u00A0ID:\u00A0%4$s\nAnalytics\u00A0ID:\u00A0%5$s + Device: %1$s\nAndroid version: %2$d\nApp version: %3$s\nBuild number: %4$d\nLibrary version: %5$s\nAccount\u00A0ID:\u00A0%6$s\nAnalytics\u00A0ID:\u00A0%7$s Space name Personal Space You can store up to %1$s of your files on our\nencrypted backup node for free. If you reach the\nlimit, files will be stored only locally.