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:
parent
3238742767
commit
c0d1e2ba92
11 changed files with 397 additions and 3 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
// )
|
||||
//)
|
|
@ -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>
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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) }
|
||||
|
|
|
@ -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 = [
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue