├── .gitignore
├── LICENSE
├── README.assets
├── 1.jpg
├── 2.jpg
├── 3.jpg
├── 5.jpg
├── 6.jpg
└── Screenrecorder-2023-11-03-20-20-52-372.gif
├── README.md
├── README_en.md
├── app
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── kiylx
│ │ └── composepreference
│ │ ├── AppCtx.kt
│ │ ├── testcompose
│ │ ├── MainActivity.kt
│ │ ├── NewComponents.kt
│ │ └── NewComponents2.kt
│ │ └── ui
│ │ └── theme
│ │ ├── Color.kt
│ │ ├── DefaultColorScheme.kt
│ │ ├── Theme.kt
│ │ └── Type.kt
│ └── res
│ ├── drawable
│ ├── ic_launcher_background.xml
│ └── ic_launcher_foreground.xml
│ ├── mipmap-anydpi
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.webp
│ └── ic_launcher_round.webp
│ ├── mipmap-mdpi
│ ├── ic_launcher.webp
│ └── ic_launcher_round.webp
│ ├── mipmap-xhdpi
│ ├── ic_launcher.webp
│ └── ic_launcher_round.webp
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.webp
│ └── ic_launcher_round.webp
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.webp
│ └── ic_launcher_round.webp
│ ├── values
│ ├── colors.xml
│ ├── strings.xml
│ └── themes.xml
│ └── xml
│ ├── backup_rules.xml
│ └── data_extraction_rules.xml
├── build.gradle.kts
├── datastore-util
├── .gitignore
├── build.gradle.kts
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── kiylx
│ └── libx
│ └── pref_component
│ └── datastore_util
│ ├── DataStoreDelegate.kt
│ ├── DataStoreEditor.kt
│ ├── DataStorePreferenceHolder.kt
│ ├── DataStoreUtils.kt
│ └── dataStorePreferences.kt
├── gradle.properties
├── gradle
├── composeLibs.versions.toml
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── jitpack.yml
├── preference-data-core
├── .gitignore
├── build.gradle.kts
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ └── main
│ └── java
│ └── com
│ └── kiylx
│ └── libx
│ └── pref_component
│ └── core
│ ├── DefaultPreferenceHolder.kt
│ ├── IPreferenceEditor.kt
│ └── PreferenceHolder.kt
├── preference-mmkv-util
├── .gitignore
├── build.gradle.kts
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── kiylx
│ └── libx
│ └── pref_component
│ └── mmkv_util
│ ├── MMKVEditor.kt
│ ├── MMKVPreferenceHolder.kt
│ ├── MmkvUtil.kt
│ └── delegate
│ └── MExt.kt
├── preference-ui-compose
├── .gitignore
├── build.gradle.kts
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── kiylx
│ └── compose
│ └── preference
│ ├── component
│ ├── auto
│ │ ├── NodeBase.kt
│ │ ├── PreferenceItem.kt
│ │ ├── PreferenceSlider.kt
│ │ ├── PreferenceSwitch.kt
│ │ └── PreferencesLocal.kt
│ └── cross
│ │ ├── PreferenceCheckBox.kt
│ │ ├── PreferenceItem.kt
│ │ ├── PreferenceRadio.kt
│ │ ├── PreferenceSlider.kt
│ │ └── PreferenceSwitch.kt
│ ├── theme
│ ├── PreferenceDimen.kt
│ ├── PreferenceStyle.kt
│ ├── PreferenceTextStyle.kt
│ └── Preferences.kt
│ └── ui
│ ├── Color.kt
│ ├── ComposeSwitch.kt
│ ├── Exts.kt
│ ├── ImageParser.kt
│ ├── PreferenceLayout.kt
│ ├── RippleClickable.kt
│ └── icons
│ ├── ArrowDropDown.kt
│ ├── ArrowDropUp.kt
│ ├── Check.kt
│ └── EmptyIcon.kt
├── preference-util
├── .gitignore
├── build.gradle.kts
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── kiylx
│ └── libx
│ └── pref_component
│ └── preference_util
│ ├── OldPreferenceHolder.kt
│ ├── PrefsUtil.kt
│ ├── SPEditor.kt
│ └── delegate
│ └── Prefs.kt
└── settings.gradle.kts
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea
2 | *.iml
3 | .gradle
4 | /local.properties
5 | /.idea/caches
6 | /.idea/libraries
7 | /.idea/modules.xml
8 | /.idea/workspace.xml
9 | /.idea/navEditor.xml
10 | /.idea/assetWizardSettings.xml
11 | .DS_Store
12 | /build
13 | /captures
14 | .externalNativeBuild
15 | .cxx
16 | local.properties
17 | ### Android template
18 | # Built application files
19 | *.apk
20 | *.aar
21 | *.ap_
22 | *.aab
23 |
24 | # Files for the ART/Dalvik VM
25 | *.dex
26 |
27 | # Java class files
28 | *.class
29 |
30 | # Generated files
31 | bin/
32 | gen/
33 | out/
34 | # Uncomment the following line in case you need and you don't have the release build type files in your app
35 | # release/
36 |
37 | # Gradle files
38 | .gradle/
39 | build/
40 |
41 | # Local configuration file (sdk path, etc)
42 | local.properties
43 |
44 | # Proguard folder generated by Eclipse
45 | proguard/
46 |
47 | # Log Files
48 | *.log
49 |
50 | # Android Studio Navigation editor temp files
51 | .navigation/
52 |
53 | # Android Studio captures folder
54 | captures/
55 |
56 | # IntelliJ
57 | *.iml
58 | .idea/
59 | .idea/workspace.xml
60 | .idea/tasks.xml
61 | .idea/gradle.xml
62 | .idea/assetWizardSettings.xml
63 | .idea/dictionaries
64 | .idea/libraries
65 | # Android Studio 3 in .gitignore file.
66 | .idea/caches
67 | .idea/modules.xml
68 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you
69 | .idea/navEditor.xml
70 |
71 | # Keystore files
72 | # Uncomment the following lines if you do not want to check your keystore files in.
73 | #*.jks
74 | #*.keystore
75 |
76 | # External native build folder generated in Android Studio 2.2 and later
77 | .externalNativeBuild
78 | .cxx/
79 |
80 | # Google Services (e.g. APIs or Firebase)
81 | # google-services.json
82 |
83 | # Freeline
84 | freeline.py
85 | freeline/
86 | freeline_project_description.json
87 |
88 | # fastlane
89 | fastlane/report.xml
90 | fastlane/Preview.html
91 | fastlane/screenshots
92 | fastlane/test_output
93 | fastlane/readme.md
94 |
95 | # Version control
96 | vcs.xml
97 |
98 | # lint
99 | lint/intermediates/
100 | lint/generated/
101 | lint/outputs/
102 | lint/tmp/
103 | # lint/reports/
104 |
105 | # Android Profiling
106 | *.hprof
107 |
108 |
--------------------------------------------------------------------------------
/README.assets/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/README.assets/1.jpg
--------------------------------------------------------------------------------
/README.assets/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/README.assets/2.jpg
--------------------------------------------------------------------------------
/README.assets/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/README.assets/3.jpg
--------------------------------------------------------------------------------
/README.assets/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/README.assets/5.jpg
--------------------------------------------------------------------------------
/README.assets/6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/README.assets/6.jpg
--------------------------------------------------------------------------------
/README.assets/Screenrecorder-2023-11-03-20-20-52-372.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/README.assets/Screenrecorder-2023-11-03-20-20-52-372.gif
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
2 | plugins {
3 | alias(libs.plugins.android.application)
4 | alias(libs.plugins.kotlin.android)
5 | id("org.jetbrains.kotlin.plugin.compose")
6 | }
7 |
8 | android {
9 | namespace = "com.kiylx.composepreference"
10 | compileSdk = 34
11 |
12 | defaultConfig {
13 | applicationId = "com.kiylx.composepreference"
14 | minSdk = 26
15 | targetSdk = 34
16 | versionCode = 1
17 | versionName = "1.0"
18 |
19 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
20 | vectorDrawables {
21 | useSupportLibrary = true
22 | }
23 | }
24 |
25 | buildTypes {
26 | debug {
27 | applicationIdSuffix = ".debug"
28 | isMinifyEnabled = false
29 | isShrinkResources = false
30 | }
31 | release {
32 | applicationIdSuffix = ".r1"
33 | isMinifyEnabled = true
34 | isShrinkResources = true
35 | proguardFiles(
36 | getDefaultProguardFile("proguard-android-optimize.txt"),
37 | "proguard-rules.pro"
38 | )
39 | }
40 | }
41 | compileOptions {
42 | sourceCompatibility = JavaVersion.VERSION_17
43 | targetCompatibility = JavaVersion.VERSION_17
44 | }
45 | kotlinOptions {
46 | jvmTarget = "17"
47 | }
48 | buildFeatures {
49 | compose = true
50 | }
51 |
52 | packagingOptions {
53 | resources {
54 | excludes += "/META-INF/{AL2.0,LGPL2.1}"
55 | excludes += "META-INF/versions/9/previous-compilation-data.bin"
56 | }
57 | }
58 | }
59 |
60 | dependencies {
61 | implementation(libs.bundles.bundleAndroidx)
62 | implementation(libs.bundles.kotlins)
63 |
64 | val composeBom = platform(composeLibs.androidx.compose.bom)
65 | implementation(composeBom)
66 | androidTestImplementation(composeBom)
67 | implementation(composeLibs.androidx.compose.material3)
68 |
69 | implementation("androidx.compose.ui:ui-tooling-preview")
70 | debugImplementation("androidx.compose.ui:ui-tooling")
71 | debugImplementation("androidx.compose.ui:ui-test-manifest")
72 | //icons
73 | implementation("androidx.compose.material:material-icons-extended")
74 | // Optional - Add window size utils
75 | implementation("androidx.compose.material3:material3-window-size-class")
76 | // Optional - Integration with activities
77 | implementation(composeLibs.androidx.activity.compose)
78 | // Optional - Integration with ViewModels
79 | implementation(composeLibs.androidx.lifecycle.viewmodel.compose)
80 | // Optional - Integration with LiveData
81 | implementation("androidx.compose.runtime:runtime-livedata")
82 | implementation(composeLibs.google.accompanist.systemUiController)
83 |
84 | //datastore
85 | implementation(libs.androidx.datastore.preferences) {
86 | exclude("org.jetbrains.kotlinx","kotlinx-coroutines-core")
87 | }
88 |
89 | implementation(libs.github.mmkv)
90 |
91 | //lib
92 | implementation(project(":preference-ui-compose"))
93 | implementation(project(":preference-data-core"))
94 | implementation(project(":preference-mmkv-util"))
95 | implementation(project(":preference-util"))
96 | implementation(project(":datastore-util"))
97 |
98 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
16 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/kiylx/composepreference/AppCtx.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.composepreference
19 |
20 | import android.app.Application
21 | import com.tencent.mmkv.MMKV
22 | import kotlinx.coroutines.CoroutineName
23 | import kotlinx.coroutines.CoroutineScope
24 | import kotlinx.coroutines.Dispatchers
25 | import kotlinx.coroutines.SupervisorJob
26 | import kotlinx.coroutines.plus
27 |
28 |
29 | class AppCtx : Application() {
30 |
31 | override fun onCreate() {
32 | super.onCreate()
33 | instance = this
34 | scope = CoroutineScope(Dispatchers.IO) + SupervisorJob() + CoroutineName("GLOAB")
35 | //mmkv初始化
36 | MMKV.initialize(this)
37 | }
38 |
39 | companion object {
40 | lateinit var instance: AppCtx
41 | lateinit var scope: CoroutineScope
42 | }
43 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/kiylx/composepreference/testcompose/MainActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.composepreference.testcompose
19 |
20 | import android.os.Bundle
21 | import androidx.activity.compose.setContent
22 | import androidx.appcompat.app.AppCompatActivity
23 | import androidx.compose.foundation.background
24 | import androidx.compose.foundation.isSystemInDarkTheme
25 | import androidx.compose.foundation.layout.fillMaxSize
26 | import androidx.compose.foundation.layout.padding
27 | import androidx.compose.material.icons.Icons
28 | import androidx.compose.material.icons.filled.ExposurePlus1
29 | import androidx.compose.material.icons.filled.ExposurePlus2
30 | import androidx.compose.material3.Icon
31 | import androidx.compose.material3.MaterialTheme
32 | import androidx.compose.material3.NavigationBar
33 | import androidx.compose.material3.NavigationBarItem
34 | import androidx.compose.material3.Scaffold
35 | import androidx.compose.material3.Surface
36 | import androidx.compose.material3.Text
37 | import androidx.compose.runtime.Composable
38 | import androidx.compose.runtime.CompositionLocalProvider
39 | import androidx.compose.runtime.SideEffect
40 | import androidx.compose.runtime.collectAsState
41 | import androidx.compose.runtime.compositionLocalOf
42 | import androidx.compose.runtime.getValue
43 | import androidx.compose.runtime.mutableStateOf
44 | import androidx.compose.runtime.remember
45 | import androidx.compose.runtime.setValue
46 | import androidx.compose.ui.Modifier
47 | import androidx.compose.ui.graphics.Color
48 | import androidx.core.view.WindowCompat
49 | import com.google.accompanist.systemuicontroller.rememberSystemUiController
50 | import com.kiylx.composepreference.ui.theme.ComposeTestTheme
51 | import kotlinx.coroutines.flow.MutableStateFlow
52 |
53 | var isDarkFlow: MutableStateFlow = MutableStateFlow(false)
54 | var LocalTheme = compositionLocalOf { false }
55 |
56 | class MainActivity : AppCompatActivity() {
57 | val TAG = "MainActivity"
58 |
59 | @Composable
60 | fun TransparentSystemBars() {
61 | WindowCompat.setDecorFitsSystemWindows(window, false)
62 | val systemUiController = rememberSystemUiController()
63 | val useDarkIcons = !isSystemInDarkTheme()
64 | SideEffect {
65 | systemUiController.setSystemBarsColor(
66 | color = Color.Transparent,
67 | darkIcons = useDarkIcons,
68 | isNavigationBarContrastEnforced = false,
69 | )
70 | }
71 | }
72 |
73 | override fun onCreate(savedInstanceState: Bundle?) {
74 | super.onCreate(savedInstanceState)
75 | setContent {
76 | var selected by remember {
77 | mutableStateOf(1)
78 | }
79 | val isD = isDarkFlow.collectAsState()
80 | CompositionLocalProvider(LocalTheme provides isD.value) {
81 | ComposeTestTheme(
82 | darkTheme = LocalTheme.current
83 | ) {
84 | TransparentSystemBars()
85 | Scaffold(bottomBar = {
86 | NavigationBar {
87 | NavigationBarItem(
88 | selected = selected == 0,
89 | onClick = { selected = 0 },
90 | icon = {
91 | Icon(
92 | imageVector = Icons.Default.ExposurePlus1,
93 | contentDescription = "one"
94 | )
95 | },
96 | label = { Text("First") })
97 |
98 | NavigationBarItem(
99 | selected = selected == 1,
100 | onClick = { selected = 1 },
101 | icon = {
102 | Icon(
103 | imageVector = Icons.Default.ExposurePlus2,
104 | contentDescription = "two"
105 | )
106 | },
107 | label = { Text("Second") })
108 | }
109 | }) {
110 | Surface(
111 | modifier = Modifier
112 | .fillMaxSize()
113 | .background(MaterialTheme.colorScheme.background)
114 | .padding(it)
115 | //同时添加状态栏和导航栏高度对应的上下 padding
116 | ) {
117 | if (selected == 0) {
118 | NewComponents()
119 | } else {
120 | NewComponents2(this)
121 | }
122 | }
123 | }
124 |
125 | }
126 | }
127 |
128 | }
129 | }
130 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/kiylx/composepreference/testcompose/NewComponents.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.composepreference.testcompose
2 |
3 | import androidx.compose.foundation.layout.Arrangement
4 | import androidx.compose.foundation.layout.Column
5 | import androidx.compose.foundation.layout.PaddingValues
6 | import androidx.compose.foundation.layout.padding
7 | import androidx.compose.foundation.rememberScrollState
8 | import androidx.compose.foundation.verticalScroll
9 | import androidx.compose.material.icons.Icons
10 | import androidx.compose.material.icons.outlined.AccountCircle
11 | import androidx.compose.material.icons.outlined.CloudSync
12 | import androidx.compose.material.icons.outlined.Language
13 | import androidx.compose.material.icons.outlined.LocalDining
14 | import androidx.compose.material.icons.outlined.Palette
15 | import androidx.compose.material.icons.outlined.TipsAndUpdates
16 | import androidx.compose.material.icons.outlined.TouchApp
17 | import androidx.compose.material3.MaterialTheme
18 | import androidx.compose.runtime.Composable
19 | import androidx.compose.runtime.getValue
20 | import androidx.compose.runtime.mutableStateOf
21 | import androidx.compose.runtime.remember
22 | import androidx.compose.runtime.setValue
23 | import androidx.compose.ui.Modifier
24 | import androidx.compose.ui.unit.dp
25 | import com.kiylx.compose.preference.component.cross.PreferenceCheckBox
26 | import com.kiylx.compose.preference.component.cross.PreferenceCollapseItem
27 | import com.kiylx.compose.preference.component.cross.PreferenceItem
28 | import com.kiylx.compose.preference.component.cross.PreferenceRadio
29 | import com.kiylx.compose.preference.component.cross.PreferenceSlider
30 | import com.kiylx.compose.preference.component.cross.PreferenceSubTitle
31 | import com.kiylx.compose.preference.component.cross.PreferenceSwitch
32 | import com.kiylx.compose.preference.component.cross.PreferenceSwitchWithContainer
33 | import com.kiylx.compose.preference.component.cross.PreferenceWithDividerSwitch
34 | import com.kiylx.compose.preference.component.cross.PreferencesCautionCard
35 | import com.kiylx.compose.preference.theme.PreferenceIconStyle
36 | import com.kiylx.compose.preference.theme.Preferences
37 |
38 | @Composable
39 | fun NewComponents() {
40 | Column(
41 | modifier = Modifier
42 | .verticalScroll(rememberScrollState()),
43 | verticalArrangement = Arrangement.spacedBy(4.dp)
44 | ) {
45 | Preferences.SetTheme(
46 | // boxStyle = defaultPreferenceBoxStyle.copy(
47 | // color = MaterialTheme.colorScheme.secondaryContainer
48 | // ),
49 | iconStyle = PreferenceIconStyle(
50 | paddingValues = PaddingValues(8.dp),
51 | tint = MaterialTheme.colorScheme.onPrimary,
52 | backgroundColor = MaterialTheme.colorScheme.primary,
53 | )
54 | ) {
55 | PreferenceItemTest()
56 | PreferenceSubTitle(
57 | modifier = Modifier.padding(top = 8.dp),
58 | title = "其他"
59 | )
60 | var progress by remember {
61 | mutableStateOf(0f)
62 | }
63 | PreferenceSlider(
64 | value = progress,
65 | desc = "滑动条描述",
66 | onValueChanged = { progress = it })
67 | SwitchTest()
68 | PreferenceSubTitle(title = "多选框", modifier = Modifier)
69 | CheckBoxTest()
70 | PreferenceSubTitle(title = "单选框", modifier = Modifier)
71 | RadioTest()
72 | PreferenceSubTitle(title = "折叠", modifier = Modifier)
73 | var expand by remember { mutableStateOf(false) }
74 | PreferenceCollapseItem(
75 | expand = expand,
76 | title = "附加内容",
77 | stateChanged = { expand = !expand })
78 | {
79 | Preferences.SetTheme {
80 | Column(modifier = Modifier.padding(top = 12.dp, start = 12.dp, end = 12.dp)) {
81 | PreferenceItemTest()
82 | }
83 | }
84 | }
85 | }
86 | }
87 | }
88 |
89 | @Composable
90 | private fun PreferenceItemTest() {
91 | PreferencesCautionCard(
92 | title = "调整您的设置信息",
93 | desc = "账户、翻译、帮助信息等",
94 | icon = Icons.Outlined.AccountCircle,
95 | )
96 | PreferenceItem(
97 | modifier = Modifier,
98 | title = "账户",
99 | // icon = Icons.Outlined.AccountCircle,
100 | // desc = "本地、谷歌",
101 | )
102 | PreferenceItem(
103 | title = "颜色和样式",
104 | icon = Icons.Outlined.Palette,
105 | desc = "主题、色调样式、字体",
106 | )
107 | PreferenceItem(
108 | title = "动画",
109 | icon = Icons.Outlined.TouchApp,
110 | desc = "动画反馈、触感反馈",
111 | )
112 | PreferenceItem(
113 | title = "语言",
114 | desc = "中文(zh)",
115 | icon = Icons.Outlined.Language,
116 | )
117 | PreferenceItem(
118 | enabled = false,
119 | title = "关于",
120 | desc = "开源信息、版权",
121 | icon = Icons.Outlined.TipsAndUpdates,
122 | )
123 | }
124 |
125 | @Composable
126 | private fun SwitchTest() {
127 | var checked by remember { mutableStateOf(false) }
128 | PreferenceWithDividerSwitch(
129 | icon = Icons.Outlined.CloudSync,
130 | isChecked = checked,
131 | title = "同步",
132 | desc = "同步您的账户数据"
133 | ) {
134 | checked = it
135 | }
136 |
137 | var checked2 by remember { mutableStateOf(false) }
138 | PreferenceSwitch(
139 | icon = Icons.Outlined.LocalDining,
140 | isChecked = checked2,
141 | title = "餐馆",
142 | desc = "查找附近的餐馆"
143 | ) {
144 | checked2 = it
145 | }
146 |
147 | PreferenceSwitchWithContainer(
148 | title = "调整您的设置信息",
149 | desc = "账户、翻译、帮助信息等",
150 | isChecked = checked2,
151 | icon = Icons.Outlined.AccountCircle,
152 | ) {
153 | checked2 = it
154 | }
155 |
156 | }
157 |
158 | @Composable
159 | private fun RadioTest() {
160 |
161 | var selected by remember {
162 | mutableStateOf(1)
163 | }
164 | PreferenceRadio(
165 | title = "激活背包",
166 | selected = selected == 1,
167 | desc = "使用更大的背包",
168 | ) {
169 | if (it) selected = 1
170 | }
171 | PreferenceRadio(
172 | title = "天空材质",
173 | selected = selected == 2,
174 | desc = "使用更精美的天空材质贴图",
175 | ) {
176 | if (it) selected = 2
177 |
178 | }
179 |
180 | PreferenceRadio(
181 | title = "非官方修复补丁",
182 | selected = selected == 3,
183 | desc = "可能会带来新的bug",
184 | ) {
185 | if (it) selected = 3
186 |
187 | }
188 | }
189 |
190 | @Composable
191 | private fun CheckBoxTest() {
192 | var check1 by remember {
193 | mutableStateOf(false)
194 | }
195 | PreferenceCheckBox(
196 | title = "激活背包",
197 | checked = check1,
198 | desc = "使用更大的背包",
199 | ) {
200 | check1 = it
201 | }
202 |
203 | var check2 by remember {
204 | mutableStateOf(false)
205 | }
206 | PreferenceCheckBox(
207 | title = "天空材质",
208 | checked = check2,
209 | desc = "使用更精美的天空材质贴图",
210 | ) {
211 | check2 = it
212 | }
213 |
214 | var check3 by remember {
215 | mutableStateOf(false)
216 | }
217 | PreferenceCheckBox(
218 | title = "非官方修复补丁",
219 | checked = check3,
220 | desc = "可能会带来新的bug",
221 | ) {
222 | check3 = it
223 | }
224 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/kiylx/composepreference/testcompose/NewComponents2.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.composepreference.testcompose
2 |
3 | import android.content.Context
4 | import androidx.compose.foundation.layout.Arrangement
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.foundation.layout.PaddingValues
7 | import androidx.compose.foundation.layout.padding
8 | import androidx.compose.foundation.rememberScrollState
9 | import androidx.compose.foundation.shape.RoundedCornerShape
10 | import androidx.compose.foundation.verticalScroll
11 | import androidx.compose.material.icons.Icons
12 | import androidx.compose.material.icons.outlined.AccountCircle
13 | import androidx.compose.material.icons.outlined.Language
14 | import androidx.compose.material.icons.outlined.TouchApp
15 | import androidx.compose.material3.MaterialTheme
16 | import androidx.compose.runtime.Composable
17 | import androidx.compose.runtime.getValue
18 | import androidx.compose.runtime.mutableStateOf
19 | import androidx.compose.runtime.remember
20 | import androidx.compose.runtime.rememberCoroutineScope
21 | import androidx.compose.runtime.setValue
22 | import androidx.compose.ui.Modifier
23 | import androidx.compose.ui.unit.dp
24 | import androidx.datastore.dataStoreFile
25 | import com.kiylx.compose.preference.component.auto.PreferenceCollapseItem
26 | import com.kiylx.compose.preference.component.auto.PreferenceItem
27 | import com.kiylx.compose.preference.component.auto.PreferenceSwitch
28 | import com.kiylx.compose.preference.component.auto.PreferenceSwitchWithContainer
29 | import com.kiylx.compose.preference.component.auto.PreferencesCautionCard
30 | import com.kiylx.compose.preference.component.auto.SetTheme
31 | import com.kiylx.compose.preference.component.cross.PreferenceItem
32 | import com.kiylx.compose.preference.component.cross.PreferencesHintCard
33 | import com.kiylx.compose.preference.theme.PreferenceDimen
34 | import com.kiylx.compose.preference.theme.PreferenceIconStyle
35 | import com.kiylx.compose.preference.theme.Preferences
36 | import com.kiylx.compose.preference.theme.defaultPreferenceBoxStyle
37 | import com.kiylx.composepreference.AppCtx
38 | import com.kiylx.libx.pref_component.core.DependenceNode
39 | import com.kiylx.libx.pref_component.datastore_util.DataStorePreferenceHolder
40 | import com.kiylx.libx.pref_component.datastore_util.getDataStore
41 | import getting
42 | import gettingNullable
43 | import kotlinx.coroutines.GlobalScope
44 | import kotlinx.coroutines.launch
45 |
46 | @Composable
47 | fun NewComponents2(ctx: Context) {
48 | //1. 使用dataStore存储偏好值
49 | val holder = remember {
50 | val ds = getDataStore(ctx, "test.pb")
51 | DataStorePreferenceHolder.instance(ds)
52 | }
53 |
54 | //2. 使用mmkv存储偏好值
55 | // val holder = remember {
56 | // MMKVPreferenceHolder.instance(MMKV.defaultMMKV())
57 | // }
58 | //3. 使用sharedprefrence存储偏好值
59 | // val holder = remember {
60 | // OldPreferenceHolder.instance(
61 | // AppCtx.instance.getSharedPreferences(
62 | // "ddd",
63 | // Context.MODE_PRIVATE
64 | // )
65 | // )
66 | // }
67 | val customNodeName = "customNode"
68 | //创建一个自定义节点
69 | val node = holder.registerDependence(customNodeName, true)
70 | val scope = rememberCoroutineScope()
71 |
72 | Column(
73 | modifier = Modifier
74 | .verticalScroll(rememberScrollState()),
75 | verticalArrangement = Arrangement.spacedBy(4.dp)
76 | ) {
77 | Preferences.SetTheme(
78 | holder = holder,
79 | iconStyle = PreferenceIconStyle(
80 | paddingValues = PaddingValues(8.dp),
81 | tint = MaterialTheme.colorScheme.onPrimary,
82 | backgroundColor = MaterialTheme.colorScheme.primary,
83 | )
84 | ) {
85 |
86 | Column {
87 | PreferencesHintCard(
88 | title = "调整您的设置信息",
89 | desc = "账户、翻译、帮助信息等",
90 | )
91 |
92 | PreferenceSwitch(
93 | defaultValue = false,
94 | title = "使用新特性",
95 | desc = "实验功能,可能不稳定",
96 | dependenceKey = DependenceNode.rootName,
97 | keyName = "s1"
98 | ) { state ->
99 | //这里获取并修改了当前的enable状态,
100 | //依赖这个节点的会改变显示状态,
101 | //如果当前没有指定依赖,自身也会受到影响
102 | scope.launch {
103 | holder.getDependence("s1")?.setEnabled(state)
104 | }
105 | }
106 | PreferenceItem(
107 | dependenceKey = "s1",
108 | title = "关联组件",
109 | icon = Icons.Outlined.AccountCircle
110 | )
111 |
112 | PreferenceSwitchWithContainer(
113 | title = "调整您的设置信息",
114 | desc = "账户、翻译、帮助信息等",
115 | defaultValue = false,
116 | keyName = "b2",
117 | dependenceKey = DependenceNode.rootName,
118 | icon = Icons.Outlined.AccountCircle,
119 | ) {
120 | scope.launch {
121 | node.setEnabled(it)
122 | }
123 | }
124 | PreferenceItem(
125 | modifier = Modifier,
126 | title = "账户",
127 | icon = Icons.Outlined.AccountCircle,
128 | dependenceKey = customNodeName,
129 | desc = "本地、谷歌",
130 | )
131 | var expand by remember { mutableStateOf(false) }
132 | PreferenceCollapseItem(
133 | expand = expand,
134 | title = "附加内容",
135 | dependenceKey = customNodeName,
136 | stateChanged = { expand = !expand })
137 | {
138 | Column(modifier = Modifier.padding(horizontal = 16.dp)) {
139 | PreferenceItem(
140 | title = "动画",
141 | icon = Icons.Outlined.TouchApp,
142 | desc = "动画反馈、触感反馈",
143 | )
144 | PreferenceItem(
145 | title = "语言",
146 | desc = "中文(zh)",
147 | icon = Icons.Outlined.Language,
148 | )
149 | }
150 | }
151 | PreferencesCautionCard(
152 | title = "调整您的设置信息",
153 | desc = "账户、翻译、帮助信息等",
154 | dependenceKey = customNodeName,
155 | icon = Icons.Outlined.AccountCircle,
156 | )
157 |
158 | }
159 | }
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/app/src/main/java/com/kiylx/composepreference/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.composepreference.ui.theme
19 | import androidx.compose.ui.graphics.Color
20 |
21 |
22 | val md_theme_light_primary = Color(0xFF6750A4)
23 | val md_theme_light_onPrimary = Color(0xFFFFFFFF)
24 | val md_theme_light_primaryContainer = Color(0xFFEADDFF)
25 | val md_theme_light_onPrimaryContainer = Color(0xFF21005D)
26 | val md_theme_light_secondary = Color(0xFF625B71)
27 | val md_theme_light_onSecondary = Color(0xFFFFFFFF)
28 | val md_theme_light_secondaryContainer = Color(0xFFE8DEF8)
29 | val md_theme_light_onSecondaryContainer = Color(0xFF1D192B)
30 | val md_theme_light_tertiary = Color(0xFF7D5260)
31 | val md_theme_light_onTertiary = Color(0xFFFFFFFF)
32 | val md_theme_light_tertiaryContainer = Color(0xFFFFD8E4)
33 | val md_theme_light_onTertiaryContainer = Color(0xFF31111D)
34 | val md_theme_light_error = Color(0xFFB3261E)
35 | val md_theme_light_onError = Color(0xFFFFFFFF)
36 | val md_theme_light_errorContainer = Color(0xFFF9DEDC)
37 | val md_theme_light_onErrorContainer = Color(0xFF410E0B)
38 | val md_theme_light_outline = Color(0xFF79747E)
39 | val md_theme_light_background = Color(0xFFFFFBFE)
40 | val md_theme_light_onBackground = Color(0xFF1C1B1F)
41 | val md_theme_light_surface = Color(0xFFFFFBFE)
42 | val md_theme_light_onSurface = Color(0xFF1C1B1F)
43 | val md_theme_light_surfaceVariant = Color(0xFFE7E0EC)
44 | val md_theme_light_onSurfaceVariant = Color(0xFF49454F)
45 | val md_theme_light_inverseSurface = Color(0xFF313033)
46 | val md_theme_light_inverseOnSurface = Color(0xFFF4EFF4)
47 | val md_theme_light_inversePrimary = Color(0xFFD0BCFF)
48 | val md_theme_light_shadow = Color(0xFF000000)
49 | val md_theme_light_surfaceTint = Color(0xFF6750A4)
50 | val md_theme_light_outlineVariant = Color(0xFFCAC4D0)
51 | val md_theme_light_scrim = Color(0xFF000000)
52 |
53 | val md_theme_dark_primary = Color(0xFFD0BCFF)
54 | val md_theme_dark_onPrimary = Color(0xFF381E72)
55 | val md_theme_dark_primaryContainer = Color(0xFF4F378B)
56 | val md_theme_dark_onPrimaryContainer = Color(0xFFEADDFF)
57 | val md_theme_dark_secondary = Color(0xFFCCC2DC)
58 | val md_theme_dark_onSecondary = Color(0xFF332D41)
59 | val md_theme_dark_secondaryContainer = Color(0xFF4A4458)
60 | val md_theme_dark_onSecondaryContainer = Color(0xFFE8DEF8)
61 | val md_theme_dark_tertiary = Color(0xFFEFB8C8)
62 | val md_theme_dark_onTertiary = Color(0xFF492532)
63 | val md_theme_dark_tertiaryContainer = Color(0xFF633B48)
64 | val md_theme_dark_onTertiaryContainer = Color(0xFFFFD8E4)
65 | val md_theme_dark_error = Color(0xFFF2B8B5)
66 | val md_theme_dark_onError = Color(0xFF601410)
67 | val md_theme_dark_errorContainer = Color(0xFF8C1D18)
68 | val md_theme_dark_onErrorContainer = Color(0xFFF9DEDC)
69 | val md_theme_dark_outline = Color(0xFF938F99)
70 | val md_theme_dark_background = Color(0xFF1C1B1F)
71 | val md_theme_dark_onBackground = Color(0xFFE6E1E5)
72 | val md_theme_dark_surface = Color(0xFF1C1B1F)
73 | val md_theme_dark_onSurface = Color(0xFFE6E1E5)
74 | val md_theme_dark_surfaceVariant = Color(0xFF49454F)
75 | val md_theme_dark_onSurfaceVariant = Color(0xFFCAC4D0)
76 | val md_theme_dark_inverseSurface = Color(0xFFE6E1E5)
77 | val md_theme_dark_inverseOnSurface = Color(0xFF313033)
78 | val md_theme_dark_inversePrimary = Color(0xFF6750A4)
79 | val md_theme_dark_shadow = Color(0xFF000000)
80 | val md_theme_dark_surfaceTint = Color(0xFFD0BCFF)
81 | val md_theme_dark_outlineVariant = Color(0xFF49454F)
82 | val md_theme_dark_scrim = Color(0xFF000000)
83 |
84 | val seed = Color(0xFF6750A4)
85 |
86 | val pink = Color(0xFFA2B2ED)
87 | val light_pink = Color(0xFF405AA9)
88 | val light_onpink = Color(0xFFFFFFFF)
89 | val light_pinkContainer = Color(0xFFDBE1FF)
90 | val light_onpinkContainer = Color(0xFF00164D)
91 | val dark_pink = Color(0xFFB5C4FF)
92 | val dark_onpink = Color(0xFF042978)
93 | val dark_pinkContainer = Color(0xFF254190)
94 | val dark_onpinkContainer = Color(0xFFDBE1FF)
95 |
96 | val gray =Color(0xFFAAA8AD)
97 |
--------------------------------------------------------------------------------
/app/src/main/java/com/kiylx/composepreference/ui/theme/DefaultColorScheme.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.composepreference.ui.theme
19 |
20 | import androidx.compose.material3.darkColorScheme
21 | import androidx.compose.material3.lightColorScheme
22 |
23 | object DefaultColorScheme{
24 | val LightColorScheme = lightColorScheme(
25 | primary = md_theme_light_primary,
26 | onPrimary = md_theme_light_onPrimary,
27 | primaryContainer = md_theme_light_primaryContainer,
28 | onPrimaryContainer = md_theme_light_onPrimaryContainer,
29 | secondary = md_theme_light_secondary,
30 | onSecondary = md_theme_light_onSecondary,
31 | secondaryContainer = md_theme_light_secondaryContainer,
32 | onSecondaryContainer = md_theme_light_onSecondaryContainer,
33 | tertiary = md_theme_light_tertiary,
34 | onTertiary = md_theme_light_onTertiary,
35 | tertiaryContainer = md_theme_light_tertiaryContainer,
36 | onTertiaryContainer = md_theme_light_onTertiaryContainer,
37 | error = md_theme_light_error,
38 | errorContainer = md_theme_light_errorContainer,
39 | onError = md_theme_light_onError,
40 | onErrorContainer = md_theme_light_onErrorContainer,
41 | background = md_theme_light_background,
42 | onBackground = md_theme_light_onBackground,
43 | surface = md_theme_light_surface,
44 | onSurface = md_theme_light_onSurface,
45 | surfaceVariant = md_theme_light_surfaceVariant,
46 | onSurfaceVariant = md_theme_light_onSurfaceVariant,
47 | outline = md_theme_light_outline,
48 | inverseOnSurface = md_theme_light_inverseOnSurface,
49 | inverseSurface = md_theme_light_inverseSurface,
50 | inversePrimary = md_theme_light_inversePrimary,
51 | surfaceTint = md_theme_light_surfaceTint,
52 | outlineVariant = md_theme_light_outlineVariant,
53 | scrim = md_theme_light_scrim,
54 | )
55 |
56 |
57 | val DarkColorScheme = darkColorScheme(
58 | primary = md_theme_dark_primary,
59 | onPrimary = md_theme_dark_onPrimary,
60 | primaryContainer = md_theme_dark_primaryContainer,
61 | onPrimaryContainer = md_theme_dark_onPrimaryContainer,
62 | secondary = md_theme_dark_secondary,
63 | onSecondary = md_theme_dark_onSecondary,
64 | secondaryContainer = md_theme_dark_secondaryContainer,
65 | onSecondaryContainer = md_theme_dark_onSecondaryContainer,
66 | tertiary = md_theme_dark_tertiary,
67 | onTertiary = md_theme_dark_onTertiary,
68 | tertiaryContainer = md_theme_dark_tertiaryContainer,
69 | onTertiaryContainer = md_theme_dark_onTertiaryContainer,
70 | error = md_theme_dark_error,
71 | errorContainer = md_theme_dark_errorContainer,
72 | onError = md_theme_dark_onError,
73 | onErrorContainer = md_theme_dark_onErrorContainer,
74 | background = md_theme_dark_background,
75 | onBackground = md_theme_dark_onBackground,
76 | surface = md_theme_dark_surface,
77 | onSurface = md_theme_dark_onSurface,
78 | surfaceVariant = md_theme_dark_surfaceVariant,
79 | onSurfaceVariant = md_theme_dark_onSurfaceVariant,
80 | outline = md_theme_dark_outline,
81 | inverseOnSurface = md_theme_dark_inverseOnSurface,
82 | inverseSurface = md_theme_dark_inverseSurface,
83 | inversePrimary = md_theme_dark_inversePrimary,
84 | surfaceTint = md_theme_dark_surfaceTint,
85 | outlineVariant = md_theme_dark_outlineVariant,
86 | scrim = md_theme_dark_scrim,
87 | )
88 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/kiylx/composepreference/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.composepreference.ui.theme
19 |
20 | import android.app.Activity
21 | import android.os.Build
22 | import androidx.compose.foundation.isSystemInDarkTheme
23 | import androidx.compose.material3.MaterialTheme
24 | import androidx.compose.material3.dynamicDarkColorScheme
25 | import androidx.compose.material3.dynamicLightColorScheme
26 | import androidx.compose.runtime.Composable
27 | import androidx.compose.runtime.SideEffect
28 | import androidx.compose.ui.graphics.toArgb
29 | import androidx.compose.ui.platform.LocalContext
30 | import androidx.compose.ui.platform.LocalView
31 | import androidx.core.view.WindowCompat
32 | import com.kiylx.composepreference.ui.theme.DefaultColorScheme.DarkColorScheme
33 | import com.kiylx.composepreference.ui.theme.DefaultColorScheme.LightColorScheme
34 |
35 |
36 | @Composable
37 | fun ComposeTestTheme(
38 | darkTheme: Boolean = isSystemInDarkTheme(),
39 | // Dynamic color is available on Android 12+
40 | dynamicColor: Boolean = true,
41 | content: @Composable () -> Unit
42 | ) {
43 | val colorScheme = when {
44 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
45 | val context = LocalContext.current
46 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
47 | }
48 |
49 | darkTheme -> DarkColorScheme
50 | else -> LightColorScheme
51 | }
52 | val view = LocalView.current
53 | if (!view.isInEditMode) {
54 | SideEffect {
55 | val window = (view.context as Activity).window
56 | window.statusBarColor = colorScheme.primary.toArgb()
57 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
58 | }
59 | }
60 |
61 | MaterialTheme(
62 | colorScheme = colorScheme,
63 | typography = Typography,
64 | content = content
65 | )
66 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/kiylx/composepreference/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.composepreference.ui.theme
19 |
20 | import androidx.compose.material3.Typography
21 | import androidx.compose.ui.text.TextStyle
22 | import androidx.compose.ui.text.font.FontFamily
23 | import androidx.compose.ui.text.font.FontWeight
24 | import androidx.compose.ui.unit.sp
25 |
26 | // Set of Material typography styles to start with
27 | val Typography = Typography(
28 | bodyLarge = TextStyle(
29 | fontFamily = FontFamily.Default,
30 | fontWeight = FontWeight.Normal,
31 | fontSize = 16.sp,
32 | lineHeight = 24.sp,
33 | letterSpacing = 0.5.sp
34 | )
35 | /* Other default text styles to override
36 | titleLarge = TextStyle(
37 | fontFamily = FontFamily.Default,
38 | fontWeight = FontWeight.Normal,
39 | fontSize = 22.sp,
40 | lineHeight = 28.sp,
41 | letterSpacing = 0.sp
42 | ),
43 | labelSmall = TextStyle(
44 | fontFamily = FontFamily.Default,
45 | fontWeight = FontWeight.Medium,
46 | fontSize = 11.sp,
47 | lineHeight = 16.sp,
48 | letterSpacing = 0.5.sp
49 | )
50 | */
51 | )
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
24 |
27 |
32 |
37 |
42 |
47 |
52 |
57 |
62 |
67 |
72 |
77 |
82 |
87 |
92 |
97 |
102 |
107 |
112 |
117 |
122 |
127 |
132 |
137 |
142 |
147 |
152 |
157 |
162 |
167 |
172 |
177 |
182 |
187 |
188 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
17 |
18 |
24 |
25 |
26 |
32 |
35 |
38 |
39 |
40 |
41 |
47 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 | #FFBB86FC
21 | #FF6200EE
22 | #FF3700B3
23 | #FF03DAC5
24 | #FF018786
25 | #FF000000
26 | #FFFFFFFF
27 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 | ComposeTset
20 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
17 |
18 |
25 |
26 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
17 |
18 |
23 |
24 |
25 |
29 |
30 |
36 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | alias(libs.plugins.android.application) apply false
4 | alias(libs.plugins.android.library) apply false
5 | alias(libs.plugins.kotlin.android) apply false
6 | alias(libs.plugins.compose.compiler) apply false
7 | }
8 |
9 | ext {
10 | this["version"] = "1.2.8"
11 | }
12 |
--------------------------------------------------------------------------------
/datastore-util/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/datastore-util/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.gradle.internal.impldep.org.junit.experimental.categories.Categories.CategoryFilter.exclude
2 |
3 | @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
4 | plugins {
5 | alias(libs.plugins.android.library)
6 | alias(libs.plugins.kotlin.android)
7 | id("maven-publish")
8 |
9 | }
10 |
11 | android {
12 | namespace = "com.kiylx.libx.pref_component.datastore_util"
13 | compileSdk = 34
14 |
15 | defaultConfig {
16 | minSdk = 24
17 |
18 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
19 | consumerProguardFiles("consumer-rules.pro")
20 | }
21 |
22 | buildTypes {
23 | release {
24 | isMinifyEnabled = false
25 | proguardFiles(
26 | getDefaultProguardFile("proguard-android-optimize.txt"),
27 | "proguard-rules.pro"
28 | )
29 | }
30 | }
31 | compileOptions {
32 | sourceCompatibility = JavaVersion.VERSION_1_8
33 | targetCompatibility = JavaVersion.VERSION_1_8
34 | }
35 | kotlinOptions {
36 | jvmTarget = "1.8"
37 | }
38 | publishing {
39 | singleVariant("release") {
40 | withSourcesJar()
41 | withJavadocJar()
42 | }
43 | }
44 | }
45 |
46 | dependencies {
47 | implementation(libs.kotlin.coroutines.core)
48 | compileOnly(project(":preference-data-core"))
49 | //datastore
50 | implementation(libs.androidx.datastore.preferences) {
51 | exclude("org.jetbrains.kotlinx","kotlinx-coroutines-core")
52 | }
53 | }
54 | afterEvaluate {
55 | publishing {
56 | publications {
57 | create("release") {
58 | groupId = "com.github.knightwood"
59 | artifactId = "datastore-util"
60 | version = rootProject.ext["version"].toString()
61 | afterEvaluate {
62 | from(components["release"])
63 | }
64 | pom {
65 | name.set("datastore-util")
66 | description.set("preference datastore util")
67 | url.set("https://github.com/Knightwood/ComposePreference")
68 | licenses {
69 | license {
70 | name.set("The Apache License, Version 2.0")
71 | url.set("https://www.apache.org/licenses/LICENSE-2.0.txt")
72 | }
73 | }
74 | developers {
75 | developer {
76 | id.set("knightwood")
77 | name.set("KnightWood")
78 | email.set("33772264+Knightwood@users.noreply.github.com")
79 | }
80 | }
81 | }
82 | }
83 | }
84 | }
85 | }
--------------------------------------------------------------------------------
/datastore-util/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/datastore-util/consumer-rules.pro
--------------------------------------------------------------------------------
/datastore-util/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/datastore-util/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/datastore-util/src/main/java/com/kiylx/libx/pref_component/datastore_util/DataStoreDelegate.kt:
--------------------------------------------------------------------------------
1 | import androidx.datastore.core.DataStore
2 | import androidx.datastore.preferences.core.*
3 | import com.kiylx.libx.pref_component.datastore_util.getKey
4 | import kotlinx.coroutines.CoroutineScope
5 | import kotlinx.coroutines.flow.Flow
6 | import kotlinx.coroutines.flow.firstOrNull
7 | import kotlinx.coroutines.flow.map
8 | import kotlinx.coroutines.launch
9 | import kotlin.properties.ReadWriteProperty
10 | import kotlin.reflect.KClass
11 | import kotlin.reflect.KProperty
12 |
13 | class DataStoreDelegate(
14 | private val scope: CoroutineScope,
15 | private val dataStore: DataStore,
16 | private val key: Preferences.Key,
17 | private val defaultValue: T,
18 | ) : ReadWriteProperty {
19 | private var _value: T = defaultValue
20 |
21 | init {
22 | scope.launch {
23 | _value = dataStore.data.map { preferences ->
24 | preferences[key]
25 | }.firstOrNull() ?: defaultValue
26 | }
27 | }
28 |
29 | override fun getValue(thisRef: Any?, property: KProperty<*>): T {
30 | return _value // 这里需要根据实际情况获取Flow的值
31 | }
32 |
33 | override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
34 | _value = value//同步更改,异步写入datastore
35 | scope.launch {
36 | dataStore.edit { preferences ->
37 | if (value != null) {
38 | preferences[key] = value
39 | } else {
40 | preferences.remove(key)
41 | }
42 | }
43 | }
44 | }
45 | }
46 |
47 |
48 | class DataStoreProvider(
49 | private val dataStore: DataStore,
50 | private val defaultValue: T,
51 | private val scope: CoroutineScope,
52 | private val type: KClass<*>
53 | ) {
54 | companion object {
55 | inline fun of(dataStore: DataStore, defaultValue: T, scope: CoroutineScope): DataStoreProvider {
56 | return DataStoreProvider(dataStore, defaultValue, scope, T::class)
57 | }
58 |
59 | }
60 |
61 | operator fun provideDelegate(thisRef: Any?, property: kotlin.reflect.KProperty<*>): DataStoreDelegate {
62 | return DataStoreDelegate(scope, dataStore, dataStore.getKey(property.name, type), defaultValue)
63 | }
64 | }
65 |
66 | /**
67 | * ```
68 | * //1. 需要有一个协程作用域
69 | * val scope = CoroutineScope(Dispatchers.IO)
70 | * //2. 你可以将属性委托给datastore,变量名就是key的名称
71 | * var username by dataStore.getting(11, scope)
72 | *
73 | * //3. 对数据读写就可以存储到datastore
74 | * MaterialTheme {
75 | * //可以在compose中观察数据变化
76 | * val va = dataStore.asDataFlow("username").collectAsState(initial = 11)
77 | * Column {
78 | * Button(onClick = {
79 | * val randoms = Random.nextInt(0, 11)
80 | * //赋值就会将数据写入datastore
81 | * username = randoms
82 | * //访问变量就可以得到刚刚写入的数据
83 | * println(username)
84 | * }) {
85 | * Text("Random")
86 | * }
87 | * Text("value:${va.value}")
88 | * }
89 | * }
90 | *
91 | * ```
92 | */
93 | inline fun DataStore.getting(defaultValue: T, scope: CoroutineScope) =
94 | DataStoreProvider.of(this, defaultValue, scope)
95 |
96 | inline fun DataStore.gettingNullable(scope: CoroutineScope) =
97 | DataStoreProvider.of(this, null, scope)
98 |
--------------------------------------------------------------------------------
/datastore-util/src/main/java/com/kiylx/libx/pref_component/datastore_util/DataStoreEditor.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.libx.pref_component.datastore_util
19 |
20 | import android.util.Log
21 | import androidx.datastore.core.DataStore
22 | import androidx.datastore.preferences.core.Preferences
23 | import androidx.datastore.preferences.core.booleanPreferencesKey
24 | import androidx.datastore.preferences.core.doublePreferencesKey
25 | import androidx.datastore.preferences.core.edit
26 | import androidx.datastore.preferences.core.floatPreferencesKey
27 | import androidx.datastore.preferences.core.intPreferencesKey
28 | import androidx.datastore.preferences.core.longPreferencesKey
29 | import androidx.datastore.preferences.core.stringPreferencesKey
30 | import androidx.datastore.preferences.core.stringSetPreferencesKey
31 | import com.kiylx.libx.pref_component.core.IPreferenceEditor
32 | import kotlinx.coroutines.CoroutineScope
33 | import kotlinx.coroutines.flow.Flow
34 | import kotlinx.coroutines.flow.FlowCollector
35 | import kotlinx.coroutines.flow.collect
36 | import kotlinx.coroutines.flow.last
37 | import kotlinx.coroutines.flow.map
38 | import kotlinx.coroutines.flow.single
39 | import kotlinx.coroutines.flow.stateIn
40 | import kotlinx.coroutines.flow.toList
41 |
42 | private const val TAG="DataStoreEditor"
43 | /**
44 | * 提供偏好值的读写,datastore实现功能版本
45 | */
46 | class DataStoreEditor(
47 | val keyName: String,
48 | val defaultValue: T,
49 | val dataStore: DataStore
50 | ) : IPreferenceEditor {
51 | var key: Preferences.Key = (when (defaultValue) {
52 | is Int -> {
53 | intPreferencesKey(keyName)
54 | }
55 |
56 | is Boolean -> {
57 | booleanPreferencesKey(keyName)
58 | }
59 |
60 | is String -> {
61 | stringPreferencesKey(keyName)
62 | }
63 |
64 | is Double -> {
65 | doublePreferencesKey(keyName)
66 | }
67 |
68 | is Float -> {
69 | floatPreferencesKey(keyName)
70 | }
71 |
72 | is Long -> {
73 | longPreferencesKey(keyName)
74 | }
75 |
76 | is Set<*> -> {
77 | stringSetPreferencesKey(keyName)
78 | }
79 |
80 | else -> {
81 | throw IllegalArgumentException("not support")
82 | }
83 | }) as Preferences.Key
84 |
85 | private val flow: Flow = dataStore.data.map { preferences ->
86 | // No type safety.
87 | val tmp = preferences[key] ?: defaultValue
88 | Log.e(TAG, "读取:$tmp" )
89 | tmp
90 | }
91 |
92 | override fun flow(): Flow {
93 | return flow
94 | }
95 |
96 | override fun readValue(): T {
97 | throw IllegalAccessException("data store cannot use this function, please use readValueAsync()")
98 | }
99 |
100 | override suspend fun readValueAsync(): T {
101 | return flow.last()
102 | }
103 |
104 | override suspend fun write(data: T) {
105 | dataStore.edit {
106 | it[key] = data
107 | }
108 | }
109 |
110 | }
--------------------------------------------------------------------------------
/datastore-util/src/main/java/com/kiylx/libx/pref_component/datastore_util/DataStorePreferenceHolder.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.libx.pref_component.datastore_util;
19 |
20 | import android.content.Context
21 | import androidx.datastore.core.DataStore
22 | import androidx.datastore.preferences.core.Preferences
23 | import androidx.datastore.preferences.preferencesDataStore
24 | import com.kiylx.libx.pref_component.core.PreferenceHolder
25 |
26 |
27 |
28 | /**
29 | * 向界面提供、管理PreferenceProvider
30 | * ```
31 | * val Context.store by preferencesDataStore(name = "test")
32 | *
33 | * fun example(context: Context){
34 | * val holder = DataStorePreferenceHolder.instance(context.store)
35 | * }
36 | *
37 | * ```
38 | */
39 | class DataStorePreferenceHolder internal constructor(
40 | private val dataStore: DataStore,
41 | ) : PreferenceHolder() {
42 |
43 | override fun getSingleDataEditor(
44 | keyName: String,
45 | defaultValue: T,
46 | ): DataStoreEditor {
47 | return (hashMap[keyName] as? DataStoreEditor) ?: let {
48 | val tmp = DataStoreEditor(keyName, defaultValue, dataStore)
49 | hashMap[keyName] = tmp
50 | tmp
51 | }
52 | }
53 |
54 | companion object {
55 |
56 | @Volatile
57 | var ps: DataStorePreferenceHolder? = null
58 |
59 | fun instance(
60 | dataStore: DataStore
61 | ): DataStorePreferenceHolder {
62 | return ps ?: synchronized(this) {
63 | ps ?: DataStorePreferenceHolder(dataStore).also {
64 | ps = it
65 | }
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/datastore-util/src/main/java/com/kiylx/libx/pref_component/datastore_util/DataStoreUtils.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.libx.pref_component.datastore_util
19 |
20 | import androidx.datastore.core.DataStore
21 | import androidx.datastore.preferences.core.*
22 | import kotlinx.coroutines.flow.Flow
23 | import kotlinx.coroutines.flow.map
24 | import kotlin.reflect.KClass
25 |
26 | /**
27 | * 批量添加或编辑,参数使用 key to value 生成[Preferences.Pair]
28 | */
29 | suspend inline fun DataStore.putAll(vararg pairs: Preferences.Pair) {
30 | this.updateData {
31 | it.toMutablePreferences().putAll(*pairs)
32 | it
33 | }
34 | }
35 |
36 | inline fun DataStore.asDataFlow(
37 | key: Preferences.Key,
38 | defaultValue: T
39 | ): Flow {
40 | return this.data.map { preferences ->
41 | // No type safety.
42 | preferences[key] ?: defaultValue
43 | }
44 | }
45 |
46 | inline fun DataStore.asDataFlow(
47 | key: String,
48 | ): Flow {
49 | val key1 = getKey(key)
50 | return this.data.map { preferences ->
51 | // No type safety.
52 | preferences[key1]
53 | }
54 | }
55 |
56 | fun DataStore.getKey(keyName: String, cls: KClass<*>): Preferences.Key {
57 | return (when (cls) {
58 | Int::class -> {
59 | intPreferencesKey(keyName)
60 | }
61 |
62 | Boolean::class -> {
63 | booleanPreferencesKey(keyName)
64 | }
65 |
66 | String::class -> {
67 | stringPreferencesKey(keyName)
68 | }
69 |
70 | Double::class -> {
71 | doublePreferencesKey(keyName)
72 | }
73 |
74 | Float::class -> {
75 | floatPreferencesKey(keyName)
76 | }
77 |
78 | Long::class -> {
79 | longPreferencesKey(keyName)
80 | }
81 |
82 | Set::class -> {
83 | stringSetPreferencesKey(keyName)
84 | }
85 |
86 | else -> {
87 | throw IllegalArgumentException("not support")
88 |
89 | }
90 | }) as Preferences.Key
91 |
92 | }
93 |
94 | inline fun DataStore.getKey(keyName: String): Preferences.Key {
95 | return getKey(keyName, T::class)
96 | }
97 |
--------------------------------------------------------------------------------
/datastore-util/src/main/java/com/kiylx/libx/pref_component/datastore_util/dataStorePreferences.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.libx.pref_component.datastore_util
19 |
20 | import android.content.Context
21 | import androidx.datastore.core.DataMigration
22 | import androidx.datastore.core.DataStore
23 | import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
24 | import androidx.datastore.preferences.core.PreferenceDataStoreFactory
25 | import androidx.datastore.preferences.core.Preferences
26 | import androidx.datastore.preferences.preferencesDataStore
27 | import kotlinx.coroutines.CoroutineScope
28 | import kotlinx.coroutines.Dispatchers
29 | import kotlinx.coroutines.SupervisorJob
30 | import okio.Path.Companion.toPath
31 |
32 |
33 | fun getDataStore(
34 | context: Context,
35 | fileName: String,
36 | corruptionHandler: ReplaceFileCorruptionHandler? = null,
37 | coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
38 | migrations: List> = emptyList(),
39 | ): DataStore = DataStoreConfig.getDataStore(
40 | corruptionHandler = corruptionHandler,
41 | migrations = migrations,
42 | coroutineScope = coroutineScope,
43 | path = {
44 | context.filesDir.resolve(fileName).absolutePath
45 | }
46 | )
47 |
48 |
49 | internal object DataStoreConfig {
50 | private lateinit var dataStore: DataStore
51 |
52 | fun checkFileName(name: String): String {
53 | return if (!name.endsWith(".preferences_pb")) {
54 | throw IllegalArgumentException("should be end with \".preferences_pb\"")
55 | } else {
56 | name
57 | }
58 | }
59 |
60 | /**
61 | * Gets the singleton DataStore instance, creating it if necessary.
62 | */
63 | fun getDataStore(
64 | corruptionHandler: ReplaceFileCorruptionHandler? = null,
65 | coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
66 | migrations: List> = emptyList(),
67 | path: () -> String,
68 | ): DataStore {
69 | return synchronized(this::class.java) {
70 | if (DataStoreConfig::dataStore.isInitialized) {
71 | dataStore
72 | } else {
73 | PreferenceDataStoreFactory
74 | .createWithPath(
75 | corruptionHandler = corruptionHandler,
76 | scope = coroutineScope,
77 | migrations = migrations,
78 | produceFile = {
79 | path().toPath()
80 | }
81 | )
82 | .also {
83 | dataStore = it
84 | }
85 | }
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/gradle/composeLibs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | lifecycle_version = "2.7.0"
3 | activity_compose_version = "1.8.2"
4 | accompanist_version = "0.31.3-beta"
5 | compose_bom="2024.10.01"
6 | material3="1.3.1"
7 |
8 | [libraries]
9 | androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "compose_bom" }
10 | androidx-compose-material3 = { group = "androidx.compose.material3", name="material3" }
11 |
12 |
13 | #lifecycle
14 | # Lifecycle utilities for Compose
15 | androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycle_version" }
16 | # ViewModel utilities for Compose
17 | androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycle_version" }
18 |
19 | #activity
20 | androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activity_compose_version" }
21 |
22 | #Accompanist
23 | google-accompanist-navigation-animation = { module = "com.google.accompanist:accompanist-navigation-animation", version.ref = "accompanist_version" }
24 | google-accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist_version" }
25 | google-accompanist-systemUiController = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist_version" }
26 | google-accompanist-webView = { module = "com.google.accompanist:accompanist-webview", version.ref = "accompanist_version" }
27 | google-accompanist-pagerLayouts = { module = "com.google.accompanist:accompanist-pager", version.ref = "accompanist_version" }
28 | google-accompanist-pagerIndicators = { module = "com.google.accompanist:accompanist-pager-indicators", version.ref = "accompanist_version" }
29 | google-accompanist-flowlayout = { module = "com.google.accompanist:accompanist-flowlayout", version.ref = "accompanist_version" }
30 |
31 | #####################################其他组件库#############################
32 |
33 | [plugins]
34 |
35 | [bundles]
36 |
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | kotlin_version = "2.0.21"
3 | gradle_plugin_version = "8.5.1"
4 |
5 | androidx_core = "1.12.0"
6 | appcompat = "1.6.1"
7 | fragment-ktx = "1.6.1"
8 | activity-ktx = "1.8.2"
9 | google_material = "1.11.0"
10 | androidx_preference = "1.2.0"
11 | #datastore
12 | datastore-version = "1.1.1"
13 | #lifecycle
14 | lifecycle_version = "2.7.0"
15 | arch_version = "2.2.0"
16 | junit = "4.13.2"
17 | androidx-test-ext-junit = "1.1.5"
18 | espresso-core = "3.5.1"
19 | kotlin_coroutines ="1.9.0"
20 | mmkv = "1.3.0"
21 | m3ColorUtilities="1.0"
22 |
23 | ##################################################其他依赖的版本#########################################
24 |
25 |
26 | [libraries]
27 | ############################################## Kotlin Gradle插件依赖库 ##################################################
28 | kotlin-gradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin_version" }
29 |
30 | ################################################### gradle插件依赖库 ##################################################
31 | android-gradlePlugin = { module = "com.android.tools.build:gradle", version.ref = "gradle_plugin_version" }
32 |
33 | ########################## 谷歌官方库,例如AndroidX系列,material组件库,room库,导航库,布局库等 #########################
34 | #AndroidX核心库
35 | androidx-appCompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
36 | androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx_core" }
37 | androidx-fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "fragment-ktx" }
38 | androidx-activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activity-ktx" }
39 |
40 | ############################ 测试 ###########################
41 | junit = { group = "junit", name = "junit", version.ref = "junit" }
42 | androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" }
43 | espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" }
44 |
45 | ############################ 布局组件库 ###########################
46 | google-material = { module = "com.google.android.material:material", version.ref = "google_material" }
47 | androidx-preference = { module = "androidx.preference:preference-ktx", version.ref = "androidx_preference" }
48 |
49 | ########################### Datastore依赖库 ################################
50 | # Preferences DataStore(可以直接使用)
51 | androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastore-version" }
52 |
53 | ########################### Lifecycle依赖库 ################################
54 | # ViewModel
55 | lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycle_version" }
56 | # LiveData
57 | lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "lifecycle_version" }
58 | # Lifecycles only = { module =without ViewModel or LiveData}
59 | lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle_version" }
60 |
61 | ################################## kotlin依赖 #########################################
62 | kotlin-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin_coroutines" }
63 | kotlin-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlin_coroutines" }
64 |
65 | ################################## 其他依赖 #########################################
66 | github-mmkv = { module = "com.tencent:mmkv", version.ref = "mmkv" }
67 | github-knightwood-m3ColorUtilities = { module = "com.github.knightwood:MaterialColorUtilities", version.ref = "m3ColorUtilities" }
68 |
69 |
70 | [plugins]
71 | #application和library插件
72 | android-application = { id = "com.android.application", version.ref = "gradle_plugin_version" }
73 | android-library = { id = "com.android.library", version.ref = "gradle_plugin_version" }
74 | #kotlin支持
75 | kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin_version" }
76 | compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin_version" }
77 |
78 | ##################################其他插件#########################################
79 | # kotlin序列化插件的版本会跟kotlin编译器同时发布,因此一致
80 | kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin_version" }
81 |
82 |
83 | [bundles]
84 | bundleAndroidx = [
85 | "androidx-appCompat", "androidx-core-ktx",
86 | "androidx-fragment-ktx", "androidx-activity-ktx",
87 | "lifecycle-runtime-ktx", "lifecycle-viewmodel-ktx",
88 | ]
89 |
90 | kotlins = ["kotlin-coroutines-core", "kotlin-coroutines-android"]
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Aug 04 14:27:10 CST 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/jitpack.yml:
--------------------------------------------------------------------------------
1 | jdk:
2 | - openjdk17
--------------------------------------------------------------------------------
/preference-data-core/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/preference-data-core/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
2 |
3 | @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
4 | plugins {
5 | id("java-library")
6 | kotlin("jvm")
7 | id("maven-publish")
8 | }
9 |
10 | tasks.withType {
11 | kotlinOptions {
12 | jvmTarget = "17"
13 | }
14 | }
15 |
16 | java {
17 | sourceCompatibility = JavaVersion.VERSION_17
18 | targetCompatibility = JavaVersion.VERSION_17
19 | withSourcesJar()
20 | withJavadocJar()
21 | }
22 |
23 | dependencies {
24 | implementation(libs.kotlin.coroutines.core)
25 | }
26 |
27 | publishing {
28 | publications {
29 | create("release") {
30 | groupId = "com.github.knightwood"
31 | artifactId = "preference-data-core"
32 | version = rootProject.ext["version"].toString()
33 | from(components.findByName("java"))
34 |
35 | pom {
36 | name.set("preference-data-core")
37 | description.set("preference data core")
38 | url.set("https://github.com/Knightwood/ComposePreference")
39 | licenses {
40 | license {
41 | name.set("The Apache License, Version 2.0")
42 | url.set("https://www.apache.org/licenses/LICENSE-2.0.txt")
43 | }
44 | }
45 | developers {
46 | developer {
47 | id.set("knightwood")
48 | name.set("KnightWood")
49 | email.set("33772264+Knightwood@users.noreply.github.com")
50 | }
51 | }
52 | }
53 | }
54 | }
55 | }
56 |
57 | //private fun Project.configureKotlin() {
58 | // // Use withType to workaround https://youtrack.jetbrains.com/issue/KT-55947
59 | // tasks.withType().configureEach {
60 | // kotlinOptions {
61 | // // Set JVM target to 11
62 | // jvmTarget = JavaVersion.VERSION_11.toString()
63 | // // Treat all Kotlin warnings as errors (disabled by default)
64 | // // Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties
65 | // val warningsAsErrors: String? by project
66 | // allWarningsAsErrors = warningsAsErrors.toBoolean()
67 | // freeCompilerArgs = freeCompilerArgs + listOf(
68 | // // Enable experimental coroutines APIs, including Flow
69 | // "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
70 | // )
71 | // }
72 | // }
73 | //}
--------------------------------------------------------------------------------
/preference-data-core/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/preference-data-core/consumer-rules.pro
--------------------------------------------------------------------------------
/preference-data-core/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/preference-data-core/src/main/java/com/kiylx/libx/pref_component/core/DefaultPreferenceHolder.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.libx.pref_component.core
2 |
3 | import kotlinx.coroutines.flow.Flow
4 | import kotlinx.coroutines.flow.MutableStateFlow
5 |
6 | /**
7 | * 默认的偏好值存储工具,其实他根本不会存储偏好值。
8 | */
9 | class DefaultPreferenceHolder internal constructor(): PreferenceHolder() {
10 | override fun getSingleDataEditor(
11 | keyName: String,
12 | defaultValue: T
13 | ): IPreferenceEditor {
14 | return hashMap[keyName]?.let {
15 | it as IPreferenceEditor
16 | } ?: let {
17 | val tmp = FakeEditor(keyName, defaultValue)
18 | hashMap[keyName] = tmp
19 | tmp
20 | }
21 | }
22 |
23 | companion object{
24 | @Volatile
25 | var ps: DefaultPreferenceHolder? = null
26 | fun instance(
27 | ): DefaultPreferenceHolder {
28 | return ps ?: synchronized(this) {
29 | ps ?: DefaultPreferenceHolder().also { ps = it }
30 | }
31 | }
32 | }
33 | }
34 |
35 | class FakeEditor(
36 | val keyName: String,
37 | val defaultValue: T,
38 | ) : IPreferenceEditor {
39 | private val stateFlow: MutableStateFlow = MutableStateFlow(defaultValue)
40 |
41 | override fun flow(): Flow {
42 | return stateFlow
43 | }
44 |
45 | override fun readValue(): T {
46 | return stateFlow.value
47 | }
48 |
49 | override suspend fun write(data: T) {
50 | this.stateFlow.emit(data)
51 | }
52 | }
--------------------------------------------------------------------------------
/preference-data-core/src/main/java/com/kiylx/libx/pref_component/core/IPreferenceEditor.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.libx.pref_component.core
19 |
20 | import kotlinx.coroutines.flow.Flow
21 |
22 | /**
23 | * 子类实现此接口以提供具体的每个偏好值的读写能力
24 | */
25 | interface IPreferenceEditor {
26 |
27 | fun flow(): Flow
28 |
29 | fun readValue(): T
30 |
31 | suspend fun readValueAsync(): T = readValue()
32 |
33 | suspend fun write(data: T)
34 |
35 | }
36 | //兼容旧名称
37 | typealias IPreferenceReadWrite = IPreferenceEditor
--------------------------------------------------------------------------------
/preference-data-core/src/main/java/com/kiylx/libx/pref_component/core/PreferenceHolder.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.libx.pref_component.core
19 |
20 | import kotlinx.coroutines.flow.MutableStateFlow
21 |
22 | abstract class PreferenceHolder {
23 | //记录每个偏好值的key与其对应的编辑工具
24 | val hashMap: HashMap = hashMapOf()
25 |
26 | //记录每个key对应的enable状态
27 | val dependenceTree: HashMap = hashMapOf()
28 |
29 | init {
30 | //放入默认的公共依赖的根节点
31 | dependenceTree[DependenceNode.rootName] = DependenceNode(
32 | enable = true,
33 | keyName = DependenceNode.rootName
34 | )
35 | }
36 |
37 | /**
38 | * 获取编辑单个偏好值的读写工具,其持有某个key对应的偏好值
39 | *
40 | * @param keyName String
41 | * @param defaultValue T
42 | * @return IPreferenceReadWrite
43 | */
44 | abstract fun getSingleDataEditor(
45 | keyName: String,
46 | defaultValue: T,
47 | ): IPreferenceEditor
48 |
49 | /**
50 | * 将注册者自身(preference compose function)的状态记录下来,并返回注册者依赖的节点状态
51 | *
52 | * @param currentKey 注册者自身的key
53 | * @param currentState 注册者自身的状态
54 | * @param targetKey 注册者要依赖于哪个节点的key,如果为null,则依赖于根节点状态
55 | * @return 返回依赖的节点的状态,若targetKey为null,返回自身节点状态
56 | */
57 | fun getDependence(
58 | currentKey: String,
59 | currentState: Boolean,
60 | targetKey: String? = null
61 | ): DependenceNode {
62 | if (!dependenceTree.contains(currentKey)) {
63 | val node = DependenceNode(currentState, currentKey)
64 | dependenceTree.putIfAbsent(currentKey, node)
65 | }
66 | return targetKey?.let {
67 | dependenceTree[it]
68 | } ?: dependenceTree[currentKey]!!
69 | }
70 |
71 | /**
72 | * 注册并返回key的状态
73 | */
74 | fun registerDependence(
75 | key: String,
76 | state: Boolean,
77 | ): DependenceNode {
78 | if (!dependenceTree.contains(key)) {
79 | val node = DependenceNode(state, key)
80 | dependenceTree.putIfAbsent(key, node)
81 | }
82 | return dependenceTree[key]!!
83 | }
84 |
85 | /**
86 | * 获取某个key对应的状态
87 | */
88 | fun getDependence(
89 | key: String
90 | ): DependenceNode? {
91 | return dependenceTree[key]
92 | }
93 |
94 | /**
95 | * 获取某个key对应的状态,如不存在,返回默认启用状态
96 | */
97 | fun getDependenceNotEmpty(
98 | key: String?,
99 | enable: Boolean = true,
100 | ): DependenceNode {
101 | return dependenceTree[key] ?: DependenceNode(enable, "")
102 | }
103 |
104 |
105 | }
106 |
107 | class DependenceNode(
108 | enable: Boolean,
109 | val keyName: String,
110 | ) {
111 | val enableStateFlow: MutableStateFlow = MutableStateFlow(enable)
112 |
113 | // val enableState = mutableStateOf(enable)
114 | suspend fun setEnabled(enable: Boolean) {
115 | enableStateFlow.emit(enable)
116 | }
117 |
118 | companion object {
119 | const val rootName = "Pref_Dependence_Node_Root"
120 | }
121 | }
--------------------------------------------------------------------------------
/preference-mmkv-util/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/preference-mmkv-util/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
2 | plugins {
3 | alias(libs.plugins.android.library)
4 | alias(libs.plugins.kotlin.android)
5 | id("maven-publish")
6 | }
7 |
8 | android {
9 | namespace = "com.kiylx.libx.pref_component.mmkv_util"
10 | compileSdk = 34
11 |
12 | defaultConfig {
13 | minSdk = 24
14 |
15 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
16 | consumerProguardFiles("consumer-rules.pro")
17 | }
18 |
19 | buildTypes {
20 | release {
21 | isMinifyEnabled = false
22 | proguardFiles(
23 | getDefaultProguardFile("proguard-android-optimize.txt"),
24 | "proguard-rules.pro"
25 | )
26 | }
27 | }
28 | compileOptions {
29 | sourceCompatibility = JavaVersion.VERSION_1_8
30 | targetCompatibility = JavaVersion.VERSION_1_8
31 | }
32 | kotlinOptions {
33 | jvmTarget = "1.8"
34 | }
35 | publishing {
36 | singleVariant("release") {
37 | withSourcesJar()
38 | withJavadocJar()
39 | }
40 | }
41 | }
42 |
43 | dependencies {
44 | implementation(libs.kotlin.coroutines.core)
45 | compileOnly(project(":preference-data-core"))
46 | implementation(libs.github.mmkv)
47 | }
48 | afterEvaluate {
49 | publishing {
50 | publications {
51 | create("release") {
52 | groupId = "com.github.knightwood"
53 | artifactId = "preference-mmkv-util"
54 | version = rootProject.ext["version"].toString()
55 | afterEvaluate {
56 | from(components["release"])
57 | }
58 | pom {
59 | name.set("preference-mmkv-util")
60 | description.set("preference mmkv util")
61 | url.set("https://github.com/Knightwood/ComposePreference")
62 | licenses {
63 | license {
64 | name.set("The Apache License, Version 2.0")
65 | url.set("https://www.apache.org/licenses/LICENSE-2.0.txt")
66 | }
67 | }
68 | developers {
69 | developer {
70 | id.set("knightwood")
71 | name.set("KnightWood")
72 | email.set("33772264+Knightwood@users.noreply.github.com")
73 | }
74 | }
75 | }
76 | }
77 | }
78 | }
79 | }
--------------------------------------------------------------------------------
/preference-mmkv-util/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/preference-mmkv-util/consumer-rules.pro
--------------------------------------------------------------------------------
/preference-mmkv-util/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/preference-mmkv-util/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/preference-mmkv-util/src/main/java/com/kiylx/libx/pref_component/mmkv_util/MMKVEditor.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.libx.pref_component.mmkv_util
19 |
20 | import android.os.Parcelable
21 | import com.kiylx.libx.pref_component.core.IPreferenceEditor
22 | import com.tencent.mmkv.MMKV
23 | import kotlinx.coroutines.flow.Flow
24 | import kotlinx.coroutines.flow.MutableSharedFlow
25 |
26 | /**
27 | * 提供偏好值的读写,MMKV实现功能版本
28 | */
29 | class MMKVEditor(
30 | val kv: MMKV,
31 | val keyName: String,
32 | val defaultValue: T,
33 | ) : IPreferenceEditor {
34 | val TAG = "mmkv_tool"
35 |
36 | private val flow: MutableSharedFlow = MutableSharedFlow(1)
37 | var readWrite: MmkvUtil = (when (defaultValue) {
38 | is Int -> {
39 | kv.intRW(keyName, defaultValue)
40 | }
41 |
42 | is Boolean -> {
43 | kv.boolRW(keyName, defaultValue)
44 | }
45 |
46 | is String -> {
47 | kv.strRW(keyName, defaultValue)
48 | }
49 |
50 | is Double -> {
51 | kv.doubleRW(keyName, defaultValue)
52 | }
53 |
54 | is Float -> {
55 | kv.floatRW(keyName, defaultValue)
56 | }
57 |
58 | is Long -> {
59 | kv.longRW(keyName, defaultValue)
60 | }
61 |
62 | is Parcelable -> {
63 | kv.parcelableRW(keyName, defaultValue)
64 | }
65 |
66 | else -> {
67 | throw IllegalArgumentException("not support")
68 | }
69 | }) as MmkvUtil
70 |
71 | init {
72 | flow.tryEmit(readWrite.read())
73 | }
74 |
75 | override fun flow(): Flow {
76 | return flow
77 | }
78 |
79 | override fun readValue(): T {
80 | return readWrite.read()
81 | }
82 |
83 | override suspend fun write(data: T) {
84 | readWrite.write(data)
85 | flow.emit(data)
86 | }
87 | }
--------------------------------------------------------------------------------
/preference-mmkv-util/src/main/java/com/kiylx/libx/pref_component/mmkv_util/MMKVPreferenceHolder.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.libx.pref_component.mmkv_util;
19 |
20 | import com.kiylx.libx.pref_component.core.IPreferenceEditor
21 | import com.kiylx.libx.pref_component.core.PreferenceHolder
22 | import com.tencent.mmkv.MMKV
23 |
24 | /**
25 | * 向界面提供、管理PreferenceProvider
26 | */
27 | class MMKVPreferenceHolder internal constructor(
28 | private val mmkv: MMKV
29 | ) : PreferenceHolder() {
30 |
31 | override fun getSingleDataEditor(
32 | keyName: String,
33 | defaultValue: T,
34 | ): IPreferenceEditor {
35 | return hashMap[keyName]?.let {
36 | it as IPreferenceEditor
37 | } ?: let {
38 | val tmp = MMKVEditor(mmkv, keyName, defaultValue)
39 | hashMap[keyName] = tmp
40 | tmp
41 | }
42 | }
43 |
44 | companion object {
45 | @Volatile
46 | var ps: MMKVPreferenceHolder? = null
47 | fun instance(
48 | mmkv: MMKV
49 | ): MMKVPreferenceHolder {
50 | return ps ?: synchronized(this) {
51 | ps ?: MMKVPreferenceHolder(mmkv).also { ps = it }
52 | }
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/preference-mmkv-util/src/main/java/com/kiylx/libx/pref_component/mmkv_util/MmkvUtil.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.libx.pref_component.mmkv_util
19 |
20 | import android.os.Parcelable
21 | import com.tencent.mmkv.MMKV
22 |
23 | /**
24 | * mmkv的读写工具,非委托版本
25 | */
26 | class MmkvUtil(
27 | val mmkv: MMKV,
28 | var key: String,
29 | var defaultValue: T,
30 | var getter: MMKV.(String, T) -> T,
31 | var setter: MMKV.(String, T) -> Boolean
32 | ) {
33 |
34 | fun read(): T {
35 | return mmkv.getter(key, defaultValue)
36 | }
37 |
38 | fun write(data: T) {
39 | mmkv.setter(key, data)
40 | }
41 | }
42 |
43 | fun MMKV.readWriteUtil(
44 | key: String,
45 | defaultValue: T,
46 | getter: MMKV.(String, T) -> T,
47 | setter: MMKV.(String, T) -> Boolean
48 | ): MmkvUtil = MmkvUtil(this, key, defaultValue, getter, setter)
49 |
50 | fun MMKV.strRW(
51 | key: String,
52 | defValue: String = "",
53 | ): MmkvUtil {
54 | return readWriteUtil(key, defValue, getter = { key1, def ->
55 | return@readWriteUtil this.decodeString(key1, def) ?: def
56 | }, MMKV::encode)
57 | }
58 |
59 | fun MMKV.intRW(
60 | key: String,
61 | defValue: Int = 0,
62 | ): MmkvUtil {
63 | return readWriteUtil(key, defValue, MMKV::decodeInt, MMKV::encode)
64 | }
65 |
66 | fun MMKV.boolRW(
67 | key: String,
68 | defValue: Boolean = false,
69 | ): MmkvUtil {
70 | return readWriteUtil(key, defValue, MMKV::decodeBool, MMKV::encode)
71 | }
72 |
73 | fun MMKV.longRW(
74 | key: String,
75 | defValue: Long = 0L,
76 | ): MmkvUtil {
77 | return readWriteUtil(key, defValue, MMKV::decodeLong, MMKV::encode)
78 | }
79 |
80 | fun MMKV.floatRW(
81 | key: String,
82 | defValue: Float = 0F,
83 | ): MmkvUtil {
84 | return readWriteUtil(key, defValue, MMKV::decodeFloat, MMKV::encode)
85 | }
86 |
87 | fun MMKV.doubleRW(
88 | key: String,
89 | defValue: Double = 0.0,
90 | ): MmkvUtil {
91 | return readWriteUtil(key, defValue, MMKV::decodeDouble, MMKV::encode)
92 | }
93 |
94 | fun MMKV.bytesRW(
95 | key: String,
96 | defValue: ByteArray = byteArrayOf(),
97 | ): MmkvUtil {
98 | return readWriteUtil(key, defValue, getter = { key1, def ->
99 | return@readWriteUtil this.decodeBytes(key1, def) ?: def
100 | }, MMKV::encode)
101 | }
102 |
103 | inline fun MMKV.parcelableRW(
104 | key: String,
105 | defValue: T,
106 | ): MmkvUtil {
107 | return readWriteUtil(key, defValue, getter = { key1, def ->
108 | return@readWriteUtil this.decodeParcelable(key1, T::class.java, def) ?: def
109 | }, MMKV::encode)
110 | }
111 |
112 |
113 |
--------------------------------------------------------------------------------
/preference-mmkv-util/src/main/java/com/kiylx/libx/pref_component/mmkv_util/delegate/MExt.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.libx.pref_component.mmkv_util.delegate
19 |
20 | import android.os.Parcelable
21 | import com.tencent.mmkv.MMKV
22 | import kotlin.properties.ReadWriteProperty
23 | import kotlin.reflect.KProperty
24 |
25 | /**
26 | * 使用方式:
27 | *
28 | * ```
29 | * class MMKVHelper private constructor(val mmkv: MMKV) {
30 | * //使用委托的方式生成一个委托对象,除了[parcelableM]方法,初始值可选
31 | * var name by mv.strM("tom","初始值")
32 | * }
33 | *
34 | * //1. 获取单例
35 | * val helper =MMKVHelper.getInstance(prefs)
36 | * //2. 使用赋值将值存入
37 | * helper.name="Tom"
38 | * //3. 直接使用即读取值,如果没有值写入,读取出来的会是默认值。
39 | * log.d(TAG,helper.name)*
40 | * ```
41 | */
42 | class MExt
43 |
44 | inline fun MMKV.delegate(
45 | key: String? = null,
46 | defaultValue: T,
47 | crossinline getter: MMKV.(String, T) -> T,
48 | crossinline setter: MMKV.(String, T) -> Boolean
49 | ): ReadWriteProperty = object : ReadWriteProperty {
50 |
51 | override fun getValue(thisRef: Any, property: KProperty<*>): T =
52 | getter(key ?: property.name, defaultValue)
53 |
54 | override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
55 | setter(key ?: property.name, value)
56 | }
57 | }
58 |
59 | /**
60 | * read write string
61 | *
62 | * @param key
63 | * @param defValue
64 | * @return
65 | */
66 | fun MMKV.strM(
67 | key: String,
68 | defValue: String = "",
69 | ): ReadWriteProperty {
70 | return delegate(key, defValue, getter = { key1, def ->
71 | return@delegate this.decodeString(key1, def) ?: def
72 | }, MMKV::encode)
73 | }
74 |
75 | fun MMKV.intM(
76 | key: String,
77 | defValue: Int = 0,
78 | ): ReadWriteProperty {
79 | return delegate(key, defValue, MMKV::decodeInt, MMKV::encode)
80 | }
81 |
82 | fun MMKV.boolM(
83 | key: String,
84 | defValue: Boolean = false,
85 | ): ReadWriteProperty {
86 | return delegate(key, defValue, MMKV::decodeBool, MMKV::encode)
87 | }
88 |
89 | fun MMKV.longM(
90 | key: String,
91 | defValue: Long = 0L,
92 | ): ReadWriteProperty {
93 | return delegate(key, defValue, MMKV::decodeLong, MMKV::encode)
94 | }
95 |
96 | fun MMKV.floatM(
97 | key: String,
98 | defValue: Float = 0F,
99 | ): ReadWriteProperty {
100 | return delegate(key, defValue, MMKV::decodeFloat, MMKV::encode)
101 | }
102 |
103 | fun MMKV.doubleM(
104 | key: String,
105 | defValue: Double = 0.0,
106 | ): ReadWriteProperty {
107 | return delegate(key, defValue, MMKV::decodeDouble, MMKV::encode)
108 | }
109 |
110 | fun MMKV.bytesM(
111 | key: String,
112 | defValue: ByteArray = byteArrayOf(),
113 | ): ReadWriteProperty {
114 | return delegate(key, defValue, getter = { key1, def ->
115 | return@delegate this.decodeBytes(key1, def) ?: def
116 | }, MMKV::encode)
117 | }
118 |
119 | inline fun MMKV.parcelableM(
120 | key: String,
121 | defValue: T,
122 | ): ReadWriteProperty {
123 | return delegate(key, defValue, getter = { key1, def ->
124 | return@delegate this.decodeParcelable(key1, T::class.java, def) ?: def
125 | }, MMKV::encode)
126 | }
127 |
128 |
129 |
--------------------------------------------------------------------------------
/preference-ui-compose/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/preference-ui-compose/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.library")
3 | id("org.jetbrains.kotlin.android")
4 | id("maven-publish")
5 | id("org.jetbrains.kotlin.plugin.compose")
6 | }
7 |
8 | android {
9 | namespace = "com.kiylx.compose.preference"
10 | compileSdk = 34
11 |
12 | defaultConfig {
13 | minSdk = 26
14 |
15 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
16 | consumerProguardFiles("consumer-rules.pro")
17 | }
18 |
19 | buildTypes {
20 | release {
21 | isMinifyEnabled = false //true会在打包后因为没有引用而导致class.jar为空
22 | proguardFiles(
23 | getDefaultProguardFile("proguard-android-optimize.txt"),
24 | "proguard-rules.pro"
25 | )
26 | }
27 | }
28 | compileOptions {
29 | sourceCompatibility = JavaVersion.VERSION_17
30 | targetCompatibility = JavaVersion.VERSION_17
31 | }
32 | kotlinOptions {
33 | jvmTarget = "17"
34 | }
35 | buildFeatures {
36 | compose = true
37 | }
38 | packagingOptions {
39 | resources {
40 | excludes += "/META-INF/{AL2.0,LGPL2.1}"
41 | }
42 | }
43 | publishing {
44 | singleVariant("release") {
45 | withSourcesJar()
46 | withJavadocJar()
47 | }
48 | }
49 | }
50 |
51 | dependencies {
52 | implementation(libs.github.knightwood.m3ColorUtilities)
53 | implementation(libs.kotlin.coroutines.core)
54 | implementation(platform(composeLibs.androidx.compose.bom))
55 | implementation(composeLibs.androidx.compose.material3)
56 | implementation("androidx.compose.ui:ui-tooling-preview")
57 | debugImplementation("androidx.compose.ui:ui-tooling")
58 |
59 | compileOnly(project(":preference-data-core"))
60 | }
61 |
62 | afterEvaluate {
63 | publishing {
64 | publications {
65 | create("release") {
66 | groupId = "com.github.knightwood"
67 | artifactId = "preference-ui-compose"
68 | version = rootProject.ext["version"].toString()
69 | afterEvaluate {
70 | from(components["release"])
71 | }
72 | pom {
73 | name.set("preference-ui-compose")
74 | description.set("A compose ui for preference")
75 | url.set("https://github.com/Knightwood/ComposePreference")
76 | licenses {
77 | license {
78 | name.set("The Apache License, Version 2.0")
79 | url.set("https://www.apache.org/licenses/LICENSE-2.0.txt")
80 | }
81 | }
82 | developers {
83 | developer {
84 | id.set("knightwood")
85 | name.set("KnightWood")
86 | email.set("33772264+Knightwood@users.noreply.github.com")
87 | }
88 | }
89 | }
90 | }
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/preference-ui-compose/consumer-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 | ##########################基本规则###############################
23 | # 1.不能混淆的
24 | # 被反射的元素,实体类,四大组件,jni调用的java方法
25 | # 1.1 不建议混淆的
26 | # 自定义控件,js调用的java方法,java的native方法等.
27 |
28 | #Java的反射,为什么不能混淆呢?因为代码混淆,类名、方法名、属性名都改变了,而反射它还是按照原来的名字去反射,结果只射出一个程序崩溃
29 | #注解用了反射,所以不能混淆。 不混淆任何包含native方法的类的类名以及native方法名,否则找不到本地方法。
30 | #四大组件不能混淆,因为AndroidManifest.xml文件中是完整的名字,其他应用程序访问组件时可能会用到类的包名加类名,如果经过混淆,可能会无法找到对应组件或者产生异常。
31 | #自定义view也是带了包名写在xml布局中,不能混淆
32 |
33 | # 2.修饰符:
34 | #libraryjars 声明lib jar文件
35 | #dontwarn 不提示警告 dontwarn是一个和keep可以说是形影不离,尤其是处理引入的library时.
36 | #引入的library可能存在一些无法找到的引用和其他问题,在build时可能会发出警告,
37 | #如果我们不进行处理,通常会导致build中止.
38 | #因此为了保证build继续,我们需要使用dontwarn处理这些我们无法解决的library的警告.
39 | #dontnote:指定不去输出打印该类产生的错误或遗漏
40 | # 3.混淆输出结果
41 | #混淆构建完成之后,会在 /build/outputs/mapping/release/ 目录下生成以下文件:
42 |
43 | # dump.txt
44 | # 说明 APK 内所有类文件的内部结构。
45 |
46 | # mapping.txt
47 | # 提供混淆前后的内容对照表,内容主要包含类、方法和类的成员变量。
48 |
49 | # seeds.txt
50 | # 罗列出未进行混淆处理的类和成员。
51 |
52 | # usage.txt
53 | # 罗列出从 APK 中移除的代码。
54 | # 4.retrace 脚本工具
55 | # 位于Android SDK 路径的 /tools/proguard/bin.
56 | # 结合上文提到的 mapping.txt 文件,就可以将混淆后的崩溃堆栈追踪信息还原成正常情况下的 StackTrace 信息
57 |
58 | # 参考: https://jebware.com/blog/?p=418
59 | # 默认情况,不使用任何keep相关指令: 既会压缩(例如,去除无用代码),也会混淆(例如,重命名事物)类和类成员
60 |
61 | #keep 不压缩,不混淆;即不用于类,也不用于成员
62 |
63 | #keepnames 压缩类及成员,但不混淆它们。也就是说,未使用的代码将被移除。剩下的代码则维持原状。
64 |
65 | #keepclassmembers 只保护类的成员不被压缩和混淆。也就是说,如果类未被使用,则删除类。
66 | # 如果类被使用,保留并重命名该类。类里的成员维持不变,仍然是之前的名字。
67 |
68 | #keepclassmembernames 只保留类中的成员,防止被混淆,成员没有引用会被移除
69 | # 这是最宽容的keep指令;它允许ProGuard完成几乎所有工作。
70 | # 移除未使用的类,剩下的类被重命名,类中未使用的成员将被移除,剩余的成员保留原来的名称。
71 |
72 | #keepclasseswithmembers 保留类和类中的成员,防止被混淆或移除,保留指明的成员
73 | # 与-keep作用一致。区别是它只适用于拥有类规范中所有成员的类。
74 |
75 | #keepclasseswithmembernames 保留类和类中的成员,防止被混淆,保留指明的成员,成员没有引用会被移除
76 | # 这条规则与-keepnames一致。区别也是它只适用于拥有类规范中所有成员的类。
77 |
78 | #通配符
79 |
80 | # *号: 匹配任意长度字符,但不包含分隔符“.”。比如我们的完整类名是 com.kiylx.test.MyActivity
81 | #使用com.* ,或是com.kiylx.* 都是无法匹配的,因为* 无法匹配包名中的分隔符,正确的匹配方式是:
82 | #com.kiylx.*.* ,或者com.kiylx.test.* 。如果不写其他内容,只有一个*,那就表示匹配所有东西
83 | # **号: 匹配任意长度字符,并且包含包名分隔符"." ,比如-dontwarn android.support.** 就可以匹配
84 | #android.support包下的所有内容,包括任意长度的子包
85 | #示例: *号和**号:
86 | #-keep class cn.hadcn.test.**
87 | #-keep class cn.hadcn.test.*
88 | #一颗星表示只是保持该包下的类名,而子包下的类名还是会被混淆;两颗星表示把本包和所含子包下的类名都保持;
89 | #用以上方法保持类后,你会发现类名虽然未混淆,但里面的具体方法和变量命名还是变了,这时如果既想保持类名,又想保持里面的内容不被混淆,我们就需要以下方法了:
90 | #-keep class cn.hadcn.test.* {*;}
91 | #***号:
92 | #匹配任意参数类型。比如void set*(***)就能匹配人已传入的参数类型,***get*()就能匹配任意返回值的类型
93 |
94 | ##########################基本规则###############################
95 |
96 |
97 | #############################################
98 | #
99 | # 对于一些基本指令的添加
100 | #
101 | #############################################
102 | # 代码混淆压缩比,在0~7之间,默认为5,一般不做修改
103 | -optimizationpasses 5
104 |
105 | # 混合时不使用大小写混合,混合后的类名为小写
106 | -dontusemixedcaseclassnames
107 |
108 | # 混淆时不记录日志
109 | -verbose
110 |
111 | # 指定不去忽略非公共库的类
112 | -dontskipnonpubliclibraryclasses
113 |
114 | # 指定不去忽略非公共库的类成员
115 | -dontskipnonpubliclibraryclassmembers
116 |
117 | # 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。
118 | -dontpreverify
119 |
120 | # 忽略警告
121 | #-ignorewarning
122 |
123 | # 优化不优化输入的类文件
124 | -dontoptimize
125 |
126 | # 抛出异常时保留代码行号 可以提升我们的 StackSource 查找效率
127 | -keepattributes SourceFile,LineNumberTable
128 |
129 | # 指定混淆是采用的算法,后面的参数是一个过滤器
130 | # 这个过滤器是谷歌推荐的算法,一般不做更改
131 | -optimizations !code/simplification/cast,!field/*,!class/merging/*
132 |
133 |
134 | #############################################
135 | #
136 | # Android开发中一些需要保留的公共部分
137 | #
138 | #############################################
139 | #避免混淆注解类
140 | #-dontwarn android.annotation
141 | -keepattributes *Annotation*
142 |
143 | # 避免混淆泛型
144 | -keepattributes Signature
145 |
146 | #避免混淆内部类
147 | -keepattributes InnerClasses
148 | # 保留我们使用的四大组件,自定义的Application等等这些类不被混淆
149 | # 因为这些子类都有可能被外部调用
150 | -keep public class * extends android.app.Activity
151 | -keep public class * extends android.app.Application
152 | -keep public class * extends android.app.Fragment
153 | -keep public class * extends android.app.Service
154 | -keep public class * extends android.content.BroadcastReceiver
155 | -keep public class * extends android.content.ContentProvider
156 | -keep public class * extends android.app.backup.BackupAgentHelper
157 | -keep public class * extends android.preference.Preference
158 | -keep public class * extends android.view.View
159 | #-keep public class com.android.vending.licensing.ILicensingService
160 |
161 | #androidx包使用混淆
162 | #-keepnames class com.google.android.material.** {*;}
163 | #-keepnames class androidx.** {*;}
164 | #-keepnames public class * extends androidx.**
165 | #-keepnames interface androidx.** {*;}
166 | #-dontwarn com.google.android.material.**
167 | #-dontnote com.google.android.material.**
168 | #-dontwarn androidx.**
169 |
170 | # 保留support下的所有类及其内部类
171 | #-keep class android.support.** {*;}
172 |
173 | # 保留继承的 support库
174 | #-keep public class * extends android.support.v4.**
175 | #-keep public class * extends android.support.v7.**
176 | #-keep public class * extends android.support.annotation.**
177 |
178 | # 保留R 资源id不被混淆 不使用反射不需要keep
179 | #-keep class **.R$* {*;}
180 |
181 | # 保留本地native方法不被混淆
182 | -keepclasseswithmembernames class * {
183 | native ;
184 | }
185 |
186 | # 保留在Activity中的方法参数是view的方法,
187 | # 这样以来我们在layout中写的onClick就不会被影响
188 | -keepclassmembers class * extends android.app.Activity{
189 | public void *(android.view.View);
190 | }
191 |
192 | # 保留枚举类不被混淆
193 | -keepclassmembers enum * {
194 | public static **[] values();
195 | public static ** valueOf(java.lang.String);
196 | }
197 |
198 | # 避免混淆序列化类
199 | # 不混淆Parcelable和它的实现子类,还有Creator成员变量
200 | -keep class * implements android.os.Parcelable {
201 | public static final android.os.Parcelable$Creator *;
202 | }
203 |
204 | # 不混淆Serializable和它的实现子类、其成员变量
205 | -keep public class * implements java.io.Serializable {*;}
206 | -keepclassmembers class * implements java.io.Serializable {
207 | static final long serialVersionUID;
208 | private static final java.io.ObjectStreamField[] serialPersistentFields;
209 | private void writeObject(java.io.ObjectOutputStream);
210 | private void readObject(java.io.ObjectInputStream);
211 | java.lang.Object writeReplace();
212 | java.lang.Object readResolve();
213 | }
214 |
215 | # 保留我们自定义控件(继承自View)不被混淆
216 | -keep public class * extends android.view.View{
217 | *** get*();
218 | void set*(***);
219 | public (android.content.Context);
220 | public (android.content.Context, android.util.AttributeSet);
221 | public (android.content.Context, android.util.AttributeSet, int);
222 | }
223 | -keepclasseswithmembers class * {
224 | public (android.content.Context, android.util.AttributeSet);
225 | public (android.content.Context, android.util.AttributeSet, int);
226 | }
227 |
228 | # webview 混淆
229 | # Webview 相关不混淆
230 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
231 | # public *;
232 | #}
233 | #-keepclassmembers class * extends android.webkit.WebViewClient {
234 | # public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
235 | # public boolean *(android.webkit.WebView, java.lang.String);
236 | #}
237 | #-keepclassmembers class * extends android.webkit.WebViewClient {
238 | # public void *(android.webkit.WebView, java.lang.String);
239 | # }
240 |
241 | # 使用GSON、fastjson等框架时,所写的JSON对象类不混淆,否则无法将JSON解析成对应的对象
242 | #-keepclassmembers class * {
243 | # public (org.json.JSONObject);
244 | #}
245 |
246 | ####################################################
247 | #
248 | # 其他
249 | #
250 | ####################################################
251 |
252 | #禁用日志打印
253 | -assumenosideeffects class android.util.Log {
254 | public static *** v(...);
255 | public static *** d(...);
256 | public static *** i(...);
257 | public static *** w(...);
258 | public static *** e(...);
259 | }
260 |
261 | #kotlin serialization 如果要序列化具有命名伴随对象的类,详情: https://github.com/Kotlin/kotlinx.serialization#android
262 | ##############################################kotlin serialization######################################
263 | # Keep `Companion` object fields of serializable classes.
264 | # This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
265 | -if @kotlinx.serialization.Serializable class **
266 | -keepclassmembers class <1> {
267 | static <1>$Companion Companion;
268 | }
269 |
270 | # Keep `serializer()` on companion objects (both default and named) of serializable classes.
271 | -if @kotlinx.serialization.Serializable class ** {
272 | static **$* *;
273 | }
274 | -keepclassmembers class <2>$<3> {
275 | kotlinx.serialization.KSerializer serializer(...);
276 | }
277 |
278 | # Keep `INSTANCE.serializer()` of serializable objects.
279 | -if @kotlinx.serialization.Serializable class ** {
280 | public static ** INSTANCE;
281 | }
282 | -keepclassmembers class <1> {
283 | public static <1> INSTANCE;
284 | kotlinx.serialization.KSerializer serializer(...);
285 | }
286 |
287 | # @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
288 | -keepattributes RuntimeVisibleAnnotations,AnnotationDefault
289 |
290 | # Don't print notes about potential mistakes or omissions in the configuration for kotlinx-serialization classes
291 | # See also https://github.com/Kotlin/kotlinx.serialization/issues/1900
292 | -dontnote kotlinx.serialization.**
293 |
294 | # Serialization core uses `java.lang.ClassValue` for caching inside these specified classes.
295 | # If there is no `java.lang.ClassValue` (for example, in Android), then R8/ProGuard will print a warning.
296 | # However, since in this case they will not be used, we can disable these warnings
297 | -dontwarn kotlinx.serialization.internal.ClassValueReferences
298 |
299 | #############################################kotlin serialization######################################
300 |
301 |
302 |
--------------------------------------------------------------------------------
/preference-ui-compose/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/component/auto/NodeBase.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.component.auto
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.State
5 | import androidx.compose.runtime.collectAsState
6 | import androidx.compose.runtime.rememberCoroutineScope
7 | import kotlinx.coroutines.CoroutineScope
8 | import kotlinx.coroutines.launch
9 |
10 |
11 | @Composable
12 | fun PreferenceNodeBase(
13 | keyName: String,
14 | enabled: Boolean,
15 | dependenceKey: String?,
16 | defaultValue: T,
17 | content: @Composable (
18 | scope: CoroutineScope,
19 | depState: Boolean,
20 | valueProvider: () -> T,
21 | writer: (T) -> Unit,
22 | ) -> Unit
23 | ) {
24 | val scope = rememberCoroutineScope()
25 | val prefStoreHolder = LocalPrefs.current
26 | val pref = prefStoreHolder.getSingleDataEditor(keyName = keyName, defaultValue = defaultValue)
27 | //注册自身节点,并且获取目标节点的状态
28 | val dependenceState: State = prefStoreHolder.getDependence(
29 | keyName,
30 | enabled,
31 | dependenceKey
32 | ).enableStateFlow.collectAsState()
33 |
34 | val currentValue = pref.flow().collectAsState(defaultValue)
35 | content(
36 | scope, dependenceState.value,
37 | { currentValue.value },
38 | { scope.launch { pref.write(it) } }
39 | )
40 | }
41 |
42 | @Composable
43 | fun PreferenceNodeBase(
44 | enabled: Boolean,
45 | dependenceKey: String?,
46 | content: @Composable (
47 | scope: CoroutineScope,
48 | depState: Boolean,
49 | ) -> Unit
50 | ) {
51 | val scope = rememberCoroutineScope()
52 | val prefStoreHolder = LocalPrefs.current
53 | //不注册自身节点,仅获取目标节点的状态
54 | val dependenceState =
55 | prefStoreHolder.getDependenceNotEmpty(
56 | dependenceKey,
57 | enabled
58 | ).enableStateFlow.collectAsState()
59 |
60 | content(
61 | scope,
62 | dependenceState.value,
63 | )
64 |
65 |
66 | }
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/component/auto/PreferenceItem.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.component.auto
2 |
3 | import android.util.Log
4 | import androidx.compose.foundation.layout.BoxScope
5 | import androidx.compose.foundation.layout.PaddingValues
6 | import androidx.compose.foundation.shape.RoundedCornerShape
7 | import androidx.compose.material.icons.Icons
8 | import androidx.compose.material.icons.filled.KeyboardArrowDown
9 | import androidx.compose.material.icons.filled.KeyboardArrowUp
10 | import androidx.compose.material3.MaterialTheme
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.runtime.LaunchedEffect
13 | import androidx.compose.ui.Modifier
14 | import androidx.compose.ui.graphics.Color
15 | import androidx.compose.ui.graphics.Shape
16 | import androidx.compose.ui.unit.dp
17 | import com.kiylx.compose.preference.ui.ParseIcon
18 | import com.kiylx.compose.preference.ui.harmonizeWithPrimary
19 | import com.kiylx.compose.preference.component.cross.PreferenceCollapseItem as FossPreferenceCollapseItem
20 | import com.kiylx.compose.preference.component.cross.PreferenceItem as FossPreferenceItem
21 | import com.kiylx.compose.preference.component.cross.PreferencesCautionCard as FossPreferencesCautionCard
22 |
23 | private const val TAG = "PreferenceItem"
24 |
25 | @Composable
26 | fun PreferenceItem(
27 | modifier: Modifier = Modifier,
28 | title: String,
29 | icon: Any? = null,
30 | desc: String? = null,
31 | enabled: Boolean = true,
32 | dependenceKey: String?,
33 | end: @Composable (BoxScope.() -> Unit)? = null,
34 | onClick: () -> Unit = {},
35 | ) {
36 | PreferenceNodeBase(dependenceKey = dependenceKey, enabled = enabled) { scope, state ->
37 | LaunchedEffect(state) {
38 | Log.d(TAG, "依赖状态:$state ")
39 | }
40 | FossPreferenceItem(
41 | modifier = modifier,
42 | title = title, icon = icon, desc = desc,
43 | enabled = state, onClick = onClick,
44 | end = end,
45 | )
46 | }
47 | }
48 |
49 | @Composable
50 | fun PreferenceCollapseItem(
51 | modifier: Modifier = Modifier,
52 | title: String,
53 | desc: String? = null,
54 | enabled: Boolean = true,
55 | dependenceKey: String?,
56 | expand: Boolean = false,
57 | end: @Composable (BoxScope.() -> Unit) = {
58 | ParseIcon(
59 | model = if (!expand) Icons.Default.KeyboardArrowDown else Icons.Default.KeyboardArrowUp,
60 | contentDescription = "icon"
61 | )
62 | },
63 | stateChanged: (expand: Boolean) -> Unit = {},
64 | content: @Composable BoxScope.() -> Unit
65 | ) {
66 | PreferenceNodeBase(dependenceKey = dependenceKey, enabled = enabled) { scope, state ->
67 | FossPreferenceCollapseItem(
68 | modifier = modifier,
69 | title = title,
70 | desc = desc,
71 | enabled = state,
72 | expand = expand,
73 | end = end,
74 | stateChanged = stateChanged,
75 | content = content
76 | )
77 | }
78 | }
79 |
80 | @Composable
81 | fun PreferencesCautionCard(
82 | modifier: Modifier = Modifier,
83 | boxMarginValues: PaddingValues = PaddingValues(
84 | start = 16.dp,
85 | end = 16.dp
86 | ),
87 | color: Color = MaterialTheme.colorScheme.errorContainer.harmonizeWithPrimary(),
88 | shape: Shape = RoundedCornerShape(28.dp),
89 | title: String,
90 | icon: Any? = null,
91 | desc: String? = null,
92 | enabled: Boolean = true,
93 | dependenceKey: String?,
94 | end: @Composable (BoxScope.() -> Unit)? = null,
95 | onClick: () -> Unit = {},
96 | ) {
97 | PreferenceNodeBase(dependenceKey = dependenceKey, enabled = enabled) { scope, state ->
98 | FossPreferencesCautionCard(
99 | modifier = modifier,
100 | boxMarginValues = boxMarginValues,
101 | color = color,
102 | shape = shape,
103 | title = title,
104 | icon = icon,
105 | desc = desc,
106 | enabled = state,
107 | end = end,
108 | onClick = onClick
109 | )
110 | }
111 | }
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/component/auto/PreferenceSlider.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.component.auto
2 |
3 | import androidx.compose.foundation.layout.BoxScope
4 | import androidx.compose.foundation.layout.fillMaxHeight
5 | import androidx.compose.foundation.layout.padding
6 | import androidx.compose.foundation.layout.size
7 | import androidx.compose.material3.SliderColors
8 | import androidx.compose.material3.SliderDefaults
9 | import androidx.compose.material3.Text
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.runtime.getValue
12 | import androidx.compose.runtime.mutableStateOf
13 | import androidx.compose.runtime.remember
14 | import androidx.compose.runtime.setValue
15 | import androidx.compose.ui.Alignment
16 | import androidx.compose.ui.Modifier
17 | import androidx.compose.ui.unit.dp
18 | import com.kiylx.compose.preference.component.cross.PreferenceSlider as FossPreferenceSlider
19 |
20 | @Composable
21 | fun PreferenceSlider(
22 | modifier: Modifier = Modifier,
23 | keyName: String,
24 | dependenceKey: String?,
25 | icon: Any? = null,
26 | min: Float = 0F,
27 | max: Float = 100F,
28 | value: Float = min,
29 | steps: Int = 0,
30 | desc: String? = null,
31 | enabled: Boolean = true,
32 | colors: SliderColors = SliderDefaults.colors(),
33 | end: @Composable (BoxScope.(value: Float) -> Unit) = {
34 | Text(
35 | text = it.toString(),
36 | maxLines = 1,
37 | modifier = Modifier
38 | .align(Alignment.TopStart)
39 | .padding(top = 12.dp)
40 | .fillMaxHeight()
41 | .size(width = 46.dp, height = 24.dp)
42 | )
43 | },
44 | onValueChanged: (value: Float) -> Unit = {},
45 | changeFinished: () -> Unit = {},
46 | ) {
47 | PreferenceNodeBase(
48 | dependenceKey = dependenceKey,
49 | keyName = keyName, defaultValue = value,
50 | enabled = enabled
51 | ) { scope, state, provider, writer ->
52 | val prefValue = provider()
53 |
54 | var progress by remember {
55 | mutableStateOf(value)
56 | }
57 | remember(prefValue) {
58 | if (prefValue != progress)
59 | progress = prefValue
60 | onValueChanged.invoke(prefValue)
61 | provider
62 | }
63 |
64 | FossPreferenceSlider(
65 | modifier = modifier,
66 | icon = icon,
67 | min = min,
68 | max = max,
69 | value = progress,
70 | steps = steps,
71 | desc = desc,
72 | enabled = state,
73 | colors = colors,
74 | end = end,
75 | onValueChanged = {
76 | progress = it
77 | },
78 | changeFinished = {
79 | writer.invoke(progress)
80 | changeFinished()
81 | }
82 | )
83 | }
84 |
85 | }
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/component/auto/PreferenceSwitch.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.component.auto
2 |
3 | import androidx.compose.foundation.layout.PaddingValues
4 | import androidx.compose.foundation.shape.RoundedCornerShape
5 | import androidx.compose.material3.MaterialTheme
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.runtime.getValue
8 | import androidx.compose.runtime.mutableStateOf
9 | import androidx.compose.runtime.remember
10 | import androidx.compose.runtime.setValue
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.graphics.Color
13 | import androidx.compose.ui.graphics.Shape
14 | import androidx.compose.ui.unit.dp
15 | import com.kiylx.compose.preference.ui.harmonizeWithPrimary
16 | import com.kiylx.compose.preference.component.cross.PreferenceSwitch as FossPreferenceSwitch
17 | import com.kiylx.compose.preference.component.cross.PreferenceSwitchWithContainer as FossPreferenceSwitchWithContainer
18 | import com.kiylx.compose.preference.component.cross.PreferenceWithDividerSwitch as FossPreferenceWithDividerSwitch
19 |
20 |
21 | @Composable
22 | fun PreferenceSwitch(
23 | modifier: Modifier = Modifier,
24 | defaultValue: Boolean = false,
25 | title: String,
26 | dependenceKey: String?,
27 | keyName: String,
28 | icon: Any? = null,
29 | desc: String? = null,
30 | enabled: Boolean = true,
31 | onChecked: ((Boolean) -> Unit)? = null,
32 | ) {
33 | PreferenceNodeBase(
34 | dependenceKey = dependenceKey,
35 | keyName = keyName, defaultValue = defaultValue,
36 | enabled = enabled
37 | ) { scope, state, provider, writer ->
38 | val prefValue = provider()
39 |
40 | var checked by remember {
41 | mutableStateOf(defaultValue)
42 | }
43 | remember(prefValue) {
44 | if (prefValue != checked)
45 | checked = prefValue
46 | onChecked?.invoke(prefValue)
47 | provider
48 | }
49 |
50 | FossPreferenceSwitch(
51 | modifier = modifier,
52 | isChecked = checked,
53 | title = title,
54 | icon = icon,
55 | desc = desc,
56 | enabled = state,
57 | onChecked = {
58 | checked = it
59 | writer.invoke(it)
60 | }
61 | )
62 | }
63 | }
64 |
65 | @Composable
66 | fun PreferenceSwitchWithContainer(
67 | modifier: Modifier = Modifier,
68 | boxMarginValues: PaddingValues = PaddingValues(
69 | start = 16.dp,
70 | end = 16.dp
71 | ),
72 | color: Color = MaterialTheme.colorScheme.errorContainer.harmonizeWithPrimary(),
73 | shape: Shape = RoundedCornerShape(28.dp),
74 | defaultValue: Boolean,
75 | dependenceKey: String?,
76 | keyName: String,
77 | title: String,
78 | icon: Any? = null,
79 | desc: String? = null,
80 | enabled: Boolean = true,
81 | onChecked: ((Boolean) -> Unit)? = null,
82 | ) {
83 | PreferenceNodeBase(
84 | dependenceKey = dependenceKey,
85 | keyName = keyName, defaultValue = defaultValue,
86 | enabled = enabled
87 | ) { scope, state, provider, writer ->
88 | val prefValue = provider()
89 |
90 | var checked by remember {
91 | mutableStateOf(defaultValue)
92 | }
93 | remember(prefValue) {
94 | if (prefValue != checked)
95 | checked = prefValue
96 | onChecked?.invoke(prefValue)
97 | provider
98 | }
99 |
100 | FossPreferenceSwitchWithContainer(
101 | modifier = modifier, isChecked = checked,
102 | boxMarginValues = boxMarginValues, color = color, shape = shape,
103 | title = title, icon = icon, desc = desc, enabled = state,
104 | onChecked = {
105 | checked = it
106 | writer.invoke(it)
107 | },
108 | )
109 | }
110 | }
111 |
112 | @Composable
113 | fun PreferenceWithDividerSwitch(
114 | modifier: Modifier = Modifier,
115 | defaultValue: Boolean,
116 | dependenceKey: String?,
117 | keyName: String,
118 | title: String,
119 | icon: Any? = null,
120 | desc: String? = null,
121 | enabled: Boolean = true,
122 | onClick: (() -> Unit) = {},
123 | onChecked: ((Boolean) -> Unit)? = null,
124 | ) {
125 | PreferenceNodeBase(
126 | dependenceKey = dependenceKey,
127 | keyName = keyName, defaultValue = defaultValue,
128 | enabled = enabled
129 | ) { scope, state, provider, writer ->
130 | val prefValue = provider()
131 |
132 | var checked by remember {
133 | mutableStateOf(defaultValue)
134 | }
135 | remember(prefValue) {
136 | if (prefValue != checked)
137 | checked = prefValue
138 | onChecked?.invoke(prefValue)
139 | provider
140 | }
141 |
142 | FossPreferenceWithDividerSwitch(
143 | modifier = modifier,
144 | isChecked = checked,
145 | title = title,
146 | icon = icon,
147 | desc = desc,
148 | enabled = state,
149 | onClick = onClick,
150 | onChecked = {
151 | checked = it
152 | writer.invoke(it)
153 | }
154 | )
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/component/auto/PreferencesLocal.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.component.auto
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.CompositionLocalProvider
5 | import androidx.compose.runtime.compositionLocalOf
6 | import androidx.compose.runtime.remember
7 | import com.kiylx.compose.preference.theme.LocalPreferenceDimens
8 | import com.kiylx.compose.preference.theme.LocalPreferenceStyle
9 | import com.kiylx.compose.preference.theme.LocalPreferenceTextStyle
10 | import com.kiylx.compose.preference.theme.PreferenceBoxStyle
11 | import com.kiylx.compose.preference.theme.PreferenceDimen
12 | import com.kiylx.compose.preference.theme.PreferenceIconStyle
13 | import com.kiylx.compose.preference.theme.PreferenceStyle
14 | import com.kiylx.compose.preference.theme.PreferenceTextStyle
15 | import com.kiylx.compose.preference.theme.Preferences
16 | import com.kiylx.compose.preference.theme.defaultIconStyle
17 | import com.kiylx.compose.preference.theme.defaultPreferenceBoxStyle
18 | import com.kiylx.compose.preference.theme.defaultPreferenceTextStyle
19 | import com.kiylx.libx.pref_component.core.DefaultPreferenceHolder
20 | import com.kiylx.libx.pref_component.core.PreferenceHolder
21 |
22 |
23 | //持有偏好值
24 | val LocalPrefs = compositionLocalOf {
25 | DefaultPreferenceHolder.instance()
26 | }
27 |
28 |
29 | /**
30 | * 快速修改当前使用的preference各个样式
31 | *
32 | * @param boxStyleProvider
33 | * @param iconStyleProvider
34 | * @param textStyleProvider
35 | * @param dimenProvider
36 | * @param content
37 | */
38 | @Composable
39 | fun Preferences.CopyTheme(
40 | titleMaxLine: Int = 1,
41 | descMaxLine: Int = 2,
42 | boxStyleProvider: @Composable PreferenceBoxStyle.() -> PreferenceBoxStyle = { this },
43 | iconStyleProvider: @Composable PreferenceIconStyle.() -> PreferenceIconStyle = { this },
44 | textStyleProvider: @Composable PreferenceTextStyle.() -> PreferenceTextStyle = { this },
45 | dimenProvider: @Composable PreferenceDimen.() -> PreferenceDimen = { this },
46 | holder: PreferenceHolder = LocalPrefs.current,
47 | content: @Composable () -> Unit,
48 | ) {
49 | SetTheme(
50 | boxStyle = LocalPreferenceStyle.current.boxStyle.boxStyleProvider(),
51 | iconStyle = LocalPreferenceStyle.current.iconStyle.iconStyleProvider(),
52 | textStyle = LocalPreferenceTextStyle.current.textStyleProvider(),
53 | dimen = LocalPreferenceDimens.current.dimenProvider(),
54 | holder = holder,
55 | content = content, titleMaxLine = titleMaxLine, descMaxLine = descMaxLine
56 | )
57 | }
58 |
59 | /**
60 | * 设置preference样式
61 | *
62 | * @param boxStyle
63 | * @param iconStyle
64 | * @param textStyle
65 | * @param dimen
66 | * @param content
67 | */
68 | @Composable
69 | fun Preferences.SetTheme(
70 | titleMaxLine: Int = 1,
71 | descMaxLine: Int = 2,
72 | boxStyle: PreferenceBoxStyle = defaultPreferenceBoxStyle,
73 | iconStyle: PreferenceIconStyle = defaultIconStyle,
74 | textStyle: PreferenceTextStyle = defaultPreferenceTextStyle,
75 | dimen: PreferenceDimen = PreferenceDimen(),
76 | holder: PreferenceHolder = remember {
77 | DefaultPreferenceHolder.instance()
78 | },
79 | content: @Composable () -> Unit,
80 | ) {
81 | CompositionLocalProvider(
82 | LocalPreferenceDimens provides dimen,
83 | LocalPreferenceTextStyle provides textStyle,
84 | LocalPreferenceStyle provides PreferenceStyle(
85 | boxStyle,
86 | iconStyle,
87 | titleMaxLine,
88 | descMaxLine
89 | ),
90 | LocalPrefs provides holder,
91 | content = content
92 | )
93 | }
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/component/cross/PreferenceCheckBox.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.component.cross
2 |
3 | import androidx.compose.foundation.layout.BoxScope
4 | import androidx.compose.material3.Checkbox
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.Modifier
7 | import com.kiylx.compose.preference.ui.SamplePreference
8 |
9 | @Composable
10 | fun PreferenceCheckBox(
11 | modifier: Modifier = Modifier,
12 | title: String,
13 | desc: String? = null,
14 | checked: Boolean,
15 | enabled: Boolean = true,
16 | end: @Composable (BoxScope.() -> Unit)? = null,
17 | onChecked: (Boolean) -> Unit = {},
18 | ) = SamplePreference(
19 | modifier = modifier,
20 | title = title, desc = desc, icon = Any(), enabled = enabled,
21 | onClick = {
22 | onChecked(!checked)
23 | },
24 | start = {
25 | Checkbox(checked = checked, enabled = enabled, onCheckedChange = onChecked)
26 | },
27 | end = end,
28 | )
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/component/cross/PreferenceItem.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.component.cross
2 |
3 | import androidx.compose.animation.AnimatedVisibility
4 | import androidx.compose.animation.animateContentSize
5 | import androidx.compose.foundation.layout.Box
6 | import androidx.compose.foundation.layout.BoxScope
7 | import androidx.compose.foundation.layout.Column
8 | import androidx.compose.foundation.layout.PaddingValues
9 | import androidx.compose.foundation.shape.RoundedCornerShape
10 | import androidx.compose.material.icons.Icons
11 | import androidx.compose.material.icons.filled.KeyboardArrowDown
12 | import androidx.compose.material.icons.filled.KeyboardArrowUp
13 | import androidx.compose.material3.MaterialTheme
14 | import androidx.compose.material3.Text
15 | import androidx.compose.material3.contentColorFor
16 | import androidx.compose.runtime.Composable
17 | import androidx.compose.ui.Alignment
18 | import androidx.compose.ui.Modifier
19 | import androidx.compose.ui.graphics.Color
20 | import androidx.compose.ui.graphics.Shape
21 | import androidx.compose.ui.unit.dp
22 | import com.kiylx.compose.preference.theme.Preferences
23 | import com.kiylx.compose.preference.ui.ParseIcon
24 | import com.kiylx.compose.preference.ui.SamplePreference
25 | import com.kiylx.compose.preference.ui.harmonizeWithPrimary
26 |
27 |
28 | @Composable
29 | fun PreferenceSubTitle(
30 | modifier: Modifier = Modifier,
31 | paddingValues: PaddingValues = PaddingValues(
32 | start = 8.dp,
33 | end = 8.dp,
34 | top = 24.dp,
35 | bottom = 0.dp
36 | ),
37 | color: Color = Color.Transparent,
38 | title: String,
39 | icon: Any? = null,
40 | ) {
41 | Preferences.CopyTheme(
42 | dimenProvider = { copy(boxPaddingValues = paddingValues) },
43 | boxStyleProvider = { copy(color = color) },
44 | ) {
45 | SamplePreference(
46 | modifier = modifier,
47 | title = title,
48 | titleContent = {
49 | Text(
50 | title,
51 | style = Preferences.textStyle.categoryTextStyle,
52 | )
53 | },
54 | icon = icon, desc = null,
55 | enabled = false,
56 | onClick = null,
57 | end = null,
58 | )
59 | }
60 | }
61 |
62 | @Composable
63 | fun PreferenceItem(
64 | modifier: Modifier = Modifier,
65 | title: String,
66 | icon: Any? = null,
67 | desc: String? = null,
68 | enabled: Boolean = true,
69 | end: @Composable (BoxScope.() -> Unit)? = null,
70 | onClick: () -> Unit = {},
71 | ) = SamplePreference(
72 | modifier = modifier,
73 | title = title, icon = icon, desc = desc,
74 | enabled = enabled, onClick = onClick,
75 | end = end,
76 | )
77 |
78 | @Composable
79 | fun PreferenceCollapseItem(
80 | modifier: Modifier = Modifier,
81 | title: String,
82 | desc: String? = null,
83 | enabled: Boolean = true,
84 | expand: Boolean = false,
85 | end: @Composable (BoxScope.() -> Unit) = {
86 | ParseIcon(
87 | modifier = Modifier.align(Alignment.Center),
88 | model = if (!expand) Icons.Default.KeyboardArrowDown else Icons.Default.KeyboardArrowUp,
89 | contentDescription = "icon"
90 | )
91 | },
92 | stateChanged: (expand: Boolean) -> Unit = {},
93 | content: @Composable BoxScope.() -> Unit
94 | ) {
95 | Column(
96 | modifier = modifier
97 | .animateContentSize()
98 | ) {
99 | SamplePreference(
100 | modifier = modifier,
101 | title = title, icon = null, desc = desc,
102 | enabled = enabled, onClick = { stateChanged(!expand) },
103 | end = end,
104 | )
105 | AnimatedVisibility(visible = expand && enabled) {
106 | Box(content = content)
107 | }
108 | }
109 |
110 |
111 | }
112 |
113 | @Composable
114 | fun PreferencesCautionCard(
115 | modifier: Modifier = Modifier,
116 | boxMarginValues: PaddingValues = PaddingValues(
117 | start = 16.dp,
118 | end = 16.dp
119 | ),
120 | color: Color = MaterialTheme.colorScheme.errorContainer.harmonizeWithPrimary(),
121 | shape: Shape = RoundedCornerShape(28.dp),
122 | title: String,
123 | icon: Any? = null,
124 | desc: String? = null,
125 | enabled: Boolean = true,
126 | end: @Composable (BoxScope.() -> Unit)? = null,
127 | onClick: () -> Unit = {},
128 | ) {
129 | Preferences.CopyTheme(
130 | dimenProvider = { copy(boxMarginValues = boxMarginValues) },
131 | boxStyleProvider = { copy(color = color, shape = shape) }
132 | ) {
133 | SamplePreference(
134 | modifier = modifier,
135 | title = title, icon = icon, desc = desc,
136 | enabled = enabled, onClick = onClick,
137 | end = end,
138 | )
139 | }
140 |
141 | }
142 |
143 | @Composable
144 | fun PreferencesHintCard(
145 | modifier: Modifier = Modifier,
146 | boxMarginValues: PaddingValues = PaddingValues(
147 | start = 16.dp,
148 | end = 16.dp
149 | ),
150 | backgroundColor: Color = MaterialTheme.colorScheme.secondary,
151 | contentColor: Color = contentColorFor(backgroundColor),
152 | shape: Shape = RoundedCornerShape(28.dp),
153 | title: String,
154 | icon: Any? = null,
155 | desc: String? = null,
156 | enabled: Boolean = true,
157 | end: @Composable (BoxScope.() -> Unit)? = null,
158 | onClick: () -> Unit = {},
159 | ) {
160 | Preferences.CopyTheme(
161 | dimenProvider = { copy(boxMarginValues = boxMarginValues) },
162 | boxStyleProvider = { copy(color = backgroundColor, contentColor = contentColor, shape = shape) }
163 | ) {
164 | SamplePreference(
165 | modifier = modifier,
166 | title = title, icon = icon, desc = desc,
167 | enabled = enabled, onClick = onClick,
168 | end = end,
169 | )
170 | }
171 |
172 | }
173 |
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/component/cross/PreferenceRadio.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.component.cross
2 |
3 | import androidx.compose.foundation.layout.BoxScope
4 | import androidx.compose.foundation.layout.padding
5 | import androidx.compose.foundation.layout.size
6 | import androidx.compose.material3.RadioButton
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Alignment
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.unit.dp
11 | import com.kiylx.compose.preference.ui.SamplePreference
12 |
13 | @Composable
14 | fun PreferenceRadio(
15 | modifier: Modifier = Modifier,
16 | title: String,
17 | desc: String? = null,
18 | selected: Boolean,
19 | enabled: Boolean = true,
20 | end: @Composable (BoxScope.() -> Unit)? = null,
21 | onSelected: (Boolean) -> Unit = {},
22 | ) = SamplePreference(
23 | modifier = modifier,
24 | title = title, desc = desc, icon = Any(), enabled = enabled,
25 | onClick = {
26 | onSelected(!selected)
27 | },
28 | start = {
29 | RadioButton(
30 | selected = selected,
31 | enabled = enabled,
32 | onClick = { onSelected(!selected) })
33 | },
34 | end = end,
35 | )
36 |
37 | @Composable
38 | fun PreferenceRadioInvert(
39 | modifier: Modifier = Modifier,
40 | title: String,
41 | desc: String? = null,
42 | selected: Boolean,
43 | enabled: Boolean = true,
44 | end: @Composable (BoxScope.() -> Unit)? = null,
45 | onSelected: (Boolean) -> Unit = {},
46 | ) = SamplePreference(
47 | modifier = modifier,
48 | title = title, desc = desc, enabled = enabled,
49 | onClick = {
50 | onSelected(!selected)
51 | },
52 | end = {
53 | RadioButton(
54 | selected = selected,
55 | enabled = enabled,
56 | onClick = { onSelected(!selected) }
57 | )
58 | },
59 | )
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/component/cross/PreferenceSlider.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.component.cross
2 |
3 | import androidx.compose.foundation.layout.BoxScope
4 | import androidx.compose.foundation.layout.fillMaxHeight
5 | import androidx.compose.foundation.layout.padding
6 | import androidx.compose.foundation.layout.size
7 | import androidx.compose.foundation.layout.width
8 | import androidx.compose.foundation.layout.widthIn
9 | import androidx.compose.material3.Slider
10 | import androidx.compose.material3.SliderColors
11 | import androidx.compose.material3.SliderDefaults
12 | import androidx.compose.material3.Text
13 | import androidx.compose.runtime.Composable
14 | import androidx.compose.ui.Alignment
15 | import androidx.compose.ui.Modifier
16 | import androidx.compose.ui.unit.dp
17 | import com.kiylx.compose.preference.ui.SamplePreference
18 | import com.kiylx.compose.preference.ui.takeIf
19 |
20 | @Composable
21 | fun PreferenceSlider(
22 | modifier: Modifier = Modifier,
23 | icon: Any? = null,
24 | min: Float = 0F,
25 | max: Float = 100F,
26 | value: Float = min,
27 | steps: Int = 0,
28 | desc: String? = null,
29 | enabled: Boolean = true,
30 | colors: SliderColors = SliderDefaults.colors(),
31 | end: @Composable (BoxScope.(value: Float) -> Unit) = {
32 | Text(
33 | text = it.toString(),
34 | maxLines = 1,
35 | modifier = Modifier
36 | .let { m ->
37 | if (desc != null)
38 | m.align(Alignment.TopStart).padding(top = 12.dp)
39 | else m.align(Alignment.Center)
40 | }
41 | .width(46.dp)
42 | )
43 | },
44 | onValueChanged: (value: Float) -> Unit,
45 | changeFinished: () -> Unit = {},
46 | ) {
47 | SamplePreference(
48 | modifier = modifier,
49 | title = "",
50 | titleContent = {
51 | Slider(
52 | value = value,
53 | enabled = enabled,
54 | onValueChange = onValueChanged,
55 | colors = colors,
56 | steps = steps,
57 | valueRange = min..max,
58 | onValueChangeFinished = changeFinished,
59 | )
60 | },
61 | icon = icon, desc = desc, enabled = enabled, onClick = null,
62 | end = {
63 | end(value)
64 | },
65 | )
66 | }
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/component/cross/PreferenceSwitch.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.component.cross
2 |
3 | import androidx.compose.foundation.layout.IntrinsicSize
4 | import androidx.compose.foundation.layout.PaddingValues
5 | import androidx.compose.foundation.layout.Row
6 | import androidx.compose.foundation.layout.height
7 | import androidx.compose.foundation.layout.padding
8 | import androidx.compose.foundation.shape.RoundedCornerShape
9 | import androidx.compose.material3.MaterialTheme
10 | import androidx.compose.material3.VerticalDivider
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.ui.Alignment
13 | import androidx.compose.ui.Modifier
14 | import androidx.compose.ui.graphics.Color
15 | import androidx.compose.ui.graphics.Shape
16 | import androidx.compose.ui.unit.dp
17 | import com.kiylx.compose.preference.theme.Preferences
18 | import com.kiylx.compose.preference.ui.ComposeSwitch
19 | import com.kiylx.compose.preference.ui.SamplePreference
20 | import com.kiylx.compose.preference.ui.harmonizeWithPrimary
21 |
22 | @Composable
23 | fun PreferenceSwitch(
24 | modifier: Modifier = Modifier,
25 | isChecked: Boolean,
26 | title: String,
27 | icon: Any? = null,
28 | desc: String? = null,
29 | enabled: Boolean = true,
30 | onChecked: ((Boolean) -> Unit)? = null,
31 | ) {
32 | SamplePreference(
33 | modifier = modifier,
34 | title = title, icon = icon, desc = desc,
35 | enabled = enabled, onClick = {
36 | onChecked?.invoke(!isChecked)
37 | },
38 | end = {
39 | ComposeSwitch(isChecked = isChecked, onCheckedChange = onChecked)
40 | }
41 | )
42 | }
43 |
44 |
45 | @Composable
46 | fun PreferenceSwitchWithContainer(
47 | modifier: Modifier = Modifier,
48 | boxMarginValues: PaddingValues = PaddingValues(
49 | start = 16.dp,
50 | end = 16.dp
51 | ),
52 | color: Color = MaterialTheme.colorScheme.errorContainer.harmonizeWithPrimary(),
53 | shape: Shape = RoundedCornerShape(28.dp),
54 | isChecked: Boolean,
55 | title: String,
56 | icon: Any? = null,
57 | desc: String? = null,
58 | enabled: Boolean = true,
59 | onChecked: ((Boolean) -> Unit)? = null,
60 | ) {
61 | Preferences.CopyTheme(
62 | dimenProvider = { copy(boxMarginValues = boxMarginValues) },
63 | boxStyleProvider = { copy(color = color, shape = shape) }
64 | ) {
65 | PreferenceSwitch(
66 | modifier = modifier,
67 | isChecked = isChecked,
68 | title = title,
69 | icon = icon,
70 | desc = desc,
71 | enabled = enabled,
72 | onChecked = onChecked
73 | )
74 | }
75 |
76 | }
77 |
78 |
79 | @Composable
80 | fun PreferenceWithDividerSwitch(
81 | modifier: Modifier = Modifier,
82 | isChecked: Boolean,
83 | title: String,
84 | icon: Any? = null,
85 | desc: String? = null,
86 | enabled: Boolean = true,
87 | onClick: (() -> Unit) = {},
88 | onChecked: ((Boolean) -> Unit)? = null,
89 | ) {
90 | SamplePreference(
91 | modifier = modifier,
92 | title = title, icon = icon, desc = desc,
93 | enabled = enabled, onClick = onClick,
94 | end = {
95 | Row(
96 | modifier = Modifier
97 | .height(IntrinsicSize.Min)
98 | .align(Alignment.Center),
99 | verticalAlignment = Alignment.CenterVertically,
100 | ) {
101 | VerticalDivider(
102 | modifier = Modifier
103 | .height(24.dp)
104 | .padding(end = 16.dp),
105 | color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.3f),
106 | thickness = 2f.dp
107 | )
108 | ComposeSwitch(isChecked = isChecked, onCheckedChange = onChecked)
109 | }
110 | }
111 | )
112 |
113 | }
114 |
115 |
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/theme/PreferenceDimen.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.theme
2 |
3 | import androidx.compose.foundation.layout.PaddingValues
4 | import androidx.compose.runtime.Immutable
5 | import androidx.compose.runtime.compositionLocalOf
6 | import androidx.compose.ui.unit.Dp
7 | import androidx.compose.ui.unit.dp
8 |
9 | /**
10 | * 描述preference组件的padding
11 | *
12 | * @property boxMarginValues item surface的边距
13 | * @property boxPaddingValues item内row的边距
14 | * @property startPaddingValues item中的左侧内容的边距
15 | * @property contentPaddingValues 中间内容的边距
16 | * @property endPaddingValues item右侧内容的边距
17 | */
18 | @Immutable
19 | data class PreferenceDimen(
20 | val heightMin: Dp = 64.dp,
21 | val iconSize: Dp = 24.dp,
22 |
23 | val boxMarginValues: PaddingValues = PaddingValues(
24 | start = 12.dp,
25 | end = 12.dp,
26 | top = 0.dp,
27 | bottom = 0.dp
28 | ),
29 |
30 | val boxPaddingValues: PaddingValues = PaddingValues(
31 | start = 8.dp,
32 | end = 8.dp,
33 | top = 12.dp,
34 | bottom = 12.dp
35 | ),
36 |
37 | val startPaddingValues: PaddingValues = PaddingValues(
38 | start = 8.dp,
39 | end = 8.dp
40 | ),
41 | val contentPaddingValues: PaddingValues = PaddingValues(
42 | start = 8.dp,
43 | end = 4.dp,
44 | ),
45 | val endPaddingValues: PaddingValues = PaddingValues(
46 | start = 4.dp,
47 | end = 4.dp,
48 | ),
49 | )
50 |
51 | internal val LocalPreferenceDimens = compositionLocalOf {
52 | PreferenceDimen()
53 | }
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/theme/PreferenceStyle.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.theme
2 |
3 | import androidx.compose.foundation.BorderStroke
4 | import androidx.compose.foundation.layout.PaddingValues
5 | import androidx.compose.foundation.shape.CircleShape
6 | import androidx.compose.foundation.shape.RoundedCornerShape
7 | import androidx.compose.material3.LocalContentColor
8 | import androidx.compose.material3.MaterialTheme
9 | import androidx.compose.material3.contentColorFor
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.runtime.compositionLocalOf
12 | import androidx.compose.ui.graphics.Color
13 | import androidx.compose.ui.graphics.RectangleShape
14 | import androidx.compose.ui.graphics.Shape
15 | import androidx.compose.ui.unit.Dp
16 | import androidx.compose.ui.unit.dp
17 | import com.kiylx.compose.preference.ui.applyOpacity
18 |
19 | /**
20 | * @property boxStyle
21 | * @property iconStyle
22 | * @property descMaxLine 描述的最大行数
23 | */
24 | data class PreferenceStyle(
25 | val boxStyle: PreferenceBoxStyle,
26 | val iconStyle: PreferenceIconStyle,
27 | val titleMaxLine: Int = 1,
28 | val descMaxLine: Int = 2,
29 | )
30 |
31 | //
32 | data class PreferenceBoxStyle(
33 | val shape: Shape = RectangleShape,
34 | val color: Color,
35 | val contentColor: Color,
36 | val tonalElevation: Dp = 0.dp,
37 | val shadowElevation: Dp = 0.dp,
38 | val border: BorderStroke? = null,
39 | )
40 |
41 | val defaultPreferenceBoxStyle
42 | @Composable
43 | get() = PreferenceBoxStyle(
44 | color = MaterialTheme.colorScheme.surface,
45 | contentColor = contentColorFor(MaterialTheme.colorScheme.surface),
46 | shape = RoundedCornerShape(12.dp)
47 | )
48 |
49 | //
50 |
51 | //
52 | data class PreferenceIconStyle(
53 | val shape: Shape = CircleShape,
54 | val paddingValues: PaddingValues = PaddingValues(0.dp),
55 | val backgroundColor: Color = Color.Transparent,
56 | val tint: Color = Color.Unspecified
57 | ) {
58 | fun fixEnabledTint(enabled: Boolean): Color {
59 | return tint.applyOpacity(enabled)
60 | }
61 |
62 | fun fixEnabledBackgroundColor(enabled: Boolean): Color {
63 | return backgroundColor.applyOpacity(enabled)
64 | }
65 | }
66 |
67 | val defaultIconStyle
68 | @Composable
69 | get() = PreferenceIconStyle(
70 | tint = LocalContentColor.current
71 | )
72 |
73 | //
74 |
75 | internal val LocalPreferenceStyle = compositionLocalOf {
76 | PreferenceStyle(
77 | PreferenceBoxStyle(color = Color.Unspecified, contentColor = Color.Unspecified),
78 | PreferenceIconStyle()
79 | )
80 | }
81 |
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/theme/PreferenceTextStyle.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.theme
2 |
3 | import androidx.compose.material3.MaterialTheme
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.runtime.Immutable
6 | import androidx.compose.runtime.compositionLocalOf
7 | import androidx.compose.ui.text.TextStyle
8 | import androidx.compose.ui.text.font.FontWeight
9 | import androidx.compose.ui.unit.sp
10 | import com.kiylx.compose.preference.ui.applyOpacity
11 |
12 | @Immutable
13 | data class PreferenceTextStyle(
14 | val titleStyle: TextStyle = TextStyle.Default,
15 | val descriptionTextStyle: TextStyle = TextStyle.Default,
16 | val categoryTextStyle: TextStyle = TextStyle.Default,
17 | val labelTextStyle: TextStyle = TextStyle.Default,
18 | ) {
19 |
20 | }
21 |
22 | internal fun TextStyle.fixEnabledColor(enabled: Boolean): TextStyle {
23 | return if (enabled) this else this.copy(
24 | color = this.color.applyOpacity(false)
25 | )
26 | }
27 |
28 | internal val defaultPreferenceTextStyle
29 | @Composable
30 | get() = PreferenceTextStyle(
31 | titleStyle = MaterialTheme.typography.titleLarge.copy(fontSize = 20.sp),
32 | descriptionTextStyle = MaterialTheme.typography.bodyMedium,
33 | labelTextStyle = MaterialTheme.typography.labelSmall,
34 | categoryTextStyle = MaterialTheme.typography.bodyMedium.copy(
35 | color = MaterialTheme.colorScheme.primary,
36 | fontSize = 16.sp,
37 | fontWeight = FontWeight.Bold
38 | )
39 | )
40 |
41 | internal val LocalPreferenceTextStyle = compositionLocalOf {
42 | PreferenceTextStyle()
43 | }
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/theme/Preferences.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.theme
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.CompositionLocalProvider
5 |
6 | object Preferences {
7 | val style
8 | @Composable
9 | get() = LocalPreferenceStyle.current
10 |
11 | val dimens
12 | @Composable
13 | get() = LocalPreferenceDimens.current
14 |
15 | val textStyle
16 | @Composable
17 | get() =
18 | LocalPreferenceTextStyle.current
19 |
20 | val boxStyle
21 | @Composable
22 | get() = LocalPreferenceStyle.current.boxStyle
23 |
24 | val iconStyle
25 | @Composable
26 | get() = LocalPreferenceStyle.current.iconStyle
27 |
28 | /**
29 | * 快速修改当前使用的preference各个样式
30 | *
31 | * @param boxStyleProvider
32 | * @param iconStyleProvider
33 | * @param textStyleProvider
34 | * @param dimenProvider
35 | * @param content
36 | */
37 | @Composable
38 | fun CopyTheme(
39 | titleMaxLine: Int = 1,
40 | descMaxLine: Int = 2,
41 | boxStyleProvider: @Composable PreferenceBoxStyle.() -> PreferenceBoxStyle = { this },
42 | iconStyleProvider: @Composable PreferenceIconStyle.() -> PreferenceIconStyle = { this },
43 | textStyleProvider: @Composable PreferenceTextStyle.() -> PreferenceTextStyle = { this },
44 | dimenProvider: @Composable PreferenceDimen.() -> PreferenceDimen = { this },
45 | content: @Composable () -> Unit,
46 | ) {
47 | SetTheme(
48 | boxStyle = LocalPreferenceStyle.current.boxStyle.boxStyleProvider(),
49 | iconStyle = LocalPreferenceStyle.current.iconStyle.iconStyleProvider(),
50 | textStyle = LocalPreferenceTextStyle.current.textStyleProvider(),
51 | dimen = LocalPreferenceDimens.current.dimenProvider(),
52 | content = content, titleMaxLine = titleMaxLine, descMaxLine = descMaxLine
53 | )
54 | }
55 |
56 | /**
57 | * 设置preference样式
58 | *
59 | * @param boxStyle
60 | * @param iconStyle
61 | * @param textStyle
62 | * @param dimen
63 | * @param content
64 | */
65 | @Composable
66 | fun SetTheme(
67 | titleMaxLine: Int = 1,
68 | descMaxLine: Int = 2,
69 | boxStyle: PreferenceBoxStyle = defaultPreferenceBoxStyle,
70 | iconStyle: PreferenceIconStyle = defaultIconStyle,
71 | textStyle: PreferenceTextStyle = defaultPreferenceTextStyle,
72 | dimen: PreferenceDimen = PreferenceDimen(),
73 | content: @Composable () -> Unit,
74 | ) {
75 | CompositionLocalProvider(
76 | LocalPreferenceDimens provides dimen,
77 | LocalPreferenceTextStyle provides textStyle,
78 | LocalPreferenceStyle provides PreferenceStyle(
79 | boxStyle = boxStyle,
80 | iconStyle = iconStyle,
81 | titleMaxLine = titleMaxLine,
82 | descMaxLine = descMaxLine
83 | ),
84 | content = content
85 | )
86 | }
87 | }
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/ui/Color.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.ui
2 |
3 | import androidx.compose.material3.MaterialTheme
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.graphics.Color
6 | import androidx.compose.ui.graphics.toArgb
7 | import com.kiylx.common.lib_materialcolorutilities.blend.Blend
8 |
9 |
10 | fun Color.applyAlpha(alpha: Float): Color {
11 | return this.copy(alpha = alpha)
12 | }
13 |
14 | fun Color.applyOpacity(enabled: Boolean): Color {
15 | if (this == Color.Transparent)
16 | return this
17 | return if (enabled) this else this.applyAlpha(alpha = 0.62f)
18 | }
19 |
20 | @Composable
21 | fun Color.harmonizeWith(other: Color) =
22 | Color(Blend.harmonize(this.toArgb(), other.toArgb()))
23 |
24 | @Composable
25 | fun Color.harmonizeWithPrimary(): Color =
26 | this.harmonizeWith(other = MaterialTheme.colorScheme.primary)
27 |
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/ui/ComposeSwitch.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.ui
2 |
3 | import androidx.compose.foundation.layout.size
4 | import androidx.compose.material.icons.Icons
5 | import androidx.compose.material3.Icon
6 | import androidx.compose.material3.Switch
7 | import androidx.compose.material3.SwitchDefaults
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.graphics.vector.ImageVector
11 | import com.kiylx.compose.preference.ui.icons.Check
12 |
13 | @Composable
14 | fun ComposeSwitch(
15 | modifier: Modifier = Modifier,
16 | isChecked: Boolean,
17 | enabled: Boolean = true,
18 | icon: ImageVector = Icons.Default.Check,
19 | onCheckedChange: ((Boolean) -> Unit)? = null,
20 | ) {
21 | val thumbContent: (@Composable () -> Unit)? = if (isChecked) {
22 | {
23 | Icon(
24 | imageVector = icon,
25 | contentDescription = null,
26 | modifier = Modifier.size(SwitchDefaults.IconSize),
27 | )
28 | }
29 | } else {
30 | null
31 | }
32 | Switch(
33 | checked = isChecked,
34 | onCheckedChange = onCheckedChange,
35 | modifier = modifier,
36 | enabled = enabled,
37 | thumbContent = thumbContent
38 | )
39 | }
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/ui/Exts.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.ui
2 |
3 | import androidx.compose.ui.Modifier
4 | import kotlin.contracts.ExperimentalContracts
5 |
6 | /**
7 | * 先条件判断,如果为真,则对传入参数调用then并返回调用then的结果。如果为假,直接返回传入参数。
8 | * 相当于:
9 | * ```
10 | * return if(true) then(this) else this
11 | * ```
12 | * @param predicate
13 | * @param then
14 | * @return
15 | */
16 | @OptIn(ExperimentalContracts::class)
17 | @SinceKotlin("1.1")
18 | public inline fun T.takeIf(
19 | predicate: () -> Boolean,
20 | then: T.() -> T
21 | ): T {
22 | kotlin.contracts.contract {
23 | callsInPlace(predicate, kotlin.contracts.InvocationKind.EXACTLY_ONCE)
24 | }
25 | return if (predicate()) this.then() else this
26 | }
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/ui/ImageParser.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.ui
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.PaddingValues
5 | import androidx.compose.foundation.layout.padding
6 | import androidx.compose.foundation.layout.size
7 | import androidx.compose.foundation.layout.wrapContentSize
8 | import androidx.compose.foundation.shape.CircleShape
9 | import androidx.compose.material.icons.Icons
10 | import androidx.compose.material.icons.filled.Settings
11 | import androidx.compose.material3.Icon
12 | import androidx.compose.material3.MaterialTheme
13 | import androidx.compose.material3.Surface
14 | import androidx.compose.runtime.Composable
15 | import androidx.compose.ui.Modifier
16 | import androidx.compose.ui.graphics.Color
17 | import androidx.compose.ui.graphics.Shape
18 | import androidx.compose.ui.graphics.painter.Painter
19 | import androidx.compose.ui.graphics.vector.ImageVector
20 | import androidx.compose.ui.res.painterResource
21 | import androidx.compose.ui.tooling.preview.Preview
22 | import androidx.compose.ui.unit.dp
23 |
24 |
25 | /**
26 | * icon组件
27 | *
28 | * @param modifier icon外层surface的modifier
29 | * @param iconModifier icon的modifier
30 | * @param model ImageVector 或者 Painter 或者 resourceId
31 | * @param contentDescription
32 | * @param shape
33 | * @param paddingValues
34 | * @param backgroundColor
35 | * @param enabled
36 | * @param tint
37 | */
38 | @Composable
39 | fun ParseIcon(
40 | modifier: Modifier = Modifier,
41 | iconModifier: Modifier = Modifier,
42 | model: Any?,
43 | contentDescription: String? = null,
44 | shape: Shape = CircleShape,
45 | paddingValues: PaddingValues = PaddingValues(0.dp),
46 | backgroundColor: Color = Color.Transparent,
47 | enabled: Boolean = true,
48 | tint: Color = MaterialTheme.colorScheme.onSurfaceVariant.applyOpacity(enabled)
49 | ) {
50 | Surface(
51 | modifier = modifier.wrapContentSize(),
52 | color = backgroundColor,
53 | shape = shape,
54 | ) {
55 | when (model) {
56 | is ImageVector -> {
57 | Icon(
58 | imageVector = model,
59 | contentDescription = contentDescription,
60 | modifier = iconModifier.padding(paddingValues),
61 | tint = tint
62 | )
63 | }
64 |
65 | is Painter -> {
66 | Icon(
67 | painter = model,
68 | contentDescription = contentDescription,
69 | modifier = iconModifier.padding(paddingValues),
70 | tint = tint
71 | )
72 | }
73 |
74 | is Int -> {
75 | Icon(
76 | painter = painterResource(id = model),
77 | modifier = iconModifier.padding(paddingValues),
78 | contentDescription = contentDescription,
79 | tint = tint
80 | )
81 | }
82 | }
83 | }
84 |
85 | }
86 |
87 | @Preview()
88 | @Composable
89 | fun IconPreview() {
90 | Surface(modifier = Modifier.size(500.dp, 500.dp)) {
91 | Column(modifier = Modifier.padding(24.dp)) {
92 | ParseIcon(
93 | model = Icons.Filled.Settings,
94 | paddingValues = PaddingValues(8.dp),
95 | tint = MaterialTheme.colorScheme.onPrimary,
96 | backgroundColor = MaterialTheme.colorScheme.primary,
97 | contentDescription = "test",
98 | )
99 | }
100 |
101 | }
102 | }
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/ui/PreferenceLayout.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.ui
2 |
3 | import androidx.compose.foundation.clickable
4 | import androidx.compose.foundation.layout.Arrangement
5 | import androidx.compose.foundation.layout.Box
6 | import androidx.compose.foundation.layout.BoxScope
7 | import androidx.compose.foundation.layout.Column
8 | import androidx.compose.foundation.layout.ColumnScope
9 | import androidx.compose.foundation.layout.IntrinsicSize
10 | import androidx.compose.foundation.layout.Row
11 | import androidx.compose.foundation.layout.Spacer
12 | import androidx.compose.foundation.layout.fillMaxHeight
13 | import androidx.compose.foundation.layout.fillMaxWidth
14 | import androidx.compose.foundation.layout.height
15 | import androidx.compose.foundation.layout.heightIn
16 | import androidx.compose.foundation.layout.padding
17 | import androidx.compose.foundation.layout.size
18 | import androidx.compose.foundation.layout.sizeIn
19 | import androidx.compose.material.icons.Icons
20 | import androidx.compose.material.icons.filled.KeyboardArrowDown
21 | import androidx.compose.material.icons.filled.KeyboardArrowUp
22 | import androidx.compose.material3.Surface
23 | import androidx.compose.material3.Text
24 | import androidx.compose.material3.minimumInteractiveComponentSize
25 | import androidx.compose.runtime.Composable
26 | import androidx.compose.ui.Alignment
27 | import androidx.compose.ui.Modifier
28 | import androidx.compose.ui.unit.dp
29 | import com.kiylx.compose.preference.theme.Preferences
30 | import com.kiylx.compose.preference.theme.fixEnabledColor
31 |
32 | private const val TAG = "PreferenceLayout"
33 |
34 | @Composable
35 | fun SamplePreference(
36 | modifier: Modifier = Modifier,
37 | title: String? = null,
38 | icon: Any? = null,
39 | desc: String? = null,
40 | enabled: Boolean = true,
41 | end: @Composable (BoxScope.() -> Unit)? = null,
42 |
43 | start: @Composable BoxScope.() -> Unit = {
44 | val style = Preferences.iconStyle
45 | ParseIcon(
46 | tint = style.fixEnabledTint(enabled),
47 | model = icon,
48 | shape = style.shape,
49 | paddingValues = style.paddingValues,
50 | backgroundColor = style.fixEnabledBackgroundColor(enabled),
51 | contentDescription = "icon"
52 | )
53 | },
54 |
55 | titleContent: @Composable ColumnScope.(title: String) -> Unit = {
56 | Text(
57 | it,
58 | maxLines = Preferences.style.titleMaxLine,
59 | style = Preferences.textStyle.titleStyle.fixEnabledColor(enabled),
60 | )
61 | },
62 |
63 | descContent: @Composable ColumnScope.(desc: String) -> Unit = {
64 | Text(
65 | it,
66 | style = Preferences.textStyle.descriptionTextStyle.fixEnabledColor(enabled),
67 | maxLines = Preferences.style.descMaxLine,
68 | )
69 | },
70 |
71 | onClick: (() -> Unit)?,
72 | ) {
73 | PreferenceRow(
74 | modifier = modifier,
75 | enabled = enabled,
76 | onClick = onClick,
77 | start = if (icon != null) {
78 | start
79 | } else {
80 | null
81 | },
82 | title = if (title != null) {
83 | { titleContent(title) }
84 | } else null,
85 | description = if (desc != null) {
86 | { descContent(desc) }
87 | } else null,
88 | end = end
89 | )
90 | }
91 |
92 | @Composable
93 | fun PreferenceRow(
94 | modifier: Modifier = Modifier,
95 | enabled: Boolean = true,
96 | onClick: (() -> Unit)?,
97 | start: @Composable (BoxScope.() -> Unit)? = null,
98 | title: @Composable (ColumnScope.() -> Unit)? = null,
99 | description: @Composable (ColumnScope.() -> Unit)? = null,
100 | end: @Composable (BoxScope.() -> Unit)? = null,
101 | ) {
102 | val boxStyle = Preferences.boxStyle
103 |
104 | Surface(
105 | modifier = modifier
106 | .fillMaxWidth()
107 | .heightIn(Preferences.dimens.heightMin)
108 | .padding(Preferences.dimens.boxMarginValues),
109 | shape = boxStyle.shape,
110 | color = boxStyle.color,
111 | contentColor = boxStyle.contentColor,
112 | tonalElevation = boxStyle.tonalElevation,
113 | shadowElevation = boxStyle.shadowElevation,
114 | border = boxStyle.border
115 | ) {
116 | Row(
117 | modifier = Modifier
118 | .fillMaxWidth()
119 | .takeIf({ onClick != null }) {
120 | clickable(
121 | interactionSource = null,
122 | enabled = enabled,
123 | onClick = onClick!!,
124 | indication = rippleOrFallbackImplementation(),
125 | )
126 | }
127 | .padding(Preferences.dimens.boxPaddingValues)
128 | .height(IntrinsicSize.Min),
129 | verticalAlignment = Alignment.CenterVertically,
130 | horizontalArrangement = Arrangement.Start,
131 | ) {
132 | start?.let {
133 | Box(
134 | modifier = Modifier
135 | .padding(Preferences.dimens.startPaddingValues)
136 | .sizeIn(Preferences.dimens.iconSize),
137 | content = it
138 | )
139 | }
140 | Column(
141 | modifier = Modifier
142 | .padding(Preferences.dimens.contentPaddingValues)
143 | .weight(1f),
144 | horizontalAlignment = Alignment.Start,
145 | ) {
146 | title?.invoke(this)
147 | description?.invoke(this)
148 | }
149 | if (end != null) {
150 | Box(
151 | modifier = Modifier
152 | .minimumInteractiveComponentSize()
153 | .fillMaxHeight()
154 | .padding(Preferences.dimens.endPaddingValues),
155 | content = end
156 | )
157 | }
158 | }
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/ui/RippleClickable.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.ui
2 |
3 | import androidx.compose.foundation.Indication
4 | import androidx.compose.material3.ExperimentalMaterial3Api
5 | import androidx.compose.material3.LocalUseFallbackRippleImplementation
6 | import androidx.compose.material3.ripple
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.graphics.Color
9 | import androidx.compose.ui.unit.Dp
10 |
11 | class RippleClickable {
12 |
13 | }
14 |
15 | @Suppress("DEPRECATION_ERROR")
16 | @OptIn(ExperimentalMaterial3Api::class)
17 | @Composable
18 | internal fun rippleOrFallbackImplementation(
19 | bounded: Boolean = true,
20 | radius: Dp = Dp.Unspecified,
21 | color: Color = Color.Unspecified
22 | ): Indication {
23 | return if (LocalUseFallbackRippleImplementation.current) {
24 | androidx.compose.material.ripple.rememberRipple(bounded, radius, color)
25 | } else {
26 | ripple(bounded, radius, color)
27 | }
28 | }
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/ui/icons/ArrowDropDown.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.compose.preference.ui.icons
19 | import androidx.compose.material.icons.Icons
20 | import androidx.compose.material.icons.materialIcon
21 | import androidx.compose.material.icons.materialPath
22 | import androidx.compose.ui.graphics.vector.ImageVector
23 |
24 | public val Icons.Filled.ArrowDropDown: ImageVector
25 | get() {
26 | if (_arrowDropDown != null) {
27 | return _arrowDropDown!!
28 | }
29 | _arrowDropDown = materialIcon(name = "Filled.ArrowDropDown") {
30 | materialPath {
31 | moveTo(7.0f, 10.0f)
32 | lineToRelative(5.0f, 5.0f)
33 | lineToRelative(5.0f, -5.0f)
34 | close()
35 | }
36 | }
37 | return _arrowDropDown!!
38 | }
39 |
40 | private var _arrowDropDown: ImageVector? = null
41 |
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/ui/icons/ArrowDropUp.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.compose.preference.ui.icons
19 |
20 | import androidx.compose.material.icons.Icons
21 | import androidx.compose.material.icons.materialIcon
22 | import androidx.compose.material.icons.materialPath
23 | import androidx.compose.ui.graphics.vector.ImageVector
24 |
25 | public val Icons.Filled.ArrowDropUp: ImageVector
26 | get() {
27 | if (_arrowDropUp != null) {
28 | return _arrowDropUp!!
29 | }
30 | _arrowDropUp = materialIcon(name = "Filled.ArrowDropUp") {
31 | materialPath {
32 | moveTo(7.0f, 14.0f)
33 | lineToRelative(5.0f, -5.0f)
34 | lineToRelative(5.0f, 5.0f)
35 | close()
36 | }
37 | }
38 | return _arrowDropUp!!
39 | }
40 |
41 | private var _arrowDropUp: ImageVector? = null
42 |
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/ui/icons/Check.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.compose.preference.ui.icons
19 |
20 | import androidx.compose.material.icons.Icons
21 | import androidx.compose.material.icons.materialIcon
22 | import androidx.compose.material.icons.materialPath
23 | import androidx.compose.ui.graphics.vector.ImageVector
24 |
25 | public val Icons.Filled.Check: ImageVector
26 | get() {
27 | if (_check != null) {
28 | return _check!!
29 | }
30 | _check = materialIcon(name = "Filled.Check") {
31 | materialPath {
32 | moveTo(9.0f, 16.17f)
33 | lineTo(4.83f, 12.0f)
34 | lineToRelative(-1.42f, 1.41f)
35 | lineTo(9.0f, 19.0f)
36 | lineTo(21.0f, 7.0f)
37 | lineToRelative(-1.41f, -1.41f)
38 | close()
39 | }
40 | }
41 | return _check!!
42 | }
43 |
44 | private var _check: ImageVector? = null
45 |
--------------------------------------------------------------------------------
/preference-ui-compose/src/main/java/com/kiylx/compose/preference/ui/icons/EmptyIcon.kt:
--------------------------------------------------------------------------------
1 | package com.kiylx.compose.preference.ui.icons
2 |
3 | import androidx.compose.material.icons.Icons
4 | import androidx.compose.material.icons.materialIcon
5 | import androidx.compose.material.icons.materialPath
6 | import androidx.compose.ui.graphics.vector.ImageVector
7 |
8 | public val Icons.Filled.EmptyIcon: ImageVector
9 | get() {
10 | if (_empty_icon != null) {
11 | return _empty_icon!!
12 | }
13 | _empty_icon = materialIcon(name = "Filled.ErrorOutline") {
14 | materialPath {
15 |
16 | }
17 | }
18 | return _empty_icon!!
19 | }
20 |
21 | private var _empty_icon: ImageVector? = null
22 |
--------------------------------------------------------------------------------
/preference-util/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/preference-util/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
2 | plugins {
3 | alias(libs.plugins.android.library)
4 | alias(libs.plugins.kotlin.android)
5 | id("maven-publish")
6 |
7 | }
8 |
9 | android {
10 | namespace = "com.kiylx.libx.pref_component.preference_util"
11 | compileSdk = 34
12 |
13 | defaultConfig {
14 | minSdk = 24
15 |
16 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
17 | consumerProguardFiles("consumer-rules.pro")
18 | }
19 |
20 | buildTypes {
21 | release {
22 | isMinifyEnabled = false
23 | proguardFiles(
24 | getDefaultProguardFile("proguard-android-optimize.txt"),
25 | "proguard-rules.pro"
26 | )
27 | }
28 | }
29 | compileOptions {
30 | sourceCompatibility = JavaVersion.VERSION_1_8
31 | targetCompatibility = JavaVersion.VERSION_1_8
32 | }
33 | kotlinOptions {
34 | jvmTarget = "1.8"
35 | }
36 | publishing {
37 | singleVariant("release") {
38 | withSourcesJar()
39 | withJavadocJar()
40 | }
41 | }
42 | }
43 |
44 | dependencies {
45 | implementation(libs.androidx.preference)
46 | implementation(libs.kotlin.coroutines.core)
47 | compileOnly(project(":preference-data-core"))
48 | }
49 |
50 | afterEvaluate {
51 | publishing {
52 | publications {
53 | create("release") {
54 | groupId = "com.github.knightwood"
55 | artifactId = "preference-util"
56 | version = rootProject.ext["version"].toString()
57 | afterEvaluate {
58 | from(components["release"])
59 | }
60 | pom {
61 | name.set("preference-util")
62 | description.set("preference-util")
63 | url.set("https://github.com/Knightwood/ComposePreference")
64 | licenses {
65 | license {
66 | name.set("The Apache License, Version 2.0")
67 | url.set("https://www.apache.org/licenses/LICENSE-2.0.txt")
68 | }
69 | }
70 | developers {
71 | developer {
72 | id.set("knightwood")
73 | name.set("KnightWood")
74 | email.set("33772264+Knightwood@users.noreply.github.com")
75 | }
76 | }
77 | }
78 | }
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/preference-util/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Knightwood/ComposePreference/a39ef350056eacdea1a49faf1c7f39b4f8a77f08/preference-util/consumer-rules.pro
--------------------------------------------------------------------------------
/preference-util/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/preference-util/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/preference-util/src/main/java/com/kiylx/libx/pref_component/preference_util/OldPreferenceHolder.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.libx.pref_component.preference_util;
19 |
20 | import android.content.SharedPreferences
21 | import com.kiylx.libx.pref_component.core.IPreferenceEditor
22 | import com.kiylx.libx.pref_component.core.PreferenceHolder
23 |
24 | /**
25 | * 向界面提供、管理PreferenceProvider
26 | */
27 | class OldPreferenceHolder internal constructor(
28 | private val sp: SharedPreferences
29 | ) : PreferenceHolder() {
30 |
31 | override fun getSingleDataEditor(
32 | keyName: String,
33 | defaultValue: T,
34 | ): IPreferenceEditor {
35 | return hashMap[keyName]?.let {
36 | it as IPreferenceEditor
37 | } ?: let {
38 | val tmp = SPEditor(sp, keyName, defaultValue)
39 | hashMap[keyName] = tmp
40 | tmp
41 | }
42 | }
43 |
44 | companion object {
45 | @Volatile
46 | var ps: PreferenceHolder? = null
47 | fun instance(
48 | sp: SharedPreferences
49 | ): PreferenceHolder {
50 | return ps ?: synchronized(this) {
51 | ps ?: OldPreferenceHolder(sp)
52 | .also { ps = it }
53 | }
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/preference-util/src/main/java/com/kiylx/libx/pref_component/preference_util/PrefsUtil.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.libx.pref_component.preference_util
19 |
20 | import android.content.SharedPreferences
21 | import android.content.SharedPreferences.Editor
22 |
23 | class SharedPreferencesUtil(
24 | val sp: SharedPreferences,
25 | var key: String,
26 | var defaultValue: T,
27 | var getter: SharedPreferences.(String, T) -> T,
28 | var setter: Editor.(String, T) -> Editor
29 | ) {
30 |
31 | fun read(): T {
32 | return sp.getter(key, defaultValue)
33 | }
34 |
35 | fun write(data: T) {
36 | sp.edit().setter(key, data).apply()
37 | }
38 | }
39 |
40 | private fun SharedPreferences.delegate(
41 | key: String,
42 | defaultValue: T,
43 | getter: SharedPreferences.(String, T) -> T,
44 | setter: Editor.(String, T) -> Editor
45 | ): SharedPreferencesUtil = SharedPreferencesUtil(this, key, defaultValue, getter, setter)
46 |
47 | fun SharedPreferences.intRW(
48 | key: String,
49 | defValue: Int = 0,
50 |
51 | ): SharedPreferencesUtil {
52 | return delegate(key, defValue, SharedPreferences::getInt, Editor::putInt)
53 | }
54 |
55 | fun SharedPreferences.longRW(
56 | key: String,
57 | defValue: Long = 0,
58 |
59 | ): SharedPreferencesUtil {
60 | return delegate(key, defValue, SharedPreferences::getLong, Editor::putLong)
61 | }
62 |
63 | fun SharedPreferences.floatRW(
64 | key: String,
65 | defValue: Float = 0f,
66 | ): SharedPreferencesUtil {
67 | return delegate(key, defValue, SharedPreferences::getFloat, Editor::putFloat)
68 | }
69 |
70 | fun SharedPreferences.booleanRW(
71 | key: String,
72 | defValue: Boolean = false,
73 |
74 | ): SharedPreferencesUtil {
75 | return delegate(key, defValue, SharedPreferences::getBoolean, Editor::putBoolean)
76 | }
77 |
78 |
79 | fun SharedPreferences.stringSetRW(
80 | key: String,
81 | defValue: Set = emptySet(),
82 | ): SharedPreferencesUtil> {
83 | return delegate(key, defValue, { _, _ ->
84 | this.getStringSet(key, defValue) ?: defValue
85 | }, Editor::putStringSet)
86 |
87 | }
88 |
89 | fun SharedPreferences.stringRW(
90 | key: String,
91 | defValue: String = "",
92 |
93 | ): SharedPreferencesUtil {
94 |
95 | return delegate(key, defValue, { _, _ ->
96 | this.getString(key, defValue) ?: defValue
97 | }, Editor::putString)
98 |
99 | }
--------------------------------------------------------------------------------
/preference-util/src/main/java/com/kiylx/libx/pref_component/preference_util/SPEditor.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.libx.pref_component.preference_util
19 |
20 | import android.content.SharedPreferences
21 | import com.kiylx.libx.pref_component.core.IPreferenceEditor
22 | import kotlinx.coroutines.flow.Flow
23 | import kotlinx.coroutines.flow.MutableSharedFlow
24 |
25 | /**
26 | * 提供偏好值的读写,MMsp实现功能版本
27 | */
28 | class SPEditor(
29 | private val sp: SharedPreferences,
30 | val keyName: String,
31 | val defaultValue: T,
32 | ) : IPreferenceEditor {
33 | val TAG = "prefs_tool"
34 |
35 | private val flow: MutableSharedFlow = MutableSharedFlow(1)
36 | var readWrite: SharedPreferencesUtil = (when (defaultValue) {
37 | is Int -> {
38 | sp.intRW(keyName, defaultValue)
39 | }
40 |
41 | is Boolean -> {
42 | sp.booleanRW(keyName, defaultValue)
43 | }
44 |
45 | is String -> {
46 | sp.stringRW(keyName, defaultValue)
47 | }
48 |
49 | is Float -> {
50 | sp.floatRW(keyName, defaultValue)
51 | }
52 |
53 | is Long -> {
54 | sp.longRW(keyName, defaultValue)
55 | }
56 |
57 | is Set<*> -> {
58 | sp.stringSetRW(keyName)
59 | }
60 |
61 | else -> {
62 | throw IllegalArgumentException("not support")
63 | }
64 | }) as SharedPreferencesUtil
65 |
66 | init {
67 | flow.tryEmit(readWrite.read())
68 | }
69 |
70 | override fun flow(): Flow {
71 | return flow
72 | }
73 |
74 | override fun readValue(): T {
75 | return readWrite.read()
76 | }
77 |
78 | override suspend fun write(data: T) {
79 | readWrite.write(data)
80 | flow.emit(data)
81 | }
82 | }
--------------------------------------------------------------------------------
/preference-util/src/main/java/com/kiylx/libx/pref_component/preference_util/delegate/Prefs.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 [KnightWood]
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.kiylx.libx.pref_component.preference_util.delegate
19 |
20 | import android.content.Context
21 | import android.content.SharedPreferences
22 | import android.content.SharedPreferences.Editor
23 | import kotlin.properties.ReadWriteProperty
24 | import kotlin.reflect.KProperty
25 |
26 | /**
27 | * 使用
28 | *
29 | * ```
30 | * class PrefsHelper(prefs: SharedPreferences) {
31 | * var isFinish by prefs.boolean("isFinish")
32 | * var name by prefs.string("name")
33 | * var age by prefs.int("age")
34 | * }
35 | * ```
36 | *
37 | * 注意,实际使用过程中 PrefsHelper 应该是单例。
38 | */
39 | class Prefs private constructor(){
40 | companion object{
41 |
42 | }
43 | }
44 |
45 | private inline fun SharedPreferences.delegate(
46 | key: String? = null,
47 | defaultValue: T,
48 | crossinline getter: SharedPreferences.(String, T) -> T,
49 | crossinline setter: Editor.(String, T) -> Editor
50 | ): ReadWriteProperty =
51 | object : ReadWriteProperty {
52 | override fun getValue(thisRef: Any, property: KProperty<*>): T =
53 | this@delegate.getter(key ?: property.name, defaultValue)!!
54 |
55 | override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
56 | this@delegate.edit().setter(key ?: property.name, value).apply()
57 | }
58 | }
59 |
60 | fun SharedPreferences.int(key: String? = null, defValue: Int = 0): ReadWriteProperty {
61 | return delegate(key, defValue, SharedPreferences::getInt, Editor::putInt)
62 | }
63 |
64 | fun SharedPreferences.long(key: String? = null, defValue: Long = 0): ReadWriteProperty {
65 | return delegate(key, defValue, SharedPreferences::getLong, Editor::putLong)
66 | }
67 |
68 | fun SharedPreferences.float(
69 | key: String? = null,
70 | defValue: Float = 0f
71 | ): ReadWriteProperty {
72 | return delegate(key, defValue, SharedPreferences::getFloat, Editor::putFloat)
73 | }
74 |
75 | fun SharedPreferences.boolean(
76 | key: String? = null,
77 | defValue: Boolean = false
78 | ): ReadWriteProperty {
79 | return delegate(key, defValue, SharedPreferences::getBoolean, Editor::putBoolean)
80 | }
81 |
82 |
83 | fun SharedPreferences.stringSet(
84 | key: String? = null,
85 | defValue: Set = emptySet()
86 | ): ReadWriteProperty> {
87 | return delegate(key, defValue, { _, _ ->
88 | this.getStringSet(key, defValue) ?: defValue
89 | }, Editor::putStringSet)
90 |
91 | }
92 |
93 | fun SharedPreferences.string(
94 | key: String? = null,
95 | defValue: String = ""
96 | ): ReadWriteProperty {
97 | return delegate(key, defValue, { _, _ ->
98 | this.getString(key, defValue) ?: defValue
99 | }, Editor::putString)
100 | }
101 |
102 |
103 | /**
104 | * 获取sharedpreference的快捷方法
105 | */
106 | public fun getPreference(context: Context, name: String): SharedPreferences {
107 | return context.getSharedPreferences(name, Context.MODE_PRIVATE)
108 | }
109 |
110 | /**
111 | * 删除某个偏好值
112 | */
113 | public fun SharedPreferences.removePreference(name: String) {
114 | edit().remove(name).apply()
115 | }
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | gradlePluginPortal()
6 | maven("https://www.jitpack.io")
7 | }
8 | }
9 | dependencyResolutionManagement {
10 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
11 | repositories {
12 | google()
13 | mavenCentral()
14 | maven("https://www.jitpack.io")
15 | }
16 | versionCatalogs {
17 | create("composeLibs") {
18 | from(files("./gradle/composeLibs.versions.toml"))
19 | }
20 | }
21 | }
22 | rootProject.name = "ComposePreference"
23 | include(":app")
24 | include(":preference-ui-compose")
25 | include(":preference-data-core")
26 | include(":preference-mmkv-util")
27 | include(":preference-util")
28 | include(":datastore-util")
29 |
--------------------------------------------------------------------------------