android: blue theme and more options

This commit is contained in:
Avently
2023-04-26 22:02:45 +07:00
parent aa441c88db
commit 1f441c56c7
20 changed files with 225 additions and 72 deletions
@@ -26,8 +26,7 @@ import androidx.lifecycle.*
import chat.simplex.app.MainActivity.Companion.enteredBackground
import chat.simplex.app.model.*
import chat.simplex.app.model.NtfManager.Companion.getUserIdFromIntent
import chat.simplex.app.ui.theme.SimpleButton
import chat.simplex.app.ui.theme.SimpleXTheme
import chat.simplex.app.ui.theme.*
import chat.simplex.app.views.SplashView
import chat.simplex.app.views.call.ActiveCallView
import chat.simplex.app.views.call.IncomingCallAlertView
@@ -85,7 +84,7 @@ class MainActivity: FragmentActivity() {
SimpleXTheme {
Surface(
Modifier
.background(MaterialTheme.colors.background)
.themedBackground()
.fillMaxSize()
) {
MainPage(
@@ -14,7 +14,6 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
@@ -30,6 +29,8 @@ import kotlinx.coroutines.*
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.serialization.*
import kotlinx.serialization.builtins.MapSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.json.*
import java.util.Date
@@ -57,6 +58,7 @@ enum class SimplexLinkMode {
class AppPreferences(val context: Context) {
private val sharedPreferences: SharedPreferences = context.getSharedPreferences(SHARED_PREFS_ID, Context.MODE_PRIVATE)
private val sharedPreferencesThemes: SharedPreferences = context.getSharedPreferences(SHARED_PREFS_THEMES_ID, Context.MODE_PRIVATE)
// deprecated, remove in 2024
private val runServiceInBackground = mkBoolPreference(SHARED_PREFS_RUN_SERVICE_IN_BACKGROUND, true)
@@ -147,7 +149,12 @@ class AppPreferences(val context: Context) {
val confirmDBUpgrades = mkBoolPreference(SHARED_PREFS_CONFIRM_DB_UPGRADES, false)
val currentTheme = mkStrPreference(SHARED_PREFS_CURRENT_THEME, DefaultTheme.SYSTEM.name)
val primaryColor = mkIntPreference(SHARED_PREFS_PRIMARY_COLOR, LightColorPalette.primary.toArgb())
val systemDarkTheme = mkStrPreference(SHARED_PREFS_SYSTEM_DARK_THEME, DefaultTheme.BLUE.name)
val themeOverrides = mkMapPreference(SHARED_PREFS_THEMES, mapOf(), encode = {
json.encodeToString(MapSerializer(String.serializer(), ThemeOverrides.serializer()), it)
}, decode = {
json.decodeFromString(MapSerializer(String.serializer(), ThemeOverrides.serializer()), it)
}, sharedPreferencesThemes)
val whatsNewVersion = mkStrPreference(SHARED_PREFS_WHATS_NEW_VERSION, null)
@@ -204,8 +211,15 @@ class AppPreferences(val context: Context) {
}
)
private fun <K, V> mkMapPreference(prefName: String, default: Map<K, V>, encode: (Map<K, V>) -> String, decode: (String) -> Map<K, V>, prefs: SharedPreferences = sharedPreferences): SharedPreference<Map<K,V>> =
SharedPreference(
get = fun() = decode(prefs.getString(prefName, encode(default))!!),
set = fun(value) = prefs.edit().putString(prefName, encode(value)).apply()
)
companion object {
internal const val SHARED_PREFS_ID = "chat.simplex.app.SIMPLEX_APP_PREFS"
internal const val SHARED_PREFS_THEMES_ID = "chat.simplex.app.THEMES"
private const val SHARED_PREFS_AUTO_RESTART_WORKER_VERSION = "AutoRestartWorkerVersion"
private const val SHARED_PREFS_RUN_SERVICE_IN_BACKGROUND = "RunServiceInBackground"
private const val SHARED_PREFS_NOTIFICATIONS_MODE = "NotificationsMode"
@@ -258,7 +272,8 @@ class AppPreferences(val context: Context) {
private const val SHARED_PREFS_ENCRYPTION_STARTED_AT = "EncryptionStartedAt"
private const val SHARED_PREFS_CONFIRM_DB_UPGRADES = "ConfirmDBUpgrades"
private const val SHARED_PREFS_CURRENT_THEME = "CurrentTheme"
private const val SHARED_PREFS_PRIMARY_COLOR = "PrimaryColor"
private const val SHARED_PREFS_SYSTEM_DARK_THEME = "SystemDarkTheme"
private const val SHARED_PREFS_THEMES = "Themes"
private const val SHARED_PREFS_WHATS_NEW_VERSION = "WhatsNewVersion"
}
}
@@ -2,16 +2,60 @@ package chat.simplex.app.ui.theme
import android.app.UiModeManager
import android.content.Context
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.*
import androidx.compose.ui.unit.dp
import chat.simplex.app.SimplexApp
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.serialization.Serializable
enum class DefaultTheme {
SYSTEM, DARK, LIGHT
SYSTEM, LIGHT, DARK, BLUE;
// Call in only with base theme, not SYSTEM
fun hasChangedPrimary(colors: Colors): Boolean {
return when (this) {
SYSTEM -> return false
LIGHT -> colors.primary != LightColorPalette.primary
DARK -> colors.primary != DarkColorPalette.primary
BLUE -> colors.primary != BlueColorPalette.primary
}
}
}
@Serializable
data class ThemeColors(
val primary: String? = null
) {
fun toColors(base: DefaultTheme): Colors = when (base) {
DefaultTheme.LIGHT -> LightColorPalette.copy(primary = primary?.colorFromReadableHex() ?: LightColorPalette.primary)
DefaultTheme.DARK -> DarkColorPalette.copy(primary = primary?.colorFromReadableHex() ?: DarkColorPalette.primary)
DefaultTheme.BLUE -> BlueColorPalette.copy(primary = primary?.colorFromReadableHex() ?: BlueColorPalette.primary)
// shouldn't be here
DefaultTheme.SYSTEM -> LightColorPalette.copy(primary = primary?.colorFromReadableHex() ?: LightColorPalette.primary)
}
}
private fun String.colorFromReadableHex(): Color =
Color(this.replace("#", "").toLongOrNull(16) ?: Color.White.toArgb().toLong())
@Serializable
data class ThemeOverrides (
val base: DefaultTheme,
val colors: ThemeColors
)
fun Modifier.themedBackground(baseTheme: DefaultTheme = CurrentColors.value.base, shape: Shape = RectangleShape): Modifier {
return if (baseTheme == DefaultTheme.BLUE) {
this.background(brush = Brush.linearGradient(listOf(Color(0xff0C0B13), Color(0xff151D36)), Offset(0f, Float.POSITIVE_INFINITY), Offset(Float.POSITIVE_INFINITY, 0f)), shape = shape)
} else {
this.background(color = CurrentColors.value.colors.background, shape = shape)
}
}
val DEFAULT_PADDING = 20.dp
@@ -22,10 +66,10 @@ val DEFAULT_BOTTOM_BUTTON_PADDING = 20.dp
val DarkColorPalette = darkColors(
primary = SimplexBlue, // If this value changes also need to update #0088ff in string resource files
primaryVariant = SimplexGreen,
primaryVariant = SimplexBlue,
secondary = DarkGray,
// background = Color.Black,
// surface = Color.Black,
surface = Color(0xFF121212),
// background = Color(0xFF121212),
// surface = Color(0xFF121212),
error = Color.Red,
@@ -35,25 +79,39 @@ val DarkColorPalette = darkColors(
)
val LightColorPalette = lightColors(
primary = SimplexBlue, // If this value changes also need to update #0088ff in string resource files
primaryVariant = SimplexGreen,
primaryVariant = SimplexBlue,
secondary = LightGray,
error = Color.Red,
// background = Color.White,
// surface = Color.White
surface = Color.White,
// onPrimary = Color.White,
// onSecondary = Color.Black,
// onBackground = Color.Black,
// onSurface = Color.Black,
)
val CurrentColors: MutableStateFlow<Pair<Colors, DefaultTheme>> = MutableStateFlow(ThemeManager.currentColors(isInNightMode()))
val BlueColorPalette = darkColors(
primary = Color(0xff70F0F9), // If this value changes also need to update #0088ff in string resource files
primaryVariant = Color(0xff298AE7),
secondary = Color(0xff2C464D),
background = Color(0xff111528),
// surface = Color.Black,
// background = Color(0xFF121212),
surface = Color(0xFF1C1C22),
error = Color.Red,
onBackground = Color(0xFFFFFBFA),
onSurface = Color(0xFFFFFBFA),
// onError: Color = Color.Black,
)
val CurrentColors: MutableStateFlow<ThemeManager.ActiveTheme> = MutableStateFlow(ThemeManager.currentColors(isInNightMode()))
// Non-@Composable implementation
private fun isInNightMode() =
(SimplexApp.context.getSystemService(Context.UI_MODE_SERVICE) as UiModeManager).nightMode == UiModeManager.MODE_NIGHT_YES
@Composable
fun isInDarkTheme(): Boolean = !CurrentColors.collectAsState().value.first.isLight
fun isInDarkTheme(): Boolean = !CurrentColors.collectAsState().value.colors.isLight
@Composable
fun SimpleXTheme(darkTheme: Boolean? = null, content: @Composable () -> Unit) {
@@ -64,14 +122,14 @@ fun SimpleXTheme(darkTheme: Boolean? = null, content: @Composable () -> Unit) {
}
val systemDark = isSystemInDarkTheme()
LaunchedEffect(systemDark) {
if (CurrentColors.value.second == DefaultTheme.SYSTEM && CurrentColors.value.first.isLight == systemDark) {
if (SimplexApp.context.chatModel.controller.appPrefs.currentTheme.get() == DefaultTheme.SYSTEM.name && CurrentColors.value.colors.isLight == systemDark) {
// Change active colors from light to dark and back based on system theme
ThemeManager.applyTheme(DefaultTheme.SYSTEM.name, systemDark)
}
}
val theme by CurrentColors.collectAsState()
MaterialTheme(
colors = theme.first,
colors = theme.colors,
typography = Typography,
shapes = Shapes,
content = content
@@ -7,22 +7,41 @@ import chat.simplex.app.R
import chat.simplex.app.SimplexApp
import chat.simplex.app.model.AppPreferences
import chat.simplex.app.views.helpers.generalGetString
import okhttp3.internal.toHexString
object ThemeManager {
private val appPrefs: AppPreferences by lazy {
AppPreferences(SimplexApp.context)
SimplexApp.context.chatModel.controller.appPrefs
}
fun currentColors(darkForSystemTheme: Boolean): Pair<Colors, DefaultTheme> {
val theme = appPrefs.currentTheme.get()!!
val systemThemeColors = if (darkForSystemTheme) DarkColorPalette else LightColorPalette
val res = when (theme) {
DefaultTheme.SYSTEM.name -> Pair(systemThemeColors, DefaultTheme.SYSTEM)
DefaultTheme.DARK.name -> Pair(DarkColorPalette, DefaultTheme.DARK)
DefaultTheme.LIGHT.name -> Pair(LightColorPalette, DefaultTheme.LIGHT)
else -> Pair(systemThemeColors, DefaultTheme.SYSTEM)
data class ActiveTheme(val name: String, val base: DefaultTheme, val colors: Colors)
private fun systemDarkThemeColors(): Pair<Colors, DefaultTheme> = when (appPrefs.systemDarkTheme.get()) {
DefaultTheme.DARK.name -> DarkColorPalette to DefaultTheme.DARK
DefaultTheme.BLUE.name -> BlueColorPalette to DefaultTheme.BLUE
else -> BlueColorPalette to DefaultTheme.BLUE
}
fun currentColors(darkForSystemTheme: Boolean): ActiveTheme {
val themeName = appPrefs.currentTheme.get()!!
val themeOverrides = appPrefs.themeOverrides.get()
val theme = if (themeName != DefaultTheme.SYSTEM.name) {
themeOverrides[themeName]
} else {
themeOverrides[if (darkForSystemTheme) appPrefs.systemDarkTheme.get() else DefaultTheme.LIGHT.name]
}
return res.copy(first = res.first.copy(primary = Color(appPrefs.primaryColor.get())))
val baseTheme = when (themeName) {
DefaultTheme.SYSTEM.name -> if (darkForSystemTheme) systemDarkThemeColors() else Pair(LightColorPalette, DefaultTheme.LIGHT)
DefaultTheme.LIGHT.name -> Pair(LightColorPalette, DefaultTheme.LIGHT)
DefaultTheme.DARK.name -> Pair(DarkColorPalette, DefaultTheme.DARK)
DefaultTheme.BLUE.name -> Pair(BlueColorPalette, DefaultTheme.BLUE)
else -> if (theme != null) theme.colors.toColors(theme.base) to theme.base else Pair(LightColorPalette, DefaultTheme.LIGHT)
}
if (theme == null) {
return ActiveTheme(themeName, baseTheme.second, baseTheme.first)
}
return ActiveTheme(themeName, baseTheme.second, theme.colors.toColors(theme.base))
}
// colors, default theme enum, localized name of theme
@@ -30,7 +49,7 @@ object ThemeManager {
val allThemes = ArrayList<Triple<Colors, DefaultTheme, String>>()
allThemes.add(
Triple(
if (darkForSystemTheme) DarkColorPalette else LightColorPalette,
if (darkForSystemTheme) systemDarkThemeColors().first else LightColorPalette,
DefaultTheme.SYSTEM,
generalGetString(R.string.theme_system)
)
@@ -49,16 +68,47 @@ object ThemeManager {
generalGetString(R.string.theme_dark)
)
)
allThemes.add(
Triple(
BlueColorPalette,
DefaultTheme.BLUE,
generalGetString(R.string.theme_blue)
)
)
return allThemes
}
fun applyTheme(name: String, darkForSystemTheme: Boolean) {
appPrefs.currentTheme.set(name)
fun applyTheme(theme: String, darkForSystemTheme: Boolean) {
appPrefs.currentTheme.set(theme)
CurrentColors.value = currentColors(darkForSystemTheme)
}
fun saveAndApplyPrimaryColor(color: Color) {
appPrefs.primaryColor.set(color.toArgb())
CurrentColors.value = currentColors(!CurrentColors.value.first.isLight)
fun changeDarkTheme(theme: String, darkForSystemTheme: Boolean) {
appPrefs.systemDarkTheme.set(theme)
CurrentColors.value = currentColors(darkForSystemTheme)
}
fun saveAndApplyPrimaryColor(color: Color? = null, darkForSystemTheme: Boolean) {
val themeName = appPrefs.currentTheme.get()!!
val nonSystemThemeName = if (themeName != DefaultTheme.SYSTEM.name) {
themeName
} else {
if (darkForSystemTheme) appPrefs.systemDarkTheme.get()!! else DefaultTheme.LIGHT.name
}
val color = color ?: when(themeName) {
DefaultTheme.LIGHT.name -> LightColorPalette.primary
DefaultTheme.DARK.name -> DarkColorPalette.primary
DefaultTheme.BLUE.name -> BlueColorPalette.primary
else -> (if (darkForSystemTheme) systemDarkThemeColors().first else LightColorPalette).primary
}
val overrides = appPrefs.themeOverrides.get().toMutableMap()
val prevValue = overrides[nonSystemThemeName]
val current = prevValue?.copy(colors = prevValue.colors.copy(primary = color.toReadableHex()))
?: ThemeOverrides(base = CurrentColors.value.base, colors = ThemeColors(primary = color.toReadableHex()))
overrides[nonSystemThemeName] = current
appPrefs.themeOverrides.set(overrides)
CurrentColors.value = currentColors(!CurrentColors.value.colors.isLight)
}
}
private fun Color.toReadableHex(): String = "#" + toArgb().toHexString()
@@ -6,12 +6,13 @@ import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import chat.simplex.app.ui.theme.themedBackground
@Composable
fun SplashView() {
Surface(
Modifier
.background(MaterialTheme.colors.background)
.themedBackground()
.fillMaxSize()
) {
// Image(
@@ -100,7 +100,7 @@ fun TerminalLayout(
modifier = Modifier
.padding(contentPadding)
.fillMaxWidth()
.background(MaterialTheme.colors.background)
.themedBackground()
) {
TerminalLog(terminalItems)
}
@@ -367,7 +367,7 @@ fun CallInfoView(call: Call, alignment: Alignment.Horizontal) {
// horizontalAlignment = Alignment.CenterHorizontally,
// verticalArrangement = Arrangement.spacedBy(12.dp),
// modifier = Modifier
// .background(MaterialTheme.colors.background)
// .themedBackground()
// .fillMaxSize()
// ) {
// WebRTCView(callCommand) { apiMsg ->
@@ -97,7 +97,7 @@ fun IncomingCallActivityView(m: ChatModel) {
SimpleXTheme {
Surface(
Modifier
.background(MaterialTheme.colors.background)
.themedBackground()
.fillMaxSize()) {
if (showCallView) {
Box {
@@ -226,7 +226,7 @@ fun PreviewIncomingCallLockScreenAlert() {
SimpleXTheme(true) {
Surface(
Modifier
.background(MaterialTheme.colors.background)
.themedBackground()
.fillMaxSize()) {
IncomingCallLockScreenAlertLayout(
invitation = RcvCallInvitation(
@@ -326,7 +326,7 @@ fun ChatLayout(
Box(
Modifier
.fillMaxWidth()
.background(MaterialTheme.colors.background)
.themedBackground()
) {
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
ModalBottomSheetLayout(
@@ -96,7 +96,6 @@ fun ChatListView(chatModel: ChatModel, setPerformLA: (Boolean, FragmentActivity)
Column(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colors.background)
) {
if (chatModel.chats.isNotEmpty()) {
ChatList(chatModel, search = searchInList)
@@ -19,8 +19,7 @@ import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.unit.dp
import chat.simplex.app.R
import chat.simplex.app.model.*
import chat.simplex.app.ui.theme.HighOrLowlight
import chat.simplex.app.ui.theme.Indigo
import chat.simplex.app.ui.theme.*
import chat.simplex.app.views.helpers.*
import kotlinx.coroutines.flow.MutableStateFlow
@@ -36,7 +35,7 @@ fun ShareListView(chatModel: ChatModel, stopped: Boolean) {
Column(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colors.background)
.themedBackground()
) {
if (chatModel.chats.isNotEmpty()) {
ShareList(chatModel, search = searchInList)
@@ -107,7 +107,7 @@ fun UserPicker(
.width(IntrinsicSize.Min)
.height(IntrinsicSize.Min)
.shadow(8.dp, RoundedCornerShape(corner = CornerSize(25.dp)), clip = true)
.background(if (isInDarkTheme()) DarkGrayBackground else MaterialTheme.colors.background, RoundedCornerShape(corner = CornerSize(25.dp)))
.background(MaterialTheme.colors.surface, RoundedCornerShape(corner = CornerSize(25.dp)))
.clip(RoundedCornerShape(corner = CornerSize(25.dp)))
) {
Column(Modifier.weight(1f).verticalScroll(rememberScrollState())) {
@@ -4,6 +4,7 @@ import android.content.res.Configuration
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
@@ -48,7 +49,7 @@ fun AppBarTitle(title: String, withPadding: Boolean = true) {
.padding(bottom = DEFAULT_PADDING * 1.5f, start = if (withPadding) DEFAULT_PADDING else 0.dp, end = if (withPadding) DEFAULT_PADDING else 0.dp,),
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.h1,
color = MaterialTheme.colors.primary,
color = if (CurrentColors.collectAsState().value.base == DefaultTheme.BLUE) MaterialTheme.colors.primaryVariant else MaterialTheme.colors.primary,
textAlign = TextAlign.Center
)
}
@@ -1,5 +1,6 @@
package chat.simplex.app.views.helpers
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -19,7 +20,7 @@ fun DefaultDropdownMenu(
dropdownMenuItems: (@Composable () -> Unit)?
) {
MaterialTheme(
colors = MaterialTheme.colors.copy(surface = if (isInDarkTheme()) Color(0xFF0B0B0B) else MaterialTheme.colors.background),
colors = MaterialTheme.colors.copy(surface = MaterialTheme.colors.surface),
shapes = MaterialTheme.shapes.copy(medium = RoundedCornerShape(corner = CornerSize(25.dp)))
) {
DropdownMenu(
@@ -27,6 +28,7 @@ fun DefaultDropdownMenu(
onDismissRequest = { showMenu.value = false },
Modifier
.widthIn(min = 250.dp)
.background(MaterialTheme.colors.surface)
.padding(vertical = 4.dp),
offset = offset,
) {
@@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import chat.simplex.app.TAG
import chat.simplex.app.ui.theme.isInDarkTheme
import chat.simplex.app.ui.theme.themedBackground
import java.util.concurrent.atomic.AtomicBoolean
@Composable
@@ -24,7 +25,7 @@ fun ModalView(
) {
BackHandler(onBack = close)
Surface(Modifier.fillMaxSize()) {
Column(Modifier.background(background)) {
Column(if (background != MaterialTheme.colors.background) Modifier.background(background) else Modifier.themedBackground()) {
CloseSheetBar(close, endButtons)
Box(modifier) { content() }
}
@@ -1,14 +1,15 @@
package chat.simplex.app.ui.theme
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.ui.res.painterResource
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.text.font.FontWeight
@@ -79,7 +80,7 @@ fun SimpleButtonIconEnded(
@Composable
fun SimpleButtonFrame(click: () -> Unit, modifier: Modifier = Modifier, disabled: Boolean = false, content: @Composable () -> Unit) {
Surface(shape = RoundedCornerShape(20.dp)) {
Box(Modifier.clip(RoundedCornerShape(20.dp))) {
val modifier = if (disabled) modifier else modifier.clickable { click() }
Row(
verticalAlignment = Alignment.CenterVertically,
@@ -9,6 +9,7 @@ import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
@@ -71,7 +72,7 @@ fun WhatsNewView(viaSettings: Boolean = false, close: () -> Unit) {
) {
if (currentVersion.value > 0) {
val prev = currentVersion.value - 1
Surface(shape = RoundedCornerShape(20.dp)) {
Box(Modifier.clip(RoundedCornerShape(20.dp))) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
@@ -87,7 +88,7 @@ fun WhatsNewView(viaSettings: Boolean = false, close: () -> Unit) {
Spacer(Modifier.fillMaxWidth().weight(1f))
if (currentVersion.value < versionDescriptions.lastIndex) {
val next = currentVersion.value + 1
Surface(shape = RoundedCornerShape(20.dp)) {
Box(Modifier.clip(RoundedCornerShape(20.dp))) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
@@ -113,17 +114,7 @@ fun WhatsNewView(viaSettings: Boolean = false, close: () -> Unit) {
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(DEFAULT_PADDING)
) {
Text(
String.format(generalGetString(R.string.new_in_version), v.version),
Modifier
.fillMaxWidth()
.padding(DEFAULT_PADDING),
textAlign = TextAlign.Center,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.h1,
fontWeight = FontWeight.Normal,
color = HighOrLowlight
)
AppBarTitle(String.format(generalGetString(R.string.new_in_version), v.version))
v.features.forEach { feature ->
featureDescription(painterResource(feature.icon), feature.titleId, feature.descrId, feature.link)
@@ -39,6 +39,7 @@ import chat.simplex.app.views.helpers.*
import com.godaddy.android.colorpicker.*
import kotlinx.coroutines.delay
import java.util.*
import kotlin.collections.ArrayList
enum class AppIcon(val resId: Int) {
DEFAULT(R.mipmap.icon),
@@ -69,6 +70,7 @@ fun AppearanceView(m: ChatModel) {
AppearanceLayout(
appIcon,
m.controller.appPrefs.appLanguage,
m.controller.appPrefs.systemDarkTheme,
changeIcon = ::setAppIcon,
editPrimaryColor = { primary ->
ModalManager.shared.showModalCloseable { close ->
@@ -81,6 +83,7 @@ fun AppearanceView(m: ChatModel) {
@Composable fun AppearanceLayout(
icon: MutableState<AppIcon>,
languagePref: SharedPreference<String?>,
systemDarkTheme: SharedPreference<String?>,
changeIcon: (AppIcon) -> Unit,
editPrimaryColor: (Color) -> Unit,
) {
@@ -144,21 +147,31 @@ fun AppearanceView(m: ChatModel) {
val currentTheme by CurrentColors.collectAsState()
SectionView(stringResource(R.string.settings_section_title_themes)) {
val darkTheme = isSystemInDarkTheme()
val state = remember { derivedStateOf { currentTheme.second } }
val state = remember { derivedStateOf { currentTheme.name } }
ThemeSelector(state) {
ThemeManager.applyTheme(it.name, darkTheme)
ThemeManager.applyTheme(it, darkTheme)
}
SectionItemViewSpaceBetween({ editPrimaryColor(currentTheme.first.primary) }) {
if (state.value == DefaultTheme.SYSTEM.name) {
val systemDarkTheme = remember { systemDarkTheme.state }
PreferenceToggle(generalGetString(R.string.simplex_blue_as_dark_theme), systemDarkTheme.value == DefaultTheme.BLUE.name) {
ThemeManager.changeDarkTheme(if (it) DefaultTheme.BLUE.name else DefaultTheme.DARK.name, darkTheme)
}
/*DarkThemeSelector(systemDarkTheme) {
ThemeManager.changeDarkTheme(it, darkTheme)
}*/
}
SectionItemViewSpaceBetween({ editPrimaryColor(currentTheme.colors.primary) }) {
val title = generalGetString(R.string.color_primary)
Text(title)
Icon(painterResource(R.drawable.ic_circle_filled), title, tint = colors.primary)
}
}
if (currentTheme.first.primary != LightColorPalette.primary) {
if (currentTheme.base.hasChangedPrimary(currentTheme.colors)) {
SectionCustomFooter(PaddingValues(start = 7.dp, end = 7.dp, top = 5.dp)) {
val isInDarkTheme = isInDarkTheme()
TextButton(
onClick = {
ThemeManager.saveAndApplyPrimaryColor(LightColorPalette.primary)
ThemeManager.saveAndApplyPrimaryColor(darkForSystemTheme = isInDarkTheme)
},
) {
Text(generalGetString(R.string.reset_color))
@@ -185,10 +198,10 @@ fun ColorEditor(
}
SectionSpacer()
val isInDarkTheme = isInDarkTheme()
TextButton(
onClick = {
ThemeManager.saveAndApplyPrimaryColor(currentColor)
ThemeManager.saveAndApplyPrimaryColor(currentColor, isInDarkTheme)
close()
},
Modifier.align(Alignment.CenterHorizontally),
@@ -241,9 +254,9 @@ private fun LangSelector(state: State<String>, onSelected: (String) -> Unit) {
}
@Composable
private fun ThemeSelector(state: State<DefaultTheme>, onSelected: (DefaultTheme) -> Unit) {
private fun ThemeSelector(state: State<String>, onSelected: (String) -> Unit) {
val darkTheme = isSystemInDarkTheme()
val values by remember { mutableStateOf(ThemeManager.allThemes(darkTheme).map { it.second to it.third }) }
val values by remember { mutableStateOf(ThemeManager.allThemes(darkTheme).map { it.second.name to it.third }) }
ExposedDropDownSettingRow(
generalGetString(R.string.theme),
values,
@@ -254,6 +267,24 @@ private fun ThemeSelector(state: State<DefaultTheme>, onSelected: (DefaultTheme)
)
}
@Composable
private fun DarkThemeSelector(state: State<String?>, onSelected: (String) -> Unit) {
val values by remember {
val darkThemes = ArrayList<Pair<String, String>>()
darkThemes.add(DefaultTheme.DARK.name to generalGetString(R.string.theme_dark))
darkThemes.add(DefaultTheme.BLUE.name to generalGetString(R.string.theme_blue))
mutableStateOf(darkThemes.toList())
}
ExposedDropDownSettingRow(
generalGetString(R.string.dark_theme),
values,
state,
icon = null,
enabled = remember { mutableStateOf(true) },
onSelected = { if (it != null) onSelected(it) }
)
}
//private fun openSystemLangPicker(activity: Activity) {
// activity.startActivity(Intent(Settings.ACTION_APP_LOCALE_SETTINGS, Uri.parse("package:" + SimplexApp.context.packageName)))
//}
@@ -271,6 +302,7 @@ fun PreviewAppearanceSettings() {
AppearanceLayout(
icon = remember { mutableStateOf(AppIcon.DARK_BLUE) },
languagePref = SharedPreference({ null }, {}),
systemDarkTheme = SharedPreference({ null }, {}),
changeIcon = {},
editPrimaryColor = {},
)
@@ -134,8 +134,9 @@ fun SettingsLayout(
showVersion: () -> Unit,
withAuth: (title: String, desc: String, block: () -> Unit) -> Unit
) {
val theme = CurrentColors.collectAsState()
val uriHandler = LocalUriHandler.current
Box(Modifier.fillMaxSize().verticalScroll(rememberScrollState()).background(MaterialTheme.colors.background)) {
Box(Modifier.fillMaxSize().verticalScroll(rememberScrollState()).themedBackground(theme.value.base)) {
Column(
Modifier
.fillMaxSize()
@@ -1156,12 +1156,15 @@
<string name="theme_system">System</string>
<string name="theme_light">Light</string>
<string name="theme_dark">Dark</string>
<string name="theme_blue">Blue</string>
<!-- Languages -->
<string name="language_system">System</string>
<!-- Appearance.kt -->
<string name="theme">Theme</string>
<string name="simplex_blue_as_dark_theme">Blue in dark mode</string>
<string name="dark_theme" translatable="false">Dark theme</string>
<string name="save_color">Save color</string>
<string name="reset_color">Reset colors</string>
<string name="color_primary">Accent</string>