From e8610856bb9dabda8024c32b373dbba6b3896b61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B1=9F=E8=BE=B0?= Date: Wed, 5 Jan 2022 15:38:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=AE=BE=E7=BD=AE=E9=A1=B9=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E6=8E=A7=E5=88=B6=E6=98=AF=E5=90=A6=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=EF=BC=8C=E6=96=B9=E4=BE=BF=E8=B0=83=E8=AF=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 玩了下 DataStore - 成功动态更新 koin 的依赖 --- .idea/misc.xml | 5 ++ app/build.gradle | 4 ++ app/src/main/java/com/wecom/BotFatherApp.kt | 57 +++++++++++++-- .../java/com/wecom/botfather/di/AppModules.kt | 31 ++++---- .../botfather/ui/settings/Preferences.kt | 70 +++++++++++++++++++ .../botfather/ui/settings/SettingsActivity.kt | 46 +++++++++++- 6 files changed, 188 insertions(+), 25 deletions(-) create mode 100644 app/src/main/java/com/wecom/botfather/ui/settings/Preferences.kt diff --git a/.idea/misc.xml b/.idea/misc.xml index 4c29de9..9173cf5 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -6,6 +6,11 @@ + + + + + diff --git a/app/build.gradle b/app/build.gradle index 2f880b0..a2c690c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -74,4 +74,8 @@ dependencies { // sql implementation("com.squareup.sqldelight:android-driver:1.5.3") + + // jetpack + implementation "androidx.datastore:datastore:1.0.0" + implementation "androidx.datastore:datastore-preferences:1.0.0" } \ No newline at end of file diff --git a/app/src/main/java/com/wecom/BotFatherApp.kt b/app/src/main/java/com/wecom/BotFatherApp.kt index bfcba5d..caa2120 100644 --- a/app/src/main/java/com/wecom/BotFatherApp.kt +++ b/app/src/main/java/com/wecom/BotFatherApp.kt @@ -1,15 +1,34 @@ package com.wecom import android.app.Application -import com.wecom.botfather.di.appModules +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.preferencesDataStore +import com.wecom.botfather.BuildConfig +import com.wecom.botfather.di.appModule +import com.wecom.botfather.di.serviceModule +import com.wecom.botfather.mock.MockService +import com.wecom.botfather.sdk.service.WeComService +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidLogger +import org.koin.core.Koin +import org.koin.core.context.KoinContext +import org.koin.core.context.loadKoinModules import org.koin.core.context.startKoin +import org.koin.core.context.unloadKoinModules +import org.koin.core.module.Module +import org.koin.dsl.module import timber.log.Timber typealias T = Timber -class BotFatherApp : Application() { +class BotFatherApp : Application(), CoroutineScope by MainScope() { override fun onCreate() { super.onCreate() @@ -17,9 +36,39 @@ class BotFatherApp : Application() { T.tag(this::class.java.simpleName) startKoin { + allowOverride(true) androidContext(this@BotFatherApp) androidLogger() - modules(appModules) + modules(appModule) + } + + launch { + // 监听变更 + dataStore.data.collectLatest { + loadServiceModule(it[ABORT_REQUEST_KEY] ?: BuildConfig.DEBUG) + } + } + } + + companion object { + private val debugModule = module { single { MockService() } } + + /** + * Koin 允许加载同类的 module,覆盖之前的 + */ + fun loadServiceModule(debug: Boolean) { + if (debug) { + loadKoinModules(debugModule) + } else { + unloadKoinModules(debugModule) + loadKoinModules(serviceModule) + } } } -} \ No newline at end of file +} + +// At the top level of your kotlin file: +val Context.dataStore: DataStore by preferencesDataStore(name = "settings") + +// abort api request +val ABORT_REQUEST_KEY = booleanPreferencesKey("abort_request") diff --git a/app/src/main/java/com/wecom/botfather/di/AppModules.kt b/app/src/main/java/com/wecom/botfather/di/AppModules.kt index 562b58a..4ddfc69 100644 --- a/app/src/main/java/com/wecom/botfather/di/AppModules.kt +++ b/app/src/main/java/com/wecom/botfather/di/AppModules.kt @@ -3,7 +3,6 @@ package com.wecom.botfather.di import com.squareup.sqldelight.android.AndroidSqliteDriver import com.squareup.sqldelight.db.SqlDriver import com.wecom.botfather.Database -import com.wecom.botfather.mock.MockService import com.wecom.botfather.sdk.WeComBotHelper import com.wecom.botfather.sdk.service.WeComService import com.wecom.botfather.ui.chat.ChatViewModel @@ -14,27 +13,12 @@ import org.koin.dsl.module import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory -const val debug = true - - val uiModules = module { viewModel { HomeViewModel(get()) } viewModel { ChatViewModel(get()) } } val sdkModules = module { - if (debug) { - // 使用日志打印代替请求 - single { MockService() } - } else { - single { - Retrofit.Builder() - .baseUrl("https://qyapi.weixin.qq.com/") - .addConverterFactory(GsonConverterFactory.create()) - .build() - .create(WeComService::class.java) - } - } single { AndroidSqliteDriver( @@ -48,8 +32,19 @@ val sdkModules = module { Database(get()) } - single { WeComBotHelper(get().botQueries) } + factory { WeComBotHelper(get().botQueries) } } -val appModules = sdkModules + uiModules +val serviceModule = module { + single { + Retrofit.Builder() + .baseUrl("https://qyapi.weixin.qq.com/") + .addConverterFactory(GsonConverterFactory.create()) + .build() + .create(WeComService::class.java) + } +} + +val appModule = uiModules + sdkModules + serviceModule + diff --git a/app/src/main/java/com/wecom/botfather/ui/settings/Preferences.kt b/app/src/main/java/com/wecom/botfather/ui/settings/Preferences.kt new file mode 100644 index 0000000..57c6f93 --- /dev/null +++ b/app/src/main/java/com/wecom/botfather/ui/settings/Preferences.kt @@ -0,0 +1,70 @@ +package com.wecom.botfather.ui.settings + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.material.Switch +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +@Composable +fun PreferenceWidget() { + TODO() +} + +/** + * 配合 Prefrence 使用,分组展示 + */ +@Composable +fun PreferenceGroup( + title: @Composable () -> Unit, + content: @Composable () -> Unit +) { + Column { + title() + content() + } +} + +@Composable +fun SwitchPreference( + title: @Composable () -> Unit, + checked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + modifier: Modifier = Modifier, + description: @Composable (() -> Unit)? = null, +) { + Row(modifier = modifier.fillMaxWidth().padding(16.dp)) { + Column(modifier = Modifier.weight(1f)) { + title() + if (description != null) { + Spacer(Modifier.size(4.dp)) + description() + } + } + Spacer(Modifier.size(4.dp)) + Switch( + checked = checked, + onCheckedChange = onCheckedChange, + modifier = Modifier.alignByBaseline() + ) + } +} + +@Preview(showBackground = true) +@Composable +fun SwitchPreferencePreview() { + SwitchPreference( + title = { Text("hahah") }, + description = { Text(text = "xxxx", fontSize = 12.sp, color = Color.Gray) }, + checked = true, + onCheckedChange = {}, + modifier = Modifier.clickable { + + } + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/wecom/botfather/ui/settings/SettingsActivity.kt b/app/src/main/java/com/wecom/botfather/ui/settings/SettingsActivity.kt index 43faf48..d6fa87a 100644 --- a/app/src/main/java/com/wecom/botfather/ui/settings/SettingsActivity.kt +++ b/app/src/main/java/com/wecom/botfather/ui/settings/SettingsActivity.kt @@ -5,10 +5,24 @@ import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.compose.foundation.clickable import androidx.compose.material.Scaffold import androidx.compose.material.Text import androidx.compose.material.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.sp +import androidx.datastore.preferences.core.edit +import com.wecom.ABORT_REQUEST_KEY +import com.wecom.botfather.BuildConfig +import com.wecom.dataStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch /** * TODO @@ -18,8 +32,18 @@ class SettingsActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + val prefValue = dataStore.data.map { + it[ABORT_REQUEST_KEY] ?: BuildConfig.DEBUG + } setContent { - SettingScreen() + val scope = rememberCoroutineScope() + SettingScreen(prefValue) { newValue -> + scope.launch { + dataStore.edit { + it[ABORT_REQUEST_KEY] = newValue + } + } + } } } @@ -34,7 +58,9 @@ class SettingsActivity : ComponentActivity() { } @Composable -private fun SettingScreen() { +private fun SettingScreen(prefValue: Flow, onValueChange: (Boolean) -> Unit) { + val abortRequest by prefValue.collectAsState(false) + Scaffold( topBar = { TopAppBar( @@ -42,7 +68,21 @@ private fun SettingScreen() { ) }, content = { - + SwitchPreference( + title = { Text("禁用请求") }, + description = { + Text( + text = "不进行网络请求,改为 Log 输出", + fontSize = 12.sp, + color = Color.Gray + ) + }, + checked = abortRequest, + onCheckedChange = onValueChange, + modifier = Modifier.clickable { + onValueChange(!abortRequest) + } + ) } ) } \ No newline at end of file