adding material you toggle
- meant moving the base preferences to the core module and allowing the activity to recreate itself if the theme congifuration has changed
This commit is contained in:
parent
f646da9d1e
commit
0c113896c1
|
@ -2,8 +2,8 @@ package app.dapk.st
|
|||
|
||||
import android.content.Context
|
||||
import app.dapk.st.core.CoroutineDispatchers
|
||||
import app.dapk.st.core.Preferences
|
||||
import app.dapk.st.core.withIoContext
|
||||
import app.dapk.st.domain.Preferences
|
||||
|
||||
internal class SharedPreferencesDelegate(
|
||||
context: Context,
|
||||
|
|
|
@ -96,7 +96,8 @@ internal class AppModule(context: Application, logger: MatrixLogger) {
|
|||
private val matrixModules = MatrixModules(storeModule, trackingModule, workModule, logger, coroutineDispatchers, context.contentResolver)
|
||||
val domainModules = DomainModules(matrixModules, trackingModule.errorTracker, workModule, storeModule, context, coroutineDispatchers)
|
||||
|
||||
val coreAndroidModule = CoreAndroidModule(intentFactory = object : IntentFactory {
|
||||
val coreAndroidModule = CoreAndroidModule(
|
||||
intentFactory = object : IntentFactory {
|
||||
override fun notificationOpenApp(context: Context) = PendingIntent.getActivity(
|
||||
context,
|
||||
1000,
|
||||
|
@ -121,7 +122,9 @@ internal class AppModule(context: Application, logger: MatrixLogger) {
|
|||
roomId,
|
||||
attachments
|
||||
)
|
||||
})
|
||||
},
|
||||
unsafeLazy { storeModule.value.preferences }
|
||||
)
|
||||
|
||||
val featureModules = FeatureModules(
|
||||
storeModule,
|
||||
|
@ -187,7 +190,8 @@ internal class FeatureModules internal constructor(
|
|||
matrixModules.sync,
|
||||
context.contentResolver,
|
||||
buildMeta,
|
||||
coroutineDispatchers
|
||||
coroutineDispatchers,
|
||||
coreAndroidModule.themeStore(),
|
||||
)
|
||||
}
|
||||
val profileModule by unsafeLazy { ProfileModule(matrixModules.profile, matrixModules.sync, matrixModules.room, trackingModule.errorTracker) }
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package app.dapk.st.core
|
||||
|
||||
interface Preferences {
|
||||
|
||||
suspend fun store(key: String, value: String)
|
||||
suspend fun readString(key: String): String?
|
||||
suspend fun clear()
|
||||
suspend fun remove(key: String)
|
||||
}
|
||||
|
||||
suspend fun Preferences.readBoolean(key: String) = this.readString(key)?.toBooleanStrict()
|
||||
suspend fun Preferences.store(key: String, value: Boolean) = this.store(key, value.toString())
|
|
@ -1,6 +1,5 @@
|
|||
package app.dapk.st.design.components
|
||||
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
|
@ -19,13 +18,13 @@ private val DARK_COLOURS = darkColorScheme(
|
|||
onPrimary = Color(0xDDFFFFFF),
|
||||
)
|
||||
|
||||
private val LIGHT_COLOURS = DARK_COLOURS
|
||||
private val DARK_EXTENDED = createExtended(DARK_COLOURS.primary, DARK_COLOURS.onPrimary)
|
||||
|
||||
private val DARK_EXTENDED = ExtendedColors(
|
||||
selfBubble = DARK_COLOURS.primary,
|
||||
onSelfBubble = DARK_COLOURS.onPrimary,
|
||||
private fun createExtended(primary: Color, onPrimary: Color) = ExtendedColors(
|
||||
selfBubble = primary,
|
||||
onSelfBubble = onPrimary,
|
||||
othersBubble = Color(0x20EDEDED),
|
||||
onOthersBubble = Color(0xFF000000),
|
||||
onOthersBubble = DARK_COLOURS.onPrimary,
|
||||
selfBubbleReplyBackground = Color(0x40EAEAEA),
|
||||
otherBubbleReplyBackground = Color(0x20EAEAEA),
|
||||
missingImageColors = listOf(
|
||||
|
@ -34,7 +33,6 @@ private val DARK_EXTENDED = ExtendedColors(
|
|||
Color(0xFFf6c8cb) to Color(0xFFda2535),
|
||||
)
|
||||
)
|
||||
private val LIGHT_EXTENDED = DARK_EXTENDED
|
||||
|
||||
@Immutable
|
||||
data class ExtendedColors(
|
||||
|
@ -51,21 +49,22 @@ data class ExtendedColors(
|
|||
}
|
||||
}
|
||||
|
||||
private val LocalExtendedColors = staticCompositionLocalOf { LIGHT_EXTENDED }
|
||||
private val LocalExtendedColors = staticCompositionLocalOf { DARK_EXTENDED }
|
||||
|
||||
@Composable
|
||||
fun SmallTalkTheme(content: @Composable () -> Unit) {
|
||||
fun SmallTalkTheme(themeConfig: ThemeConfig, content: @Composable () -> Unit) {
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val systemInDarkTheme = isSystemInDarkTheme()
|
||||
MaterialTheme(
|
||||
colorScheme = dynamicDarkColorScheme(LocalContext.current)
|
||||
// colorScheme = if (systemInDarkTheme) DARK_COLOURS else LIGHT_COLOURS,
|
||||
) {
|
||||
val colorScheme = if (themeConfig.useDynamicTheme) {
|
||||
dynamicDarkColorScheme(LocalContext.current)
|
||||
} else {
|
||||
DARK_COLOURS
|
||||
}
|
||||
MaterialTheme(colorScheme = colorScheme) {
|
||||
val backgroundColor = MaterialTheme.colorScheme.background
|
||||
SideEffect {
|
||||
systemUiController.setSystemBarsColor(backgroundColor)
|
||||
}
|
||||
CompositionLocalProvider(LocalExtendedColors provides if (systemInDarkTheme) DARK_EXTENDED else LIGHT_EXTENDED) {
|
||||
CompositionLocalProvider(LocalExtendedColors provides createExtended(MaterialTheme.colorScheme.primary, MaterialTheme.colorScheme.onPrimary)) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
@ -76,3 +75,7 @@ object SmallTalkTheme {
|
|||
@Composable
|
||||
get() = LocalExtendedColors.current
|
||||
}
|
||||
|
||||
data class ThemeConfig(
|
||||
val useDynamicTheme: Boolean,
|
||||
)
|
|
@ -3,5 +3,6 @@ applyAndroidComposeLibraryModule(project)
|
|||
dependencies {
|
||||
implementation project(":core")
|
||||
implementation project(":features:navigator")
|
||||
implementation project(":design-library")
|
||||
api project(":domains:android:core")
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package app.dapk.st.core
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
|
@ -54,3 +57,9 @@ fun LifecycleEffect(onStart: () -> Unit = {}, onStop: () -> Unit = {}) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.getActivity(): Activity? = when (this) {
|
||||
is Activity -> this
|
||||
is ContextWrapper -> baseContext.getActivity()
|
||||
else -> null
|
||||
}
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
package app.dapk.st.core
|
||||
|
||||
import app.dapk.st.core.extensions.unsafeLazy
|
||||
import app.dapk.st.navigator.IntentFactory
|
||||
|
||||
class CoreAndroidModule(private val intentFactory: IntentFactory): ProvidableModule {
|
||||
class CoreAndroidModule(
|
||||
private val intentFactory: IntentFactory,
|
||||
private val preferences: Lazy<Preferences>,
|
||||
) : ProvidableModule {
|
||||
|
||||
fun intentFactory() = intentFactory
|
||||
|
||||
private val themeStore by unsafeLazy { ThemeStore(preferences.value) }
|
||||
|
||||
fun themeStore() = themeStore
|
||||
|
||||
}
|
|
@ -6,20 +6,43 @@ import androidx.activity.ComponentActivity
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import app.dapk.st.core.extensions.unsafeLazy
|
||||
import app.dapk.st.design.components.SmallTalkTheme
|
||||
import app.dapk.st.design.components.ThemeConfig
|
||||
import app.dapk.st.navigator.navigator
|
||||
import androidx.activity.compose.setContent as _setContent
|
||||
|
||||
abstract class DapkActivity : ComponentActivity(), EffectScope {
|
||||
|
||||
private val coreAndroidModule by unsafeLazy { module<CoreAndroidModule>() }
|
||||
private val themeStore by unsafeLazy { coreAndroidModule.themeStore() }
|
||||
private val remembers = mutableMapOf<Any, Any>()
|
||||
protected val navigator by navigator { coreAndroidModule.intentFactory() }
|
||||
|
||||
private lateinit var themeConfig: ThemeConfig
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
this.themeConfig = ThemeConfig(themeStore.isMaterialYouEnabled())
|
||||
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
||||
}
|
||||
|
||||
protected fun setContent(content: @Composable () -> Unit) {
|
||||
_setContent {
|
||||
SmallTalkTheme(themeConfig) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (themeConfig.useDynamicTheme != themeStore.isMaterialYouEnabled()) {
|
||||
recreate()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun OnceEffect(key: Any, sideEffect: () -> Unit) {
|
||||
val triggerSideEffect = remembers.containsKey(key).not()
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package app.dapk.st.core
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
private const val KEY_MATERIAL_YOU_ENABLED = "material_you_enabled"
|
||||
|
||||
class ThemeStore(
|
||||
private val preferences: Preferences
|
||||
) {
|
||||
|
||||
private var _isMaterialYouEnabled: Boolean? = null
|
||||
|
||||
fun isMaterialYouEnabled() = _isMaterialYouEnabled ?: blockingInitialRead()
|
||||
|
||||
private fun blockingInitialRead(): Boolean {
|
||||
return runBlocking {
|
||||
(preferences.readBoolean(KEY_MATERIAL_YOU_ENABLED) ?: false).also { _isMaterialYouEnabled = it }
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun storeMaterialYouEnabled(isEnabled: Boolean) {
|
||||
_isMaterialYouEnabled = isEnabled
|
||||
preferences.store(KEY_MATERIAL_YOU_ENABLED, isEnabled)
|
||||
}
|
||||
|
||||
}
|
|
@ -2,10 +2,10 @@ package app.dapk.st.push
|
|||
|
||||
import android.content.Context
|
||||
import app.dapk.st.core.CoroutineDispatchers
|
||||
import app.dapk.st.core.Preferences
|
||||
import app.dapk.st.core.ProvidableModule
|
||||
import app.dapk.st.core.extensions.ErrorTracker
|
||||
import app.dapk.st.core.extensions.unsafeLazy
|
||||
import app.dapk.st.domain.Preferences
|
||||
import app.dapk.st.domain.push.PushTokenRegistrarPreferences
|
||||
import app.dapk.st.firebase.messaging.Messaging
|
||||
import app.dapk.st.push.messaging.MessagingPushTokenRegistrar
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package app.dapk.st.domain
|
||||
|
||||
import app.dapk.st.core.Preferences
|
||||
|
||||
class ApplicationPreferences(
|
||||
private val preferences: Preferences,
|
||||
) {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
package app.dapk.st.domain
|
||||
|
||||
import app.dapk.st.matrix.common.UserCredentials
|
||||
import app.dapk.st.core.Preferences
|
||||
import app.dapk.st.matrix.common.CredentialsStore
|
||||
import app.dapk.st.matrix.common.UserCredentials
|
||||
|
||||
internal class CredentialsPreferences(
|
||||
private val preferences: Preferences,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package app.dapk.st.domain
|
||||
|
||||
import app.dapk.st.core.Preferences
|
||||
import app.dapk.st.matrix.sync.FilterStore
|
||||
|
||||
internal class FilterPreferences(
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
package app.dapk.st.domain
|
||||
|
||||
interface Preferences {
|
||||
|
||||
suspend fun store(key: String, value: String)
|
||||
suspend fun readString(key: String): String?
|
||||
suspend fun clear()
|
||||
suspend fun remove(key: String)
|
||||
}
|
|
@ -2,6 +2,7 @@ package app.dapk.st.domain
|
|||
|
||||
import app.dapk.db.DapkDb
|
||||
import app.dapk.st.core.CoroutineDispatchers
|
||||
import app.dapk.st.core.Preferences
|
||||
import app.dapk.st.core.extensions.ErrorTracker
|
||||
import app.dapk.st.core.extensions.unsafeLazy
|
||||
import app.dapk.st.domain.eventlog.EventLogPersistence
|
||||
|
@ -22,7 +23,7 @@ import app.dapk.st.matrix.sync.SyncStore
|
|||
class StoreModule(
|
||||
private val database: DapkDb,
|
||||
private val databaseDropper: DatabaseDropper,
|
||||
private val preferences: Preferences,
|
||||
val preferences: Preferences,
|
||||
private val credentialPreferences: Preferences,
|
||||
private val errorTracker: ErrorTracker,
|
||||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package app.dapk.st.domain
|
||||
|
||||
import app.dapk.st.core.AppLogTag
|
||||
import app.dapk.st.core.log
|
||||
import app.dapk.st.core.Preferences
|
||||
import app.dapk.st.matrix.common.SyncToken
|
||||
import app.dapk.st.matrix.sync.SyncStore
|
||||
import app.dapk.st.matrix.sync.SyncStore.SyncKey
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package app.dapk.st.domain.profile
|
||||
|
||||
import app.dapk.st.domain.Preferences
|
||||
import app.dapk.st.core.Preferences
|
||||
import app.dapk.st.matrix.common.AvatarUrl
|
||||
import app.dapk.st.matrix.common.HomeServerUrl
|
||||
import app.dapk.st.matrix.common.UserId
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package app.dapk.st.domain.push
|
||||
|
||||
import app.dapk.st.domain.Preferences
|
||||
import app.dapk.st.core.Preferences
|
||||
|
||||
private const val SELECTION_KEY = "push_token_selection"
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ import app.dapk.st.profile.ProfileScreen
|
|||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun HomeScreen(homeViewModel: HomeViewModel) {
|
||||
SmallTalkTheme {
|
||||
Surface(Modifier.fillMaxSize()) {
|
||||
LaunchedEffect(true) {
|
||||
homeViewModel.start()
|
||||
|
@ -55,7 +54,6 @@ fun HomeScreen(homeViewModel: HomeViewModel) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BottomBar(state: SignedIn, homeViewModel: HomeViewModel) {
|
||||
|
|
|
@ -46,14 +46,12 @@ class MessengerActivity : DapkActivity() {
|
|||
val payload = readPayload<MessagerActivityPayload>()
|
||||
log(AppLogTag.ERROR_NON_FATAL, payload)
|
||||
setContent {
|
||||
SmallTalkTheme {
|
||||
Surface(Modifier.fillMaxSize()) {
|
||||
MessengerScreen(RoomId(payload.roomId), payload.attachments, viewModel, navigator)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class MessagerActivityPayload(
|
||||
|
|
|
@ -5,16 +5,14 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.ui.Modifier
|
||||
import app.dapk.st.messenger.MessengerModule
|
||||
import app.dapk.st.design.components.SmallTalkTheme
|
||||
import app.dapk.st.core.DapkActivity
|
||||
import app.dapk.st.core.module
|
||||
import app.dapk.st.core.viewModel
|
||||
import app.dapk.st.matrix.common.RoomId
|
||||
import app.dapk.st.messenger.MessengerModule
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
class RoomSettingsActivity : DapkActivity() {
|
||||
|
@ -33,14 +31,12 @@ class RoomSettingsActivity : DapkActivity() {
|
|||
super.onCreate(savedInstanceState)
|
||||
val payload = readPayload<RoomSettingsActivityPayload>()
|
||||
setContent {
|
||||
SmallTalkTheme {
|
||||
Surface(Modifier.fillMaxSize()) {
|
||||
// MessengerScreen(RoomId(payload.roomId), payload.attachments, viewModel, navigator)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class RoomSettingsActivityPayload(
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package app.dapk.st.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.ui.Modifier
|
||||
|
@ -9,7 +8,6 @@ import app.dapk.st.core.DapkActivity
|
|||
import app.dapk.st.core.module
|
||||
import app.dapk.st.core.resetModules
|
||||
import app.dapk.st.core.viewModel
|
||||
import app.dapk.st.design.components.SmallTalkTheme
|
||||
|
||||
class SettingsActivity : DapkActivity() {
|
||||
|
||||
|
@ -18,7 +16,6 @@ class SettingsActivity : DapkActivity() {
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
SmallTalkTheme {
|
||||
Surface(Modifier.fillMaxSize()) {
|
||||
SettingsScreen(settingsViewModel, onSignOut = {
|
||||
resetModules()
|
||||
|
@ -29,4 +26,3 @@ class SettingsActivity : DapkActivity() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package app.dapk.st.settings
|
||||
|
||||
import app.dapk.st.core.BuildMeta
|
||||
import app.dapk.st.core.ThemeStore
|
||||
import app.dapk.st.push.PushTokenRegistrars
|
||||
|
||||
internal class SettingsItemFactory(
|
||||
private val buildMeta: BuildMeta,
|
||||
private val pushTokenRegistrars: PushTokenRegistrars,
|
||||
private val themeStore: ThemeStore,
|
||||
) {
|
||||
|
||||
suspend fun root() = listOf(
|
||||
|
@ -13,6 +15,8 @@ internal class SettingsItemFactory(
|
|||
SettingItem.Text(SettingItem.Id.Encryption, "Encryption"),
|
||||
SettingItem.Text(SettingItem.Id.EventLog, "Event log"),
|
||||
SettingItem.Text(SettingItem.Id.PushProvider, "Push provider", pushTokenRegistrars.currentSelection().id),
|
||||
SettingItem.Header("Theme"),
|
||||
SettingItem.Toggle(SettingItem.Id.ToggleDynamicTheme, "Enable Material You", state = themeStore.isMaterialYouEnabled()),
|
||||
SettingItem.Header("Data"),
|
||||
SettingItem.Text(SettingItem.Id.ClearCache, "Clear cache"),
|
||||
SettingItem.Header("Account"),
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.content.ContentResolver
|
|||
import app.dapk.st.core.BuildMeta
|
||||
import app.dapk.st.core.CoroutineDispatchers
|
||||
import app.dapk.st.core.ProvidableModule
|
||||
import app.dapk.st.core.ThemeStore
|
||||
import app.dapk.st.domain.StoreModule
|
||||
import app.dapk.st.matrix.crypto.CryptoService
|
||||
import app.dapk.st.matrix.sync.SyncService
|
||||
|
@ -18,17 +19,21 @@ class SettingsModule(
|
|||
private val contentResolver: ContentResolver,
|
||||
private val buildMeta: BuildMeta,
|
||||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
private val themeStore: ThemeStore,
|
||||
) : ProvidableModule {
|
||||
|
||||
internal fun settingsViewModel() = SettingsViewModel(
|
||||
internal fun settingsViewModel(): SettingsViewModel {
|
||||
return SettingsViewModel(
|
||||
storeModule.cacheCleaner(),
|
||||
contentResolver,
|
||||
cryptoService,
|
||||
syncService,
|
||||
UriFilenameResolver(contentResolver, coroutineDispatchers),
|
||||
SettingsItemFactory(buildMeta, pushModule.pushTokenRegistrars()),
|
||||
SettingsItemFactory(buildMeta, pushModule.pushTokenRegistrars(), themeStore),
|
||||
pushModule.pushTokenRegistrars(),
|
||||
themeStore,
|
||||
)
|
||||
}
|
||||
|
||||
internal fun eventLogViewModel(): EventLoggerViewModel {
|
||||
return EventLoggerViewModel(storeModule.eventLogStore())
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.content.ClipboardManager
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.LocalActivityResultRegistryOwner
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.clickable
|
||||
|
@ -13,11 +14,11 @@ import androidx.compose.foundation.lazy.LazyColumn
|
|||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Visibility
|
||||
import androidx.compose.material.icons.filled.VisibilityOff
|
||||
import androidx.compose.material.icons.outlined.Lock
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
|
@ -37,10 +38,10 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.net.toUri
|
||||
import app.dapk.st.core.Lce
|
||||
import app.dapk.st.core.LceWithProgress
|
||||
import app.dapk.st.core.StartObserving
|
||||
import app.dapk.st.core.components.CenteredLoading
|
||||
import app.dapk.st.core.components.Header
|
||||
import app.dapk.st.core.getActivity
|
||||
import app.dapk.st.design.components.SettingsTextRow
|
||||
import app.dapk.st.design.components.Spider
|
||||
import app.dapk.st.design.components.SpiderPage
|
||||
|
@ -222,6 +223,9 @@ private fun RootSettings(page: Page.Root, onClick: (SettingItem) -> Unit) {
|
|||
}
|
||||
|
||||
is SettingItem.Header -> Header(item.label)
|
||||
is SettingItem.Toggle -> Toggle(item, onToggle = {
|
||||
onClick(item)
|
||||
})
|
||||
}
|
||||
}
|
||||
item { Spacer(Modifier.height(12.dp)) }
|
||||
|
@ -238,6 +242,23 @@ private fun RootSettings(page: Page.Root, onClick: (SettingItem) -> Unit) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Toggle(item: SettingItem.Toggle, onToggle: () -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 24.dp, end = 24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(text = item.content)
|
||||
Switch(
|
||||
checked = item.state,
|
||||
onCheckedChange = { onToggle() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Encryption(viewModel: SettingsViewModel, page: Page.Security) {
|
||||
Column {
|
||||
|
@ -292,6 +313,9 @@ private fun SettingsViewModel.ObserveEvents(onSignOut: () -> Unit) {
|
|||
is OpenUrl -> {
|
||||
context.startActivity(Intent(Intent.ACTION_VIEW).apply { data = it.url.toUri() })
|
||||
}
|
||||
RecreateActivity -> {
|
||||
context.getActivity()?.recreate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ internal sealed interface SettingItem {
|
|||
|
||||
data class Header(val label: String, override val id: Id = Id.Ignored) : SettingItem
|
||||
data class Text(override val id: Id, val content: String, val subtitle: String? = null) : SettingItem
|
||||
data class Toggle(override val id: Id, val content: String, val state: Boolean) : SettingItem
|
||||
data class AccessToken(override val id: Id, val content: String, val accessToken: String) : SettingItem
|
||||
|
||||
enum class Id {
|
||||
|
@ -55,6 +56,7 @@ internal sealed interface SettingItem {
|
|||
Encryption,
|
||||
PrivacyPolicy,
|
||||
Ignored,
|
||||
ToggleDynamicTheme,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,5 +67,6 @@ sealed interface SettingsEvent {
|
|||
object OpenEventLog : SettingsEvent
|
||||
data class OpenUrl(val url: String) : SettingsEvent
|
||||
data class CopyToClipboard(val message: String, val content: String) : SettingsEvent
|
||||
object RecreateActivity : SettingsEvent
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.content.ContentResolver
|
|||
import android.net.Uri
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import app.dapk.st.core.Lce
|
||||
import app.dapk.st.core.ThemeStore
|
||||
import app.dapk.st.design.components.SpiderPage
|
||||
import app.dapk.st.domain.StoreCleaner
|
||||
import app.dapk.st.matrix.crypto.CryptoService
|
||||
|
@ -30,6 +31,7 @@ internal class SettingsViewModel(
|
|||
private val uriFilenameResolver: UriFilenameResolver,
|
||||
private val settingsItemFactory: SettingsItemFactory,
|
||||
private val pushTokenRegistrars: PushTokenRegistrars,
|
||||
private val themeStore: ThemeStore,
|
||||
factory: MutableStateFactory<SettingsScreenState> = defaultStateFactory(),
|
||||
) : DapkViewModel<SettingsScreenState, SettingsEvent>(
|
||||
initialState = SettingsScreenState(SpiderPage(Page.Routes.root, "Settings", null, Page.Root(Lce.Loading()))),
|
||||
|
@ -98,8 +100,16 @@ internal class SettingsViewModel(
|
|||
Ignored -> {
|
||||
// do nothing
|
||||
}
|
||||
ToggleDynamicTheme -> {
|
||||
viewModelScope.launch {
|
||||
themeStore.storeMaterialYouEnabled(!themeStore.isMaterialYouEnabled())
|
||||
start()
|
||||
_events.emit(RecreateActivity)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun fetchPushProviders() {
|
||||
updatePageState<Page.PushProviders> { copy(options = Lce.Loading()) }
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package app.dapk.st.settings.eventlogger
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.ui.Modifier
|
||||
|
@ -9,7 +8,6 @@ import app.dapk.st.core.DapkActivity
|
|||
import app.dapk.st.core.module
|
||||
import app.dapk.st.core.viewModel
|
||||
import app.dapk.st.settings.SettingsModule
|
||||
import app.dapk.st.design.components.SmallTalkTheme
|
||||
|
||||
class EventLogActivity : DapkActivity() {
|
||||
|
||||
|
@ -18,11 +16,9 @@ class EventLogActivity : DapkActivity() {
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
SmallTalkTheme {
|
||||
Surface(Modifier.fillMaxSize()) {
|
||||
EventLogScreen(viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package app.dapk.st.settings
|
||||
|
||||
import app.dapk.st.core.ThemeStore
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import test.delegateReturn
|
||||
|
||||
class FakeThemeStore {
|
||||
val instance = mockk<ThemeStore>()
|
||||
|
||||
fun givenMaterialYouIsEnabled() = every { instance.isMaterialYouEnabled() }.delegateReturn()
|
||||
}
|
|
@ -6,7 +6,6 @@ import app.dapk.st.push.Registrar
|
|||
import internalfixture.aSettingHeaderItem
|
||||
import internalfixture.aSettingTextItem
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.amshove.kluent.shouldBeEqualTo
|
||||
|
@ -14,17 +13,20 @@ import org.junit.Test
|
|||
import test.delegateReturn
|
||||
|
||||
private val A_SELECTION = Registrar("A_SELECTION")
|
||||
private const val ENABLED_MATERIAL_YOU = true
|
||||
|
||||
class SettingsItemFactoryTest {
|
||||
|
||||
private val buildMeta = BuildMeta(versionName = "a-version-name", versionCode = 100)
|
||||
private val fakePushTokenRegistrars = FakePushRegistrars()
|
||||
private val fakeThemeStore = FakeThemeStore()
|
||||
|
||||
private val settingsItemFactory = SettingsItemFactory(buildMeta, fakePushTokenRegistrars.instance)
|
||||
private val settingsItemFactory = SettingsItemFactory(buildMeta, fakePushTokenRegistrars.instance, fakeThemeStore.instance)
|
||||
|
||||
@Test
|
||||
fun `when creating root items, then is expected`() = runTest {
|
||||
fakePushTokenRegistrars.givenCurrentSelection().returns(A_SELECTION)
|
||||
fakeThemeStore.givenMaterialYouIsEnabled().returns(ENABLED_MATERIAL_YOU)
|
||||
|
||||
val result = settingsItemFactory.root()
|
||||
|
||||
|
@ -33,6 +35,8 @@ class SettingsItemFactoryTest {
|
|||
aSettingTextItem(SettingItem.Id.Encryption, "Encryption"),
|
||||
aSettingTextItem(SettingItem.Id.EventLog, "Event log"),
|
||||
aSettingTextItem(SettingItem.Id.PushProvider, "Push provider", A_SELECTION.id),
|
||||
SettingItem.Header("Theme"),
|
||||
SettingItem.Toggle(SettingItem.Id.ToggleDynamicTheme, "Enable Material You", state = ENABLED_MATERIAL_YOU),
|
||||
aSettingHeaderItem("Data"),
|
||||
aSettingTextItem(SettingItem.Id.ClearCache, "Clear cache"),
|
||||
aSettingHeaderItem("Account"),
|
||||
|
|
|
@ -40,6 +40,7 @@ internal class SettingsViewModelTest {
|
|||
private val fakeUriFilenameResolver = FakeUriFilenameResolver()
|
||||
private val fakePushTokenRegistrars = FakePushRegistrars()
|
||||
private val fakeSettingsItemFactory = FakeSettingsItemFactory()
|
||||
private val fakeThemeStore = FakeThemeStore()
|
||||
|
||||
private val viewModel = SettingsViewModel(
|
||||
fakeStoreCleaner,
|
||||
|
@ -49,6 +50,7 @@ internal class SettingsViewModelTest {
|
|||
fakeUriFilenameResolver.instance,
|
||||
fakeSettingsItemFactory.instance,
|
||||
fakePushTokenRegistrars.instance,
|
||||
fakeThemeStore.instance,
|
||||
runViewModelTest.testMutableStateFactory(),
|
||||
)
|
||||
|
||||
|
|
|
@ -4,14 +4,12 @@ import android.content.Intent
|
|||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.ui.Modifier
|
||||
import app.dapk.st.core.DapkActivity
|
||||
import app.dapk.st.core.module
|
||||
import app.dapk.st.core.viewModel
|
||||
import app.dapk.st.design.components.SmallTalkTheme
|
||||
|
||||
class ShareEntryActivity : DapkActivity() {
|
||||
|
||||
|
@ -21,12 +19,10 @@ class ShareEntryActivity : DapkActivity() {
|
|||
super.onCreate(savedInstanceState)
|
||||
val urisToShare = intent.readSendUrisOrNull() ?: throw IllegalArgumentException("Expected deeplink uris but they were missing")
|
||||
setContent {
|
||||
SmallTalkTheme {
|
||||
Surface(Modifier.fillMaxSize()) {
|
||||
ShareEntryScreen(navigator, viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
viewModel.withUris(urisToShare)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
package app.dapk.st.verification
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.ui.Modifier
|
||||
import app.dapk.st.design.components.SmallTalkTheme
|
||||
import app.dapk.st.core.DapkActivity
|
||||
import app.dapk.st.core.module
|
||||
import app.dapk.st.core.viewModel
|
||||
|
@ -17,11 +15,9 @@ class VerificationActivity : DapkActivity() {
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
SmallTalkTheme {
|
||||
Surface(Modifier.fillMaxSize()) {
|
||||
VerificationScreen(verificationViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,12 @@ package app.dapk.st.verification
|
|||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
@Composable
|
||||
fun VerificationScreen(viewModel: VerificationViewModel) {
|
||||
|
||||
|
||||
Column {
|
||||
Text("Verification request")
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package test.impl
|
||||
|
||||
import app.dapk.st.domain.Preferences
|
||||
import app.dapk.st.core.Preferences
|
||||
import test.unit
|
||||
|
||||
class InMemoryPreferences : Preferences {
|
||||
|
|
Loading…
Reference in New Issue