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

App | Settings | About-app screen on Jetpack Compose (#2113)

This commit is contained in:
Evgenii Kozlov 2022-02-17 19:33:58 +03:00 committed by GitHub
parent 3238742767
commit c0d1e2ba92
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 397 additions and 3 deletions

View file

@ -109,6 +109,10 @@ android {
jvmTarget = JavaVersion.VERSION_11
}
composeOptions {
kotlinCompilerExtensionVersion config["compose_version"]
}
lint {
abortOnError false
disable 'InvalidPackage', 'OldTargetApi', 'IconDensities', 'IconMissingDensityFolder'
@ -118,6 +122,7 @@ android {
buildFeatures {
viewBinding true
compose true
}
splits {
@ -184,6 +189,12 @@ dependencies {
implementation applicationDependencies.lifecycleRuntime
implementation applicationDependencies.lifecycleLiveData
implementation applicationDependencies.compose
implementation applicationDependencies.composeFoundation
implementation applicationDependencies.composeMaterial
implementation applicationDependencies.composeToolingPreview
debugImplementation applicationDependencies.composeTooling
implementation databaseDependencies.room
implementation applicationDependencies.crashlytics

View file

@ -9,6 +9,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.wallpaper.WallpaperSelectModule
import com.anytypeio.anytype.di.main.MainComponent
@ -600,6 +601,10 @@ class ComponentManager(private val main: MainComponent) {
.build()
}
// Settings
val aboutAppComponent = Component { main.aboutAppComponent().module(AboutAppModule).build() }
class Component<T>(private val builder: () -> T) {
private var instance: T? = null

View file

@ -0,0 +1,56 @@
package com.anytypeio.anytype.di.feature.settings
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
import com.anytypeio.anytype.domain.auth.interactor.GetCurrentAccount
import com.anytypeio.anytype.domain.auth.interactor.GetLibraryVersion
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.presentation.settings.AboutAppViewModel
import com.anytypeio.anytype.ui.settings.AboutAppFragment
import dagger.Module
import dagger.Provides
import dagger.Subcomponent
@Subcomponent(modules = [AboutAppModule::class])
@PerScreen
interface AboutAppSubComponent {
@Subcomponent.Builder
interface Builder {
fun module(module: AboutAppModule): Builder
fun build(): AboutAppSubComponent
}
fun inject(fragment: AboutAppFragment)
}
@Module
object AboutAppModule {
@JvmStatic
@Provides
@PerScreen
fun provideViewModelFactory(
getCurrentAccount: GetCurrentAccount,
getLibraryVersion: GetLibraryVersion
): AboutAppViewModel.Factory = AboutAppViewModel.Factory(
getCurrentAccount = getCurrentAccount,
getLibraryVersion = getLibraryVersion
)
@JvmStatic
@Provides
@PerScreen
fun provideGetAccountUseCase(
repo: BlockRepository,
builder: UrlBuilder
): GetCurrentAccount = GetCurrentAccount(repo = repo, builder = builder)
@JvmStatic
@Provides
@PerScreen
fun provideGetVersion(
repo: AuthRepository
): GetLibraryVersion = GetLibraryVersion(repo)
}

View file

@ -2,6 +2,7 @@ package com.anytypeio.anytype.di.main
import com.anytypeio.anytype.app.AndroidApplication
import com.anytypeio.anytype.di.feature.*
import com.anytypeio.anytype.di.feature.settings.AboutAppSubComponent
import com.anytypeio.anytype.di.feature.wallpaper.WallpaperSelectSubComponent
import dagger.Component
import javax.inject.Singleton
@ -26,6 +27,7 @@ interface MainComponent {
fun authComponentBuilder(): AuthSubComponent.Builder
fun profileComponentBuilder(): ProfileSubComponent.Builder
fun aboutAppComponent() : AboutAppSubComponent.Builder
fun splashComponentBuilder(): SplashSubComponent.Builder
fun keychainPhraseComponentBuilder(): KeychainPhraseSubComponent.Builder
fun homeDashboardComponentBuilder(): HomeDashboardSubComponent.Builder

View file

@ -0,0 +1,235 @@
package com.anytypeio.anytype.ui.settings
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.fragment.app.viewModels
import com.anytypeio.anytype.BuildConfig
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.settings.AboutAppViewModel
import javax.inject.Inject
class AboutAppFragment : BaseBottomSheetComposeFragment() {
@Inject
lateinit var factory: AboutAppViewModel.Factory
private val vm by viewModels<AboutAppViewModel> { factory }
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent { aboutAppScreen(vm) }
}
}
override fun injectDependencies() {
componentManager().aboutAppComponent.get().inject(this)
}
override fun releaseDependencies() {
componentManager().aboutAppComponent.release()
}
}
@Composable
fun aboutAppScreen(
vm: AboutAppViewModel
) {
Column {
Box(
modifier = Modifier.padding(top = 6.dp).align(Alignment.CenterHorizontally)
) {
Box(
modifier = Modifier.size(
height = 4.dp,
width = 48.dp
).background(
color = colorResource(R.color.shape_primary),
shape = RoundedCornerShape(6.dp)
)
)
}
Box(
modifier = Modifier.align(Alignment.CenterHorizontally).padding(
top = 75.dp,
bottom = 16.dp
)
) {
Text(
text = stringResource(R.string.about),
fontFamily = fonts,
fontWeight = FontWeight.Bold,
fontSize = 28.sp
)
}
Row(
modifier = Modifier.padding(
start = 20.dp,
end = 20.dp,
top = 12.dp,
bottom = 12.dp
)
) {
Box(modifier = Modifier.weight(1.0f, true)) {
Text(
text = stringResource(R.string.app_version),
fontFamily = fonts,
fontSize = 17.sp,
color = colorResource(R.color.text_secondary)
)
}
Box(
modifier = Modifier.weight(2.0f, true),
contentAlignment = Alignment.CenterEnd
) {
Text(
text = BuildConfig.VERSION_NAME,
fontFamily = fonts,
fontSize = 17.sp,
color = colorResource(R.color.text_primary)
)
}
}
Row(
modifier = Modifier.padding(
start = 20.dp,
end = 20.dp,
top = 12.dp,
bottom = 12.dp
)
) {
Box(modifier = Modifier.weight(1.0f, true)) {
Text(
text = stringResource(R.string.build_number),
fontFamily = fonts,
fontSize = 17.sp,
color = colorResource(R.color.text_secondary)
)
}
Box(
modifier = Modifier.weight(2.0f, true),
contentAlignment = Alignment.CenterEnd
) {
Text(
text = BuildConfig.VERSION_CODE.toString(),
fontFamily = fonts,
fontSize = 17.sp,
color = colorResource(R.color.text_primary)
)
}
}
Row(
modifier = Modifier.padding(
start = 20.dp,
end = 20.dp,
top = 12.dp,
bottom = 12.dp
)
) {
Box(modifier = Modifier.weight(1.0f, true)) {
Text(
text = stringResource(R.string.library),
fontFamily = fonts,
fontSize = 17.sp,
color = colorResource(R.color.text_secondary)
)
}
Box(
modifier = Modifier.weight(2.0f, true),
contentAlignment = Alignment.CenterEnd
) {
Text(
text = vm.libraryVersion.collectAsState().value,
fontFamily = fonts,
fontSize = 17.sp,
color = colorResource(R.color.text_primary)
)
}
}
Row(
modifier = Modifier.padding(
start = 20.dp,
end = 20.dp,
top = 12.dp,
bottom = 32.dp
)
) {
Box(modifier = Modifier.weight(1.0f, true)) {
Text(
text = stringResource(R.string.user_id),
fontFamily = fonts,
fontSize = 17.sp,
color = colorResource(R.color.text_secondary)
)
}
Box(
modifier = Modifier.weight(2.0f, true),
contentAlignment = Alignment.CenterEnd
) {
Text(
text = vm.userId.collectAsState().value,
fontFamily = fonts,
fontSize = 17.sp,
color = colorResource(R.color.text_primary)
)
}
}
}
}
val fonts = FontFamily(
Font(R.font.inter_regular),
Font(R.font.inter_bold, weight = FontWeight.Bold),
Font(R.font.inter_medium, weight = FontWeight.Medium),
Font(R.font.inter_semibold, weight = FontWeight.SemiBold)
)
//val typography = Typography(
// body1 = TextStyle(
// fontFamily = fonts,
// fontWeight = FontWeight.Normal,
// fontSize = 16.sp
// ),
// h1 = TextStyle(
// fontFamily = fonts,
// fontWeight = FontWeight.Bold,
// fontSize = 28.sp
// ),
// h2 = TextStyle(
// fontFamily = fonts,
// fontWeight = FontWeight.Bold,
// fontSize = 22.sp
// ),
// h3 = TextStyle(
// fontFamily = fonts,
// fontWeight = FontWeight.Bold,
// fontSize = 17.sp
// )
//)

View file

@ -263,4 +263,9 @@ Do the computation of an expensive paragraph of text on a background thread:
<string name="download_from_node">All files will be deleted from your current device. They can be downloaded again from a backup node or another device.</string>
<string name="new_profile">new profile</string>
<string name="about">About</string>
<string name="app_version">App version</string>
<string name="library">Library</string>
<string name="user_id">User ID</string>
<string name="build_number">Build Number</string>
</resources>

View file

@ -10,6 +10,7 @@ buildscript {
ext.compile_sdk = 31
ext.target_sdk = 31
ext.min_sdk = 24
ext.compose_version = '1.2.0-alpha03'
ext.application_id = 'com.anytypeio.anytype'
ext.version_name = '1.0'

View file

@ -0,0 +1,20 @@
package com.anytypeio.anytype.core_utils.ui
import android.os.Bundle
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
abstract class BaseBottomSheetComposeFragment : BottomSheetDialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
injectDependencies()
}
override fun onDestroy() {
super.onDestroy()
releaseDependencies()
}
abstract fun injectDependencies()
abstract fun releaseDependencies()
}

View file

@ -6,8 +6,8 @@ import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.test.runTest
import org.junit.Test
import kotlin.test.assertEquals
@ -49,7 +49,7 @@ class FlowExtTest {
}
@Test
fun `should apply withLatestFrom operator for several streams`() = runBlocking {
fun `should apply withLatestFrom operator for several streams`() = runTest {
run {
val flow = flowOf('a', 'b', 'c').onEach { delay(1000) }

View file

@ -9,6 +9,7 @@ ext {
androidx_test_core_version = '1.4.0'
androidx_core_testing_version = '2.1.0'
androidx_security_crypto_version = '1.0.0'
androidx_compose_version = '1.2.0-alpha03'
// Other Android framework dependencies
appcompat_version = '1.3.0'
@ -128,7 +129,12 @@ ext {
kotlinxSerializationJson: "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinx_serialization_json_version",
crashlytics: "com.google.firebase:firebase-crashlytics:$crashlytics_version",
shimmerLayout: "com.facebook.shimmer:shimmer:$shimmer_layout_version",
androidxSecurityCrypto: "androidx.security:security-crypto:$androidx_security_crypto_version"
androidxSecurityCrypto: "androidx.security:security-crypto:$androidx_security_crypto_version",
compose: "androidx.compose.ui:ui:$androidx_compose_version",
composeTooling: "androidx.compose.ui:ui-tooling:$androidx_compose_version",
composeToolingPreview: "androidx.compose.ui:ui-tooling-preview:$androidx_compose_version",
composeFoundation: "androidx.compose.foundation:foundation:$androidx_compose_version",
composeMaterial: "androidx.compose.material:material:$androidx_compose_version"
]
unitTesting = [

View file

@ -0,0 +1,53 @@
package com.anytypeio.anytype.presentation.settings
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.domain.auth.interactor.GetCurrentAccount
import com.anytypeio.anytype.domain.auth.interactor.GetLibraryVersion
import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.presentation.common.BaseViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
class AboutAppViewModel(
private val getCurrentAccount: GetCurrentAccount,
private val getLibraryVersion: GetLibraryVersion
) : BaseViewModel() {
val libraryVersion = MutableStateFlow("")
val userId = MutableStateFlow<String>("")
init {
viewModelScope.launch {
getCurrentAccount(BaseUseCase.None).process(
failure = {},
success = { acc ->
userId.value = acc.id
}
)
}
viewModelScope.launch {
getLibraryVersion(BaseUseCase.None).process(
failure = {},
success = { version ->
libraryVersion.value = version
}
)
}
}
class Factory(
private val getCurrentAccount: GetCurrentAccount,
private val getLibraryVersion: GetLibraryVersion
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return AboutAppViewModel(
getCurrentAccount = getCurrentAccount,
getLibraryVersion = getLibraryVersion
) as T
}
}
}