1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-08 05:47:05 +09:00

DROID-1305 App | Enhancement | About app screen (#3210)

* DROID-1305 about app di

* DROID-1305 sentry crash report implementation

* DROID-1305 sentry, send analytics id

* DROID-1305 build provider, debug state

* DROID-1305 about app screen, redesign + show analytics id

* DROID-1305 string resources

* DROID-1305 sentry module deps

* DROID-1305 fix tests

* DROID-1305 moving crashReporter to crash module
This commit is contained in:
Konstantin Ivanov 2023-05-26 16:49:39 +02:00 committed by uburoiubu
parent 1ea667a6b1
commit 425598a6b6
No known key found for this signature in database
GPG key ID: C8FB80E0A595FBB6
22 changed files with 198 additions and 74 deletions

View file

@ -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
)
}

View file

@ -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() {

View file

@ -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 {

View file

@ -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
)
}

View file

@ -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
}

View file

@ -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)
}
@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

View file

@ -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()
)
}

View file

@ -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
}

View file

@ -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)

View file

@ -0,0 +1,6 @@
package com.anytypeio.anytype
interface CrashReporter {
fun init()
fun setUser(userId: String)
}

View file

@ -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")
}
}
}

View file

@ -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

View file

@ -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
}
}

View file

@ -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<EventWrapper<AppNavigation.Command>> {
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) {

View file

@ -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
}
}

View file

@ -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<Command>(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)

View file

@ -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
}

View file

@ -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
)
}
}

View file

@ -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
)
}

View file

@ -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 = {}
)
}

View file

@ -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<Navigation>()
@ -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
}
}

View file

@ -23,6 +23,7 @@
<string name="pin_code">Pin code</string>
<string name="access">Access</string>
<string name="wallpaper">Wallpaper</string>
<string name="tech_info">Tech info</string>
<string name="light">Light</string>
<string name="dark">Dark</string>
@ -50,8 +51,8 @@
<string name="about_terms_and_conditions_link">https://anytype.io/terms.pdf</string>
<string name="about_privacy_policy_link">https://anytype.io/Anytype_Privacy_Statement.pdf</string>
<string name="about_meta_info">App version: %1$s\nBuild number: %2$d\nLibrary: %3$s\nUser\u00A0ID:\u00A0%4$s</string>
<string name="about_meta_info_for_copy">Device: %1$s\nAndroid version: %2$d\nApp version: %3$s\nBuild number: %4$d\nLibrary: %5$s\nUser\u00A0ID:\u00A0%6$s</string>
<string name="about_meta_info">App version: %1$s\nBuild number: %2$d\nLibrary version: %3$s\nAccount\u00A0ID:\u00A0%4$s\nAnalytics\u00A0ID:\u00A0%5$s</string>
<string name="about_meta_info_for_copy">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</string>
<string name="space_name">Space name</string>
<string name="personal_space">Personal Space</string>
<string name="you_can_store">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.</string>