Merge branch 'master' into master-ios

This commit is contained in:
Evgeny Poberezkin
2023-05-01 11:34:16 +01:00
208 changed files with 4596 additions and 1376 deletions
+68
View File
@@ -0,0 +1,68 @@
name: Bug
description: File a bug report/issue
title: "[Bug]: "
labels: ["type:bug", "type:triage"]
body:
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the bug you encountered.
options:
- label: I have searched the existing issues
required: true
- type: dropdown
attributes:
label: Platform
description: Multiple selections are possible.
multiple: true
options:
- Linux
- Mac
- Windows
- Android
- iOS
validations:
required: true
- type: input
attributes:
label: OS version
description: Specify the OS version
placeholder: ex. Android 12, Ubuntu 20.04
validations:
required: true
- type: input
attributes:
label: App version
description: Specify the SimpleX version
placeholder: ex. 4.3.2
validations:
required: true
- type: textarea
attributes:
label: Current Behavior
description: A concise description of what you're experiencing.
placeholder: Bug happened!
validations:
required: true
- type: textarea
attributes:
label: Expected Behavior
description: A concise description of what you expected to happen.
placeholder: No bug should happen!
validations:
required: true
- type: textarea
attributes:
label: Steps To Reproduce
description: Steps to reproduce the behavior.
placeholder: |
1. Go to ...
3. Click on ...
4. See error...
validations:
required: true
- type: textarea
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
render: shell
+1
View File
@@ -0,0 +1 @@
blank_issues_enabled: true
+40
View File
@@ -0,0 +1,40 @@
name: Feature
description: Suggest your feature
title: "[Feature]: "
labels: ["type:enhancement", "type:triage"]
body:
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the bug you encountered.
options:
- label: I have searched the existing issues
required: true
- type: dropdown
attributes:
label: Platform
description: Multiple selections are possible. If selected input is "all", this considered to be a general feature.
multiple: true
options:
- Linux
- Mac
- Windows
- Android
- iOS
- all
validations:
required: true
- type: input
attributes:
label: App version
description: Specify the SimpleX version
placeholder: ex. 4.3.2
validations:
required: false
- type: textarea
attributes:
label: Feature
description: Describe the feature you would like to see added
placeholder: SimpleX Chat should make me coffee!
validations:
required: true
+16
View File
@@ -0,0 +1,16 @@
name: Question
description: Ask your question
title: "[Q]: "
labels: ["type:question", "type:triage"]
body:
- type: markdown
attributes:
value: |
Generally, we encourage you to ask questions in our [official group](https://simplex.chat/invitation/#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3Dsimplex:/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D), but you can do it anyway :)
- type: textarea
attributes:
label: Question
description: Please ask your question in plain english.
placeholder: Is SimpleX - chat?
validations:
required: true
+1 -1
View File
@@ -49,8 +49,8 @@ logs/
# for website
website/node_modules/
website/src/blog/
website/src/docs/
website/translations.json
website/src/_data/supported_languages.json
website/src/img/images/
website/src/images/
# Generated files
+1
View File
@@ -119,6 +119,7 @@ dependencies {
implementation 'androidx.fragment:fragment:1.4.1'
implementation 'org.jetbrains.kotlinx:kotlinx-datetime:0.3.2'
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2'
implementation 'com.charleskorn.kaml:kaml:0.43.0'
//implementation "androidx.compose.material:material-icons-extended:$compose_version"
implementation "androidx.compose.ui:ui-util:$compose_version"
implementation "androidx.navigation:navigation-compose:2.4.1"
@@ -17,6 +17,7 @@ import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@@ -26,8 +27,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
@@ -83,11 +83,7 @@ class MainActivity: FragmentActivity() {
}
setContent {
SimpleXTheme {
Surface(
Modifier
.background(MaterialTheme.colors.background)
.fillMaxSize()
) {
Surface(color = MaterialTheme.colors.background) {
MainPage(
m,
userAuthorized,
@@ -420,7 +416,7 @@ fun MainPage(
@Composable
fun authView() {
Box(
Modifier.fillMaxSize(),
Modifier.fillMaxSize().background(MaterialTheme.colors.background),
contentAlignment = Alignment.Center
) {
SimpleButton(
@@ -633,7 +629,7 @@ fun connectIfOpenedViaUri(uri: Uri, chatModel: ChatModel) {
ConnectionLinkType.INVITATION -> generalGetString(R.string.connect_via_invitation_link)
ConnectionLinkType.GROUP -> generalGetString(R.string.connect_via_group_link)
}
AlertManager.shared.showAlertMsg(
AlertManager.shared.showAlertDialog(
title = title,
text = if (linkType == ConnectionLinkType.GROUP)
generalGetString(R.string.you_will_join_group)
@@ -6,6 +6,7 @@ import android.util.Log
import androidx.lifecycle.*
import androidx.work.*
import chat.simplex.app.model.*
import chat.simplex.app.ui.theme.DefaultTheme
import chat.simplex.app.views.helpers.*
import chat.simplex.app.views.onboarding.OnboardingStage
import chat.simplex.app.views.usersettings.NotificationsMode
@@ -95,6 +96,7 @@ class SimplexApp: Application(), LifecycleEventObserver {
initChatController()
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
context.getDir("temp", MODE_PRIVATE).deleteRecursively()
runMigrations()
}
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
@@ -184,6 +186,23 @@ class SimplexApp: Application(), LifecycleEventObserver {
MessagesFetcherWorker.scheduleWork()
}
private fun runMigrations() {
val lastMigration = chatModel.controller.appPrefs.lastMigratedVersionCode
if (lastMigration.get() < BuildConfig.VERSION_CODE) {
while (true) {
if (lastMigration.get() < 117) {
if (chatModel.controller.appPrefs.currentTheme.get() == DefaultTheme.DARK.name) {
chatModel.controller.appPrefs.currentTheme.set(DefaultTheme.SIMPLEX.name)
}
lastMigration.set(117)
} else {
lastMigration.set(BuildConfig.VERSION_CODE)
break
}
}
}
}
companion object {
lateinit var context: SimplexApp private set
@@ -1516,7 +1516,7 @@ data class CIMeta (
val isRcvNew: Boolean get() = itemStatus is CIStatus.RcvNew
fun statusIcon(primaryColor: Color, metaColor: Color = HighOrLowlight): Pair<Int, Color>? =
fun statusIcon(primaryColor: Color, metaColor: Color = CurrentColors.value.colors.secondary): Pair<Int, Color>? =
when (itemStatus) {
is CIStatus.SndSent -> R.drawable.ic_check_filled to metaColor
is CIStatus.SndErrorAuth -> R.drawable.ic_close to Color.Red
@@ -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
@@ -26,10 +25,14 @@ import chat.simplex.app.views.call.*
import chat.simplex.app.views.newchat.ConnectViaLinkTab
import chat.simplex.app.views.onboarding.OnboardingStage
import chat.simplex.app.views.usersettings.*
import com.charleskorn.kaml.Yaml
import com.charleskorn.kaml.YamlConfiguration
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 +60,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,9 +151,15 @@ 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.SIMPLEX.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)
val lastMigratedVersionCode = mkIntPreference(SHARED_PREFS_LAST_MIGRATED_VERSION_CODE, 0)
private fun mkIntPreference(prefName: String, default: Int) =
SharedPreference(
@@ -204,8 +214,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,8 +275,10 @@ 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"
private const val SHARED_PREFS_LAST_MIGRATED_VERSION_CODE = "LastMigratedVersionCode"
}
}
@@ -2581,7 +2600,7 @@ data class FeatureEnabled(
}
val iconColor: Color
get() = if (forUser) SimplexGreen else if (forContact) WarningYellow else HighOrLowlight
get() = if (forUser) SimplexGreen else if (forContact) WarningYellow else CurrentColors.value.colors.secondary
companion object {
fun enabled(asymmetric: Boolean, user: ChatPreference, contact: ChatPreference): FeatureEnabled =
@@ -2965,7 +2984,7 @@ enum class GroupFeatureEnabled {
}
val iconColor: Color
get() = if (this == ON) SimplexGreen else HighOrLowlight
get() = if (this == ON) SimplexGreen else CurrentColors.value.colors.secondary
}
@@ -2976,6 +2995,11 @@ val json = Json {
explicitNulls = false
}
val yaml = Yaml(configuration = YamlConfiguration(
strictMode = false,
encodeDefaults = false,
))
@Serializable
class APIResponse(val resp: CR, val corr: String? = null) {
companion object {
@@ -13,7 +13,6 @@ val SimplexGreen = Color(77, 218, 103, 255)
val SecretColor = Color(0x40808080)
val LightGray = Color(241, 242, 246, 255)
val DarkGray = Color(43, 44, 46, 255)
val DarkGrayBackground = Color(38, 38, 38, 255)
val HighOrLowlight = Color(139, 135, 134, 255)
val MessagePreviewDark = Color(179, 175, 174, 255)
val MessagePreviewLight = Color(49, 45, 44, 255)
@@ -22,7 +21,6 @@ val ToolbarDark = Color(80, 80, 80, 12)
val SettingsSecondaryLight = Color(200, 196, 195, 90)
val GroupDark = Color(80, 80, 80, 60)
val IncomingCallLight = Color(239, 237, 236, 255)
val IncomingCallDark = Color(34, 30, 29, 255)
val WarningOrange = Color(255, 127, 0, 255)
val WarningYellow = Color(255, 192, 0, 255)
val FileLight = Color(183, 190, 199, 255)
@@ -2,16 +2,189 @@ 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.R
import chat.simplex.app.SimplexApp
import chat.simplex.app.views.helpers.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import okhttp3.internal.toHexString
enum class DefaultTheme {
SYSTEM, DARK, LIGHT
SYSTEM, LIGHT, DARK, SIMPLEX;
// Call it only with base theme, not SYSTEM
fun hasChangedAnyColor(colors: Colors, appColors: AppColors): Boolean {
val palette = when (this) {
SYSTEM -> return false
LIGHT -> LightColorPalette
DARK -> DarkColorPalette
SIMPLEX -> SimplexColorPalette
}
val appPalette = when (this) {
SYSTEM -> return false
LIGHT -> LightColorPaletteApp
DARK -> DarkColorPaletteApp
SIMPLEX -> SimplexColorPaletteApp
}
return colors.primary != palette.primary ||
colors.primaryVariant != palette.primaryVariant ||
colors.secondary != palette.secondary ||
colors.secondaryVariant != palette.secondaryVariant ||
colors.background != palette.background ||
colors.surface != palette.surface ||
appColors != appPalette
}
}
data class AppColors(
val title: Color,
val sentMessage: Color,
val receivedMessage: Color
)
enum class ThemeColor {
PRIMARY, PRIMARY_VARIANT, SECONDARY, SECONDARY_VARIANT, BACKGROUND, SURFACE, TITLE, SENT_MESSAGE, RECEIVED_MESSAGE;
fun fromColors(colors: Colors, appColors: AppColors): Color {
return when (this) {
PRIMARY -> colors.primary
PRIMARY_VARIANT -> colors.primaryVariant
SECONDARY -> colors.secondary
SECONDARY_VARIANT -> colors.secondaryVariant
BACKGROUND -> colors.background
SURFACE -> colors.surface
TITLE -> appColors.title
SENT_MESSAGE -> appColors.sentMessage
RECEIVED_MESSAGE -> appColors.receivedMessage
}
}
val text: String
get() = when (this) {
PRIMARY -> generalGetString(R.string.color_primary)
PRIMARY_VARIANT -> generalGetString(R.string.color_primary_variant)
SECONDARY -> generalGetString(R.string.color_secondary)
SECONDARY_VARIANT -> generalGetString(R.string.color_secondary_variant)
BACKGROUND -> generalGetString(R.string.color_background)
SURFACE -> generalGetString(R.string.color_surface)
TITLE -> generalGetString(R.string.color_title)
SENT_MESSAGE -> generalGetString(R.string.color_sent_message)
RECEIVED_MESSAGE -> generalGetString(R.string.color_received_message)
}
}
@Serializable
data class ThemeColors(
@SerialName("accent")
val primary: String? = null,
@SerialName("accentVariant")
val primaryVariant: String? = null,
val secondary: String? = null,
val secondaryVariant: String? = null,
val background: String? = null,
@SerialName("menus")
val surface: String? = null,
val title: String? = null,
val sentMessage: String? = null,
val receivedMessage: String? = null,
) {
fun toColors(base: DefaultTheme): Colors {
val baseColors = when (base) {
DefaultTheme.LIGHT -> LightColorPalette
DefaultTheme.DARK -> DarkColorPalette
DefaultTheme.SIMPLEX -> SimplexColorPalette
// shouldn't be here
DefaultTheme.SYSTEM -> LightColorPalette
}
return baseColors.copy(
primary = primary?.colorFromReadableHex() ?: baseColors.primary,
primaryVariant = primaryVariant?.colorFromReadableHex() ?: baseColors.primaryVariant,
secondary = secondary?.colorFromReadableHex() ?: baseColors.secondary,
secondaryVariant = secondaryVariant?.colorFromReadableHex() ?: baseColors.secondaryVariant,
background = background?.colorFromReadableHex() ?: baseColors.background,
surface = surface?.colorFromReadableHex() ?: baseColors.surface,
)
}
fun toAppColors(base: DefaultTheme): AppColors {
val baseColors = when (base) {
DefaultTheme.LIGHT -> LightColorPaletteApp
DefaultTheme.DARK -> DarkColorPaletteApp
DefaultTheme.SIMPLEX -> SimplexColorPaletteApp
// shouldn't be here
DefaultTheme.SYSTEM -> LightColorPaletteApp
}
return baseColors.copy(
title = title?.colorFromReadableHex() ?: baseColors.title,
sentMessage = sentMessage?.colorFromReadableHex() ?: baseColors.sentMessage,
receivedMessage = receivedMessage?.colorFromReadableHex() ?: baseColors.receivedMessage,
)
}
fun withFilledColors(base: DefaultTheme): ThemeColors {
val c = toColors(base)
val ac = toAppColors(base)
return ThemeColors(
primary = c.primary.toReadableHex(),
primaryVariant = c.primaryVariant.toReadableHex(),
secondary = c.secondary.toReadableHex(),
secondaryVariant = c.secondaryVariant.toReadableHex(),
background = c.background.toReadableHex(),
surface = c.surface.toReadableHex(),
title = ac.title.toReadableHex(),
sentMessage = ac.sentMessage.toReadableHex(),
receivedMessage = ac.receivedMessage.toReadableHex()
)
}
}
private fun String.colorFromReadableHex(): Color =
Color(this.replace("#", "").toLongOrNull(16) ?: Color.White.toArgb().toLong())
private fun Color.toReadableHex(): String = "#" + toArgb().toHexString()
@Serializable
data class ThemeOverrides (
val base: DefaultTheme,
val colors: ThemeColors
) {
fun withUpdatedColor(name: ThemeColor, color: String): ThemeOverrides {
return copy(colors = when (name) {
ThemeColor.PRIMARY -> colors.copy(primary = color)
ThemeColor.PRIMARY_VARIANT -> colors.copy(primaryVariant = color)
ThemeColor.SECONDARY -> colors.copy(secondary = color)
ThemeColor.SECONDARY_VARIANT -> colors.copy(secondaryVariant = color)
ThemeColor.BACKGROUND -> colors.copy(background = color)
ThemeColor.SURFACE -> colors.copy(surface = color)
ThemeColor.TITLE -> colors.copy(title = color)
ThemeColor.SENT_MESSAGE -> colors.copy(sentMessage = color)
ThemeColor.RECEIVED_MESSAGE -> colors.copy(receivedMessage = color)
})
}
}
fun Modifier.themedBackground(baseTheme: DefaultTheme = CurrentColors.value.base, shape: Shape = RectangleShape): Modifier {
return if (baseTheme == DefaultTheme.SIMPLEX) {
this.background(brush = Brush.linearGradient(
listOf(
CurrentColors.value.colors.background.darker(0.4f),
CurrentColors.value.colors.background.lighter(0.4f)
),
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 +195,11 @@ 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,
secondary = DarkGray,
primaryVariant = SimplexBlue,
secondary = HighOrLowlight,
secondaryVariant = DarkGray,
// background = Color.Black,
// surface = Color.Black,
surface = Color(0xFF222222),
// background = Color(0xFF121212),
// surface = Color(0xFF121212),
error = Color.Red,
@@ -33,27 +207,59 @@ val DarkColorPalette = darkColors(
onSurface = Color(0xFFFFFBFA),
// onError: Color = Color.Black,
)
val DarkColorPaletteApp = AppColors(
title = SimplexBlue,
sentMessage = Color(0x1E45B8FF),
receivedMessage = Color(0x20B1B0B5)
)
val LightColorPalette = lightColors(
primary = SimplexBlue, // If this value changes also need to update #0088ff in string resource files
primaryVariant = SimplexGreen,
secondary = LightGray,
primaryVariant = SimplexBlue,
secondary = HighOrLowlight,
secondaryVariant = 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 LightColorPaletteApp = AppColors(
title = SimplexBlue,
sentMessage = Color(0x1E45B8FF),
receivedMessage = Color(0x20B1B0B5)
)
val CurrentColors: MutableStateFlow<Pair<Colors, DefaultTheme>> = MutableStateFlow(ThemeManager.currentColors(isInNightMode()))
val SimplexColorPalette = darkColors(
primary = Color(0xFF70F0F9), // If this value changes also need to update #0088ff in string resource files
primaryVariant = Color(0xFF1298A5),
secondary = HighOrLowlight,
secondaryVariant = Color(0xFF2C464D),
background = Color(0xFF111528),
// surface = Color.Black,
// background = Color(0xFF121212),
surface = Color(0xFF121C37),
error = Color.Red,
// onBackground = Color(0xFFFFFBFA),
// onSurface = Color(0xFFFFFBFA),
// onError: Color = Color.Black,
)
val SimplexColorPaletteApp = AppColors(
title = Color(0xFF267BE5),
sentMessage = Color(0x1E45B8FF),
receivedMessage = Color(0x20B1B0B5)
)
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 +270,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,53 @@ 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, val appColors: AppColors)
private fun systemDarkThemeColors(): Pair<Colors, DefaultTheme> = when (appPrefs.systemDarkTheme.get()) {
DefaultTheme.DARK.name -> DarkColorPalette to DefaultTheme.DARK
DefaultTheme.SIMPLEX.name -> SimplexColorPalette to DefaultTheme.SIMPLEX
else -> SimplexColorPalette to DefaultTheme.SIMPLEX
}
fun currentColors(darkForSystemTheme: Boolean): ActiveTheme {
val themeName = appPrefs.currentTheme.get()!!
val themeOverrides = appPrefs.themeOverrides.get()
val nonSystemThemeName = if (themeName != DefaultTheme.SYSTEM.name) {
themeName
} else {
if (darkForSystemTheme) appPrefs.systemDarkTheme.get()!! else DefaultTheme.LIGHT.name
}
return res.copy(first = res.first.copy(primary = Color(appPrefs.primaryColor.get())))
val theme = themeOverrides[nonSystemThemeName]
val baseTheme = when (nonSystemThemeName) {
DefaultTheme.LIGHT.name -> Triple(DefaultTheme.LIGHT, LightColorPalette, LightColorPaletteApp)
DefaultTheme.DARK.name -> Triple(DefaultTheme.DARK, DarkColorPalette, DarkColorPaletteApp)
DefaultTheme.SIMPLEX.name -> Triple(DefaultTheme.SIMPLEX, SimplexColorPalette, SimplexColorPaletteApp)
else -> Triple(DefaultTheme.LIGHT, LightColorPalette, LightColorPaletteApp)
}
if (theme == null) {
return ActiveTheme(themeName, baseTheme.first, baseTheme.second, baseTheme.third)
}
return ActiveTheme(themeName, baseTheme.first, theme.colors.toColors(theme.base), theme.colors.toAppColors(theme.base))
}
fun currentThemeOverridesForExport(darkForSystemTheme: Boolean): ThemeOverrides {
val themeName = appPrefs.currentTheme.get()!!
val nonSystemThemeName = if (themeName != DefaultTheme.SYSTEM.name) {
themeName
} else {
if (darkForSystemTheme) appPrefs.systemDarkTheme.get()!! else DefaultTheme.LIGHT.name
}
val overrides = appPrefs.themeOverrides.get().toMutableMap()
val nonFilledTheme = overrides[nonSystemThemeName] ?: ThemeOverrides(base = CurrentColors.value.base, colors = ThemeColors())
return nonFilledTheme.copy(colors = nonFilledTheme.colors.withFilledColors(CurrentColors.value.base))
}
// colors, default theme enum, localized name of theme
@@ -30,7 +61,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 +80,73 @@ object ThemeManager {
generalGetString(R.string.theme_dark)
)
)
allThemes.add(
Triple(
SimplexColorPalette,
DefaultTheme.SIMPLEX,
generalGetString(R.string.theme_simplex)
)
)
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 saveAndApplyThemeColor(name: ThemeColor, 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
}
var colorToSet = color
if (colorToSet == null) {
// Setting default color from a base theme
colorToSet = when(nonSystemThemeName) {
DefaultTheme.LIGHT.name -> name.fromColors(LightColorPalette, LightColorPaletteApp)
DefaultTheme.DARK.name -> name.fromColors(DarkColorPalette, DarkColorPaletteApp)
DefaultTheme.SIMPLEX.name -> name.fromColors(SimplexColorPalette, SimplexColorPaletteApp)
// Will not be here
else -> return
}
}
val overrides = appPrefs.themeOverrides.get().toMutableMap()
val prevValue = overrides[nonSystemThemeName] ?: ThemeOverrides(base = CurrentColors.value.base, colors = ThemeColors())
overrides[nonSystemThemeName] = prevValue.withUpdatedColor(name, colorToSet.toReadableHex())
appPrefs.themeOverrides.set(overrides)
CurrentColors.value = currentColors(!CurrentColors.value.colors.isLight)
}
fun saveAndApplyThemeOverrides(theme: ThemeOverrides, darkForSystemTheme: Boolean) {
val overrides = appPrefs.themeOverrides.get().toMutableMap()
val prevValue = overrides[theme.base.name] ?: ThemeOverrides(base = CurrentColors.value.base, colors = ThemeColors())
overrides[theme.base.name] = prevValue.copy(colors = theme.colors)
appPrefs.themeOverrides.set(overrides)
appPrefs.currentTheme.set(theme.base.name)
CurrentColors.value = currentColors(!CurrentColors.value.colors.isLight)
}
fun resetAllThemeColors(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 overrides = appPrefs.themeOverrides.get().toMutableMap()
val prevValue = overrides[nonSystemThemeName] ?: return
overrides[nonSystemThemeName] = prevValue.copy(colors = ThemeColors())
appPrefs.themeOverrides.set(overrides)
CurrentColors.value = currentColors(!CurrentColors.value.colors.isLight)
}
}
private fun Color.toReadableHex(): String = "#" + toArgb().toHexString()
@@ -1,6 +1,5 @@
package chat.simplex.app.views
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
@@ -11,8 +10,8 @@ import androidx.compose.ui.Modifier
fun SplashView() {
Surface(
Modifier
.background(MaterialTheme.colors.background)
.fillMaxSize()
.fillMaxSize(),
color = MaterialTheme.colors.background
) {
// Image(
// painter = painterResource(R.drawable.logo),
@@ -99,8 +99,8 @@ fun TerminalLayout(
Surface(
modifier = Modifier
.padding(contentPadding)
.fillMaxWidth()
.background(MaterialTheme.colors.background)
.fillMaxWidth(),
color = MaterialTheme.colors.background
) {
TerminalLog(terminalItems)
}
@@ -44,77 +44,75 @@ fun CreateProfilePanel(chatModel: ChatModel, close: () -> Unit) {
val fullName = rememberSaveable { mutableStateOf("") }
val focusRequester = remember { FocusRequester() }
Surface(Modifier.background(MaterialTheme.colors.onBackground)) {
Column(
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())
) {
/*CloseSheetBar(close = {
if (chatModel.users.isEmpty()) {
chatModel.onboardingStage.value = OnboardingStage.Step1_SimpleXInfo
} else {
close()
}
})*/
Column(Modifier.padding(horizontal = DEFAULT_PADDING * 1f)) {
AppBarTitle(stringResource(R.string.create_profile))
ReadableText(R.string.your_profile_is_stored_on_your_device, TextAlign.Center, padding = PaddingValues())
ReadableText(R.string.profile_is_only_shared_with_your_contacts, TextAlign.Center)
Spacer(Modifier.height(DEFAULT_PADDING * 1.5f))
Row(Modifier.padding(bottom = DEFAULT_PADDING_HALF).fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Text(
stringResource(R.string.display_name),
fontSize = 16.sp
)
if (!isValidDisplayName(displayName.value)) {
Text(
stringResource(R.string.no_spaces),
fontSize = 16.sp,
color = Color.Red
)
}
}
ProfileNameField(displayName, "", ::isValidDisplayName, focusRequester)
Spacer(Modifier.height(DEFAULT_PADDING))
Column(
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())
) {
/*CloseSheetBar(close = {
if (chatModel.users.isEmpty()) {
chatModel.onboardingStage.value = OnboardingStage.Step1_SimpleXInfo
} else {
close()
}
})*/
Column(Modifier.padding(horizontal = DEFAULT_PADDING * 1f)) {
AppBarTitle(stringResource(R.string.create_profile))
ReadableText(R.string.your_profile_is_stored_on_your_device, TextAlign.Center, padding = PaddingValues())
ReadableText(R.string.profile_is_only_shared_with_your_contacts, TextAlign.Center)
Spacer(Modifier.height(DEFAULT_PADDING * 1.5f))
Row(Modifier.padding(bottom = DEFAULT_PADDING_HALF).fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Text(
stringResource(R.string.full_name_optional__prompt),
fontSize = 16.sp,
modifier = Modifier.padding(bottom = DEFAULT_PADDING_HALF)
stringResource(R.string.display_name),
fontSize = 16.sp
)
ProfileNameField(fullName, "", ::isValidDisplayName)
}
Spacer(Modifier.fillMaxHeight().weight(1f))
Row {
if (chatModel.users.isEmpty()) {
SimpleButtonDecorated(
text = stringResource(R.string.about_simplex),
icon = painterResource(R.drawable.ic_arrow_back_ios_new),
textDecoration = TextDecoration.None,
fontWeight = FontWeight.Medium
) { chatModel.onboardingStage.value = OnboardingStage.Step1_SimpleXInfo }
}
Spacer(Modifier.fillMaxWidth().weight(1f))
val enabled = displayName.value.isNotEmpty() && isValidDisplayName(displayName.value)
val createModifier: Modifier
val createColor: Color
if (enabled) {
createModifier = Modifier.clickable { createProfile(chatModel, displayName.value, fullName.value, close) }.padding(8.dp)
createColor = MaterialTheme.colors.primary
} else {
createModifier = Modifier.padding(8.dp)
createColor = HighOrLowlight
}
Surface(shape = RoundedCornerShape(20.dp)) {
Row(verticalAlignment = Alignment.CenterVertically, modifier = createModifier) {
Text(stringResource(R.string.create_profile_button), style = MaterialTheme.typography.caption, color = createColor, fontWeight = FontWeight.Medium)
Icon(painterResource(R.drawable.ic_arrow_forward_ios), stringResource(R.string.create_profile_button), tint = createColor)
}
if (!isValidDisplayName(displayName.value)) {
Text(
stringResource(R.string.no_spaces),
fontSize = 16.sp,
color = Color.Red
)
}
}
ProfileNameField(displayName, "", ::isValidDisplayName, focusRequester)
Spacer(Modifier.height(DEFAULT_PADDING))
Text(
stringResource(R.string.full_name_optional__prompt),
fontSize = 16.sp,
modifier = Modifier.padding(bottom = DEFAULT_PADDING_HALF)
)
ProfileNameField(fullName, "", ::isValidDisplayName)
}
Spacer(Modifier.fillMaxHeight().weight(1f))
Row {
if (chatModel.users.isEmpty()) {
SimpleButtonDecorated(
text = stringResource(R.string.about_simplex),
icon = painterResource(R.drawable.ic_arrow_back_ios_new),
textDecoration = TextDecoration.None,
fontWeight = FontWeight.Medium
) { chatModel.onboardingStage.value = OnboardingStage.Step1_SimpleXInfo }
}
Spacer(Modifier.fillMaxWidth().weight(1f))
val enabled = displayName.value.isNotEmpty() && isValidDisplayName(displayName.value)
val createModifier: Modifier
val createColor: Color
if (enabled) {
createModifier = Modifier.clickable { createProfile(chatModel, displayName.value, fullName.value, close) }.padding(8.dp)
createColor = MaterialTheme.colors.primary
} else {
createModifier = Modifier.padding(8.dp)
createColor = MaterialTheme.colors.secondary
}
Surface(shape = RoundedCornerShape(20.dp), color = Color.Transparent) {
Row(verticalAlignment = Alignment.CenterVertically, modifier = createModifier) {
Text(stringResource(R.string.create_profile_button), style = MaterialTheme.typography.caption, color = createColor, fontWeight = FontWeight.Medium)
Icon(painterResource(R.drawable.ic_arrow_forward_ios), stringResource(R.string.create_profile_button), tint = createColor)
}
}
}
LaunchedEffect(Unit) {
delay(300)
focusRequester.requestFocus()
}
LaunchedEffect(Unit) {
delay(300)
focusRequester.requestFocus()
}
}
}
@@ -147,9 +145,9 @@ fun ProfileNameField(name: MutableState<String>, placeholder: String = "", isVal
derivedStateOf {
if (valid) {
if (focused) {
HighOrLowlight.copy(alpha = 0.6f)
CurrentColors.value.colors.secondary.copy(alpha = 0.6f)
} else {
HighOrLowlight.copy(alpha = 0.3f)
CurrentColors.value.colors.secondary.copy(alpha = 0.3f)
}
} else Color.Red
}
@@ -176,7 +174,7 @@ fun ProfileNameField(name: MutableState<String>, placeholder: String = "", isVal
autoCorrect = false
),
singleLine = true,
cursorBrush = SolidColor(HighOrLowlight)
cursorBrush = SolidColor(MaterialTheme.colors.secondary)
)
}
LaunchedEffect(Unit) {
@@ -316,7 +316,7 @@ private fun ActiveCallOverlayLayout(
private fun ControlButton(call: Call, icon: Painter, @StringRes iconText: Int, action: () -> Unit, enabled: Boolean = true) {
if (call.hasMedia) {
IconButton(onClick = action, enabled = enabled) {
Icon(icon, stringResource(iconText), tint = if (enabled) Color(0xFFFFFFD8) else HighOrLowlight, modifier = Modifier.size(40.dp))
Icon(icon, stringResource(iconText), tint = if (enabled) Color(0xFFFFFFD8) else MaterialTheme.colors.secondary, modifier = Modifier.size(40.dp))
}
} else {
Spacer(Modifier.size(40.dp))
@@ -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,8 +97,9 @@ fun IncomingCallActivityView(m: ChatModel) {
SimpleXTheme {
Surface(
Modifier
.background(MaterialTheme.colors.background)
.fillMaxSize()) {
.fillMaxSize(),
color = MaterialTheme.colors.background
) {
if (showCallView) {
Box {
ActiveCallView(m)
@@ -212,7 +213,7 @@ private fun LockScreenCallButton(text: String, icon: Painter, color: Color, acti
Icon(icon, text, tint = color, modifier = Modifier.scale(1.75f))
}
Spacer(Modifier.height(DEFAULT_PADDING))
Text(text, style = MaterialTheme.typography.body2, color = HighOrLowlight)
Text(text, style = MaterialTheme.typography.body2, color = MaterialTheme.colors.secondary)
}
}
}
@@ -226,8 +227,9 @@ fun PreviewIncomingCallLockScreenAlert() {
SimpleXTheme(true) {
Surface(
Modifier
.background(MaterialTheme.colors.background)
.fillMaxSize()) {
.fillMaxSize(),
color = MaterialTheme.colors.background
) {
IncomingCallLockScreenAlertLayout(
invitation = RcvCallInvitation(
user = User.sampleData,
@@ -50,13 +50,13 @@ fun IncomingCallAlertLayout(
ignoreCall: () -> Unit,
acceptCall: () -> Unit
) {
val color = if (isInDarkTheme()) IncomingCallDark else IncomingCallLight
val color = if (isInDarkTheme()) MaterialTheme.colors.surface else IncomingCallLight
Column(Modifier.fillMaxWidth().background(color).padding(top = DEFAULT_PADDING, bottom = DEFAULT_PADDING, start = DEFAULT_PADDING, end = 8.dp)) {
IncomingCallInfo(invitation, chatModel)
Spacer(Modifier.height(8.dp))
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween) {
Row(Modifier.fillMaxWidth().weight(1f), verticalAlignment = Alignment.CenterVertically) {
ProfilePreview(profileOf = invitation.contact, size = 64.dp, color = Color.White)
ProfilePreview(profileOf = invitation.contact, size = 64.dp)
}
Row(verticalAlignment = Alignment.CenterVertically) {
CallButton(stringResource(R.string.reject), painterResource(R.drawable.ic_call_end_filled), Color.Red, rejectCall)
@@ -72,13 +72,13 @@ fun IncomingCallInfo(invitation: RcvCallInvitation, chatModel: ChatModel) {
@Composable fun CallIcon(icon: Painter, descr: String) = Icon(icon, descr, tint = SimplexGreen)
Row(verticalAlignment = Alignment.CenterVertically) {
if (chatModel.users.size > 1) {
ProfileImage(size = 32.dp, image = invitation.user.profile.image, color = MaterialTheme.colors.secondary)
ProfileImage(size = 32.dp, image = invitation.user.profile.image, color = MaterialTheme.colors.secondaryVariant)
Spacer(Modifier.width(4.dp))
}
if (invitation.callType.media == CallMediaType.Video) CallIcon(painterResource(R.drawable.ic_videocam_filled), stringResource(R.string.icon_descr_video_call))
else CallIcon(painterResource(R.drawable.ic_call_filled), stringResource(R.string.icon_descr_audio_call))
Spacer(Modifier.width(4.dp))
Text(invitation.callTypeText)
Text(invitation.callTypeText, color = MaterialTheme.colors.onBackground)
}
}
@@ -96,7 +96,7 @@ private fun CallButton(text: String, icon: Painter, color: Color, action: () ->
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(icon, text, tint = color, modifier = Modifier.scale(1.2f))
Text(text, style = MaterialTheme.typography.body2, color = HighOrLowlight)
Text(text, style = MaterialTheme.typography.body2, color = MaterialTheme.colors.secondary)
}
}
}
@@ -111,7 +111,7 @@ fun ChatInfoView(
}
fun deleteContactDialog(chatInfo: ChatInfo, chatModel: ChatModel, close: (() -> Unit)? = null) {
AlertManager.shared.showAlertMsg(
AlertManager.shared.showAlertDialog(
title = generalGetString(R.string.delete_contact_question),
text = generalGetString(R.string.delete_contact_all_messages_deleted_cannot_undo_warning),
confirmText = generalGetString(R.string.delete_verb),
@@ -125,12 +125,13 @@ fun deleteContactDialog(chatInfo: ChatInfo, chatModel: ChatModel, close: (() ->
close?.invoke()
}
}
}
},
destructive = true,
)
}
fun clearChatDialog(chatInfo: ChatInfo, chatModel: ChatModel, close: (() -> Unit)? = null) {
AlertManager.shared.showAlertMsg(
AlertManager.shared.showAlertDialog(
title = generalGetString(R.string.clear_chat_question),
text = generalGetString(R.string.clear_chat_warning),
confirmText = generalGetString(R.string.clear_verb),
@@ -143,7 +144,8 @@ fun clearChatDialog(chatInfo: ChatInfo, chatModel: ChatModel, close: (() -> Unit
close?.invoke()
}
}
}
},
destructive = true,
)
}
@@ -240,7 +242,7 @@ fun ChatInfoHeader(cInfo: ChatInfo, contact: Contact) {
ChatInfoImage(cInfo, size = 192.dp, iconColor = if (isInDarkTheme()) GroupDark else SettingsSecondaryLight)
Row(Modifier.padding(bottom = 8.dp), verticalAlignment = Alignment.CenterVertically) {
if (contact.verified) {
Icon(painterResource(R.drawable.ic_verified_user), null, Modifier.padding(end = 6.dp, top = 4.dp).size(24.dp), tint = HighOrLowlight)
Icon(painterResource(R.drawable.ic_verified_user), null, Modifier.padding(end = 6.dp, top = 4.dp).size(24.dp), tint = MaterialTheme.colors.secondary)
}
Text(
contact.profile.displayName, style = MaterialTheme.typography.h1.copy(fontWeight = FontWeight.Normal),
@@ -281,13 +283,13 @@ fun LocalAliasEditor(
Text(
generalGetString(R.string.text_field_set_contact_placeholder),
textAlign = if (center) TextAlign.Center else TextAlign.Start,
color = HighOrLowlight
color = MaterialTheme.colors.secondary
)
},
leadingIcon = if (leadingIcon) {
{ Icon(painterResource(R.drawable.ic_edit_filled), null, Modifier.padding(start = 7.dp)) }
} else null,
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
focus = focus,
textStyle = TextStyle.Default.copy(textAlign = if (value.isEmpty() || !center) TextAlign.Start else TextAlign.Center),
keyboardActions = KeyboardActions(onDone = { updateValue(value) })
@@ -334,7 +336,7 @@ private fun NetworkStatusRow(networkStatus: NetworkStatus) {
) {
Text(
networkStatus.statusString,
color = HighOrLowlight
color = MaterialTheme.colors.secondary
)
ServerImage(networkStatus)
}
@@ -348,10 +350,10 @@ private fun ServerImage(networkStatus: NetworkStatus) {
is NetworkStatus.Connected ->
Icon(painterResource(R.drawable.ic_circle_filled), stringResource(R.string.icon_descr_server_status_connected), tint = MaterialTheme.colors.primaryVariant)
is NetworkStatus.Disconnected ->
Icon(painterResource(R.drawable.ic_pending_filled), stringResource(R.string.icon_descr_server_status_disconnected), tint = HighOrLowlight)
Icon(painterResource(R.drawable.ic_pending_filled), stringResource(R.string.icon_descr_server_status_disconnected), tint = MaterialTheme.colors.secondary)
is NetworkStatus.Error ->
Icon(painterResource(R.drawable.ic_error_filled), stringResource(R.string.icon_descr_server_status_error), tint = HighOrLowlight)
else -> Icon(painterResource(R.drawable.ic_circle), stringResource(R.string.icon_descr_server_status_pending), tint = HighOrLowlight)
Icon(painterResource(R.drawable.ic_error_filled), stringResource(R.string.icon_descr_server_status_error), tint = MaterialTheme.colors.secondary)
else -> Icon(painterResource(R.drawable.ic_circle), stringResource(R.string.icon_descr_server_status_pending), tint = MaterialTheme.colors.secondary)
}
}
}
@@ -379,7 +381,7 @@ fun VerifyCodeButton(contactVerified: Boolean, onClick: () -> Unit) {
if (contactVerified) painterResource(R.drawable.ic_verified_user) else painterResource(R.drawable.ic_shield),
stringResource(if (contactVerified) R.string.view_security_code else R.string.verify_security_code),
click = onClick,
iconColor = HighOrLowlight,
iconColor = MaterialTheme.colors.secondary,
)
}
@@ -421,13 +423,14 @@ private fun setContactAlias(contactApiId: Long, localAlias: String, chatModel: C
}
private fun showSwitchContactAddressAlert(m: ChatModel, contactId: Long) {
AlertManager.shared.showAlertMsg(
AlertManager.shared.showAlertDialog(
title = generalGetString(R.string.switch_receiving_address_question),
text = generalGetString(R.string.switch_receiving_address_desc),
confirmText = generalGetString(R.string.switch_verb),
onConfirm = {
switchContactAddress(m, contactId)
}
},
destructive = true,
)
}
@@ -326,7 +326,6 @@ fun ChatLayout(
Box(
Modifier
.fillMaxWidth()
.background(MaterialTheme.colors.background)
) {
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
ModalBottomSheetLayout(
@@ -462,7 +461,7 @@ fun ChatInfoToolbar(
}
@Composable
fun ChatInfoToolbarTitle(cInfo: ChatInfo, imageSize: Dp = 40.dp, iconColor: Color = MaterialTheme.colors.secondary) {
fun ChatInfoToolbarTitle(cInfo: ChatInfo, imageSize: Dp = 40.dp, iconColor: Color = MaterialTheme.colors.secondaryVariant) {
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
@@ -496,7 +495,7 @@ fun ChatInfoToolbarTitle(cInfo: ChatInfo, imageSize: Dp = 40.dp, iconColor: Colo
@Composable
private fun ContactVerifiedShield() {
Icon(painterResource(R.drawable.ic_verified_user), null, Modifier.size(18.dp).padding(end = 3.dp, top = 1.dp), tint = HighOrLowlight)
Icon(painterResource(R.drawable.ic_verified_user), null, Modifier.size(18.dp).padding(end = 3.dp, top = 1.dp), tint = MaterialTheme.colors.secondary)
}
data class CIListState(val scrolled: Boolean, val itemCount: Int, val keyboardState: KeyboardState)
@@ -2,6 +2,7 @@ import androidx.compose.foundation.background
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.res.painterResource
@@ -10,16 +11,16 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import chat.simplex.app.R
import chat.simplex.app.ui.theme.*
import chat.simplex.app.views.chat.item.SentColorLight
@Composable
fun ComposeFileView(fileName: String, cancelFile: () -> Unit, cancelEnabled: Boolean) {
val sentColor = CurrentColors.collectAsState().value.appColors.sentMessage
Row(
Modifier
.height(60.dp)
.fillMaxWidth()
.padding(top = 8.dp)
.background(SentColorLight),
.background(sentColor),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
@@ -14,21 +14,18 @@ import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.app.R
import chat.simplex.app.model.durationText
import chat.simplex.app.ui.theme.DEFAULT_PADDING_HALF
import chat.simplex.app.ui.theme.HighOrLowlight
import chat.simplex.app.views.chat.item.SentColorLight
import chat.simplex.app.ui.theme.*
import chat.simplex.app.views.helpers.UploadContent
import chat.simplex.app.views.helpers.base64ToBitmap
@Composable
fun ComposeImageView(media: ComposePreview.MediaPreview, cancelImages: () -> Unit, cancelEnabled: Boolean) {
val sentColor = CurrentColors.collectAsState().value.appColors.sentMessage
Row(
Modifier
.padding(top = 8.dp)
.background(SentColorLight),
.background(sentColor),
verticalAlignment = Alignment.CenterVertically,
) {
LazyRow(
@@ -33,7 +33,6 @@ import androidx.core.content.ContextCompat
import chat.simplex.app.*
import chat.simplex.app.R
import chat.simplex.app.model.*
import chat.simplex.app.ui.theme.HighOrLowlight
import chat.simplex.app.views.chat.item.*
import chat.simplex.app.views.helpers.*
import kotlinx.coroutines.*
@@ -689,7 +688,7 @@ fun ComposeView(
Icon(
painterResource(R.drawable.ic_attach_file_filled_500),
contentDescription = stringResource(R.string.attach),
tint = if (!composeState.value.attachmentDisabled && userCanSend.value) MaterialTheme.colors.primary else HighOrLowlight,
tint = if (!composeState.value.attachmentDisabled && userCanSend.value) MaterialTheme.colors.primary else MaterialTheme.colors.secondary,
modifier = Modifier
.size(28.dp)
.clip(CircleShape)
@@ -14,7 +14,6 @@ import androidx.compose.ui.unit.sp
import chat.simplex.app.R
import chat.simplex.app.model.durationText
import chat.simplex.app.ui.theme.*
import chat.simplex.app.views.chat.item.SentColorLight
import chat.simplex.app.views.helpers.*
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -57,12 +56,13 @@ fun ComposeVoiceView(
.height(3.dp)
.background(MaterialTheme.colors.primary)
)
val sentColor = CurrentColors.collectAsState().value.appColors.sentMessage
Row(
Modifier
.height(60.dp)
.fillMaxWidth()
.padding(top = 8.dp)
.background(SentColorLight),
.background(sentColor),
verticalAlignment = Alignment.CenterVertically
) {
IconButton(
@@ -80,7 +80,7 @@ fun ComposeVoiceView(
Modifier
.padding(start = 4.dp, end = 2.dp)
.size(36.dp),
tint = if (finishedRecording) MaterialTheme.colors.primary else HighOrLowlight
tint = if (finishedRecording) MaterialTheme.colors.primary else MaterialTheme.colors.secondary
)
}
val numberInText = remember(recordedDurationMs, progress.value) {
@@ -95,7 +95,7 @@ fun ComposeVoiceView(
Text(
durationText(numberInText.value),
fontSize = 18.sp,
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
)
Spacer(Modifier.weight(1f))
if (cancelEnabled) {
@@ -194,10 +194,10 @@ private fun TimedMessagesFeatureSection(
private fun ResetSaveButtons(reset: () -> Unit, save: () -> Unit, disabled: Boolean) {
SectionView {
SectionItemView(reset, disabled = disabled) {
Text(stringResource(R.string.reset_verb), color = if (disabled) HighOrLowlight else MaterialTheme.colors.primary)
Text(stringResource(R.string.reset_verb), color = if (disabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary)
}
SectionItemView(save, disabled = disabled) {
Text(stringResource(R.string.save_and_notify_contact), color = if (disabled) HighOrLowlight else MaterialTheme.colors.primary)
Text(stringResource(R.string.save_and_notify_contact), color = if (disabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary)
}
}
}
@@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
@@ -13,8 +14,7 @@ import androidx.compose.ui.tooling.preview.Preview
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.SimpleXTheme
import chat.simplex.app.ui.theme.*
import chat.simplex.app.views.chat.item.*
import kotlinx.datetime.Clock
@@ -25,10 +25,12 @@ fun ContextItemView(
cancelContextItem: () -> Unit
) {
val sent = contextItem.chatDir.sent
val sentColor = CurrentColors.collectAsState().value.appColors.sentMessage
val receivedColor = CurrentColors.collectAsState().value.appColors.receivedMessage
Row(
Modifier
.padding(top = 8.dp)
.background(if (sent) SentColorLight else ReceivedColorLight),
.background(if (sent) sentColor else receivedColor),
verticalAlignment = Alignment.CenterVertically
) {
Row(
@@ -45,7 +47,7 @@ fun ContextItemView(
.height(20.dp)
.width(20.dp),
contentDescription = stringResource(R.string.icon_descr_context),
tint = HighOrLowlight,
tint = MaterialTheme.colors.secondary,
)
MarkdownText(
contextItem.text, contextItem.formattedText,
@@ -44,7 +44,7 @@ import androidx.core.widget.*
import chat.simplex.app.R
import chat.simplex.app.SimplexApp
import chat.simplex.app.model.*
import chat.simplex.app.ui.theme.HighOrLowlight
import chat.simplex.app.ui.theme.CurrentColors
import chat.simplex.app.ui.theme.SimpleXTheme
import chat.simplex.app.views.chat.item.ItemAction
import chat.simplex.app.views.helpers.*
@@ -187,7 +187,7 @@ private fun NativeKeyboard(
) {
val cs = composeState.value
val textColor = MaterialTheme.colors.onBackground
val tintColor = MaterialTheme.colors.secondary
val tintColor = MaterialTheme.colors.secondaryVariant
val padding = PaddingValues(12.dp, 7.dp, 45.dp, 0.dp)
val paddingStart = with(LocalDensity.current) { 12.dp.roundToPx() }
val paddingTop = with(LocalDensity.current) { 7.dp.roundToPx() }
@@ -240,7 +240,7 @@ private fun NativeKeyboard(
editText.setPadding(paddingStart, paddingTop, paddingEnd, paddingBottom)
editText.setText(cs.message)
if (Build.VERSION.SDK_INT >= 29) {
editText.textCursorDrawable?.let { DrawableCompat.setTint(it, HighOrLowlight.toArgb()) }
editText.textCursorDrawable?.let { DrawableCompat.setTint(it, CurrentColors.value.colors.secondary.toArgb()) }
} else {
try {
val f: Field = TextView::class.java.getDeclaredField("mCursorDrawableRes")
@@ -284,7 +284,7 @@ private fun ComposeOverlay(textId: Int, textStyle: MutableState<TextStyle>, padd
Text(
generalGetString(textId),
Modifier.padding(padding),
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
style = textStyle.value.copy(fontStyle = FontStyle.Italic)
)
}
@@ -295,7 +295,7 @@ private fun BoxScope.DeleteTextButton(composeState: MutableState<ComposeState>)
{ composeState.value = composeState.value.copy(message = "") },
Modifier.align(Alignment.TopEnd).size(36.dp)
) {
Icon(painterResource(R.drawable.ic_close), null, Modifier.padding(7.dp).size(36.dp), tint = HighOrLowlight)
Icon(painterResource(R.drawable.ic_close), null, Modifier.padding(7.dp).size(36.dp), tint = MaterialTheme.colors.secondary)
}
}
@@ -354,7 +354,7 @@ private fun DisallowedVoiceButton(enabled: Boolean, onClick: () -> Unit) {
Icon(
painterResource(R.drawable.ic_keyboard_voice),
stringResource(R.string.icon_descr_record_voice_message),
tint = HighOrLowlight,
tint = MaterialTheme.colors.secondary,
modifier = Modifier
.size(36.dp)
.padding(4.dp)
@@ -424,7 +424,7 @@ private fun RecordVoiceButton(interactionSource: MutableInteractionSource) {
@Composable
private fun ProgressIndicator() {
CircularProgressIndicator(Modifier.size(36.dp).padding(4.dp), color = HighOrLowlight, strokeWidth = 3.dp)
CircularProgressIndicator(Modifier.size(36.dp).padding(4.dp), color = MaterialTheme.colors.secondary, strokeWidth = 3.dp)
}
@Composable
@@ -474,7 +474,7 @@ private fun SendMsgButton(
.padding(4.dp)
.alpha(alpha.value)
.clip(CircleShape)
.background(if (enabled) MaterialTheme.colors.primary else HighOrLowlight)
.background(if (enabled) MaterialTheme.colors.primary else MaterialTheme.colors.secondary)
.padding(3.dp)
)
}
@@ -497,7 +497,7 @@ private fun StartLiveMessageButton(enabled: Boolean, onClick: () -> Unit) {
Icon(
BoltFilled,
stringResource(R.string.icon_descr_send_message),
tint = if (enabled) MaterialTheme.colors.primary else HighOrLowlight,
tint = if (enabled) MaterialTheme.colors.primary else MaterialTheme.colors.secondary,
modifier = Modifier
.size(36.dp)
.padding(4.dp)
@@ -65,7 +65,7 @@ private fun VerifyCodeLayout(
val splitCode = splitToParts(connectionCode, 24)
Row(Modifier.fillMaxWidth().padding(bottom = DEFAULT_PADDING_HALF), horizontalArrangement = Arrangement.Center) {
if (connectionVerified) {
Icon(painterResource(R.drawable.ic_verified_user), null, Modifier.padding(end = 4.dp).size(22.dp), tint = HighOrLowlight)
Icon(painterResource(R.drawable.ic_verified_user), null, Modifier.padding(end = 4.dp).size(22.dp), tint = MaterialTheme.colors.secondary)
Text(String.format(stringResource(R.string.is_verified), displayName))
} else {
Text(String.format(stringResource(R.string.is_not_verified), displayName))
@@ -133,7 +133,7 @@ fun AddGroupMembersLayout(
Text(
stringResource(R.string.no_contacts_to_add),
Modifier.padding(),
color = HighOrLowlight
color = MaterialTheme.colors.secondary
)
}
} else {
@@ -214,7 +214,7 @@ fun InviteSectionFooter(selectedContactsCount: Int, enabled: Boolean, clearSelec
if (selectedContactsCount >= 1) {
Text(
String.format(generalGetString(R.string.num_contacts_selected), selectedContactsCount),
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
fontSize = 12.sp
)
Box(
@@ -222,14 +222,14 @@ fun InviteSectionFooter(selectedContactsCount: Int, enabled: Boolean, clearSelec
) {
Text(
stringResource(R.string.clear_contacts_selection_button),
color = if (enabled) MaterialTheme.colors.primary else HighOrLowlight,
color = if (enabled) MaterialTheme.colors.primary else MaterialTheme.colors.secondary,
fontSize = 12.sp
)
}
} else {
Text(
stringResource(R.string.no_contacts_selected),
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
fontSize = 12.sp
)
}
@@ -270,13 +270,13 @@ fun ContactCheckRow(
val iconColor: Color
if (prohibitedToInviteIncognito) {
icon = painterResource(R.drawable.ic_theater_comedy_filled)
iconColor = HighOrLowlight
iconColor = MaterialTheme.colors.secondary
} else if (checked) {
icon = painterResource(R.drawable.ic_check_circle_filled)
iconColor = if (enabled) MaterialTheme.colors.primary else HighOrLowlight
iconColor = if (enabled) MaterialTheme.colors.primary else MaterialTheme.colors.secondary
} else {
icon = painterResource(R.drawable.ic_circle)
iconColor = HighOrLowlight
iconColor = MaterialTheme.colors.secondary
}
SectionItemView(
click = if (enabled) {
@@ -294,7 +294,7 @@ fun ContactCheckRow(
Spacer(Modifier.width(DEFAULT_SPACE_AFTER_ICON))
Text(
contact.chatViewName, maxLines = 1, overflow = TextOverflow.Ellipsis,
color = if (prohibitedToInviteIncognito) HighOrLowlight else Color.Unspecified
color = if (prohibitedToInviteIncognito) MaterialTheme.colors.secondary else Color.Unspecified
)
Spacer(Modifier.fillMaxWidth().weight(1f))
Icon(
@@ -108,7 +108,7 @@ fun deleteGroupDialog(chatInfo: ChatInfo, groupInfo: GroupInfo, chatModel: ChatM
val alertTextKey =
if (groupInfo.membership.memberCurrent) R.string.delete_group_for_all_members_cannot_undo_warning
else R.string.delete_group_for_self_cannot_undo_warning
AlertManager.shared.showAlertMsg(
AlertManager.shared.showAlertDialog(
title = generalGetString(R.string.delete_group_question),
text = generalGetString(alertTextKey),
confirmText = generalGetString(R.string.delete_verb),
@@ -122,12 +122,13 @@ fun deleteGroupDialog(chatInfo: ChatInfo, groupInfo: GroupInfo, chatModel: ChatM
close?.invoke()
}
}
}
},
destructive = true,
)
}
fun leaveGroupDialog(groupInfo: GroupInfo, chatModel: ChatModel, close: (() -> Unit)? = null) {
AlertManager.shared.showAlertMsg(
AlertManager.shared.showAlertDialog(
title = generalGetString(R.string.leave_group_question),
text = generalGetString(R.string.you_will_stop_receiving_messages_from_this_group_chat_history_will_be_preserved),
confirmText = generalGetString(R.string.leave_group_button),
@@ -136,7 +137,8 @@ fun leaveGroupDialog(groupInfo: GroupInfo, chatModel: ChatModel, close: (() -> U
chatModel.controller.leaveGroup(groupInfo.groupId)
close?.invoke()
}
}
},
destructive = true,
)
}
@@ -189,7 +191,7 @@ fun GroupChatInfoLayout(
}
val onAddMembersClick = if (chat.chatInfo.incognito) ::cantInviteIncognitoAlert else addMembers
val tint = if (chat.chatInfo.incognito) HighOrLowlight else MaterialTheme.colors.primary
val tint = if (chat.chatInfo.incognito) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
AddMembersButton(tint, onAddMembersClick)
}
SectionItemView(minHeight = 54.dp) {
@@ -303,7 +305,7 @@ private fun MemberRow(member: GroupMember, user: Boolean = false) {
val statusDescr = if (user) String.format(generalGetString(R.string.group_info_member_you), s) else s
Text(
statusDescr,
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
fontSize = 12.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis
@@ -312,14 +314,14 @@ private fun MemberRow(member: GroupMember, user: Boolean = false) {
}
val role = member.memberRole
if (role == GroupMemberRole.Owner || role == GroupMemberRole.Admin) {
Text(role.text, color = HighOrLowlight)
Text(role.text, color = MaterialTheme.colors.secondary)
}
}
}
@Composable
private fun MemberVerifiedShield() {
Icon(painterResource(R.drawable.ic_verified_user), null, Modifier.padding(end = 3.dp).size(16.dp), tint = HighOrLowlight)
Icon(painterResource(R.drawable.ic_verified_user), null, Modifier.padding(end = 3.dp).size(16.dp), tint = MaterialTheme.colors.secondary)
}
@Composable
@@ -328,7 +330,7 @@ private fun GroupLinkButton(onClick: () -> Unit) {
painterResource(R.drawable.ic_link),
stringResource(R.string.group_link),
onClick,
iconColor = HighOrLowlight
iconColor = MaterialTheme.colors.secondary
)
}
@@ -338,7 +340,7 @@ private fun CreateGroupLinkButton(onClick: () -> Unit) {
painterResource(R.drawable.ic_add_link),
stringResource(R.string.create_group_link),
onClick,
iconColor = HighOrLowlight
iconColor = MaterialTheme.colors.secondary
)
}
@@ -348,7 +350,7 @@ fun EditGroupProfileButton(onClick: () -> Unit) {
painterResource(R.drawable.ic_edit),
stringResource(R.string.button_edit_group_profile),
onClick,
iconColor = HighOrLowlight
iconColor = MaterialTheme.colors.secondary
)
}
@@ -363,7 +365,7 @@ private fun AddOrEditWelcomeMessage(welcomeMessage: String?, onClick: () -> Unit
painterResource(R.drawable.ic_maps_ugc),
text,
onClick,
iconColor = HighOrLowlight
iconColor = MaterialTheme.colors.secondary
)
}
@@ -4,8 +4,7 @@ import SectionBottomSpacer
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Text
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
@@ -66,7 +65,7 @@ fun GroupLinkView(chatModel: ChatModel, groupInfo: GroupInfo, connReqContact: St
}
},
deleteLink = {
AlertManager.shared.showAlertMsg(
AlertManager.shared.showAlertDialog(
title = generalGetString(R.string.delete_link_question),
text = generalGetString(R.string.all_group_members_will_remain_connected),
confirmText = generalGetString(R.string.delete_verb),
@@ -78,7 +77,8 @@ fun GroupLinkView(chatModel: ChatModel, groupInfo: GroupInfo, connReqContact: St
onGroupLinkUpdated(null to null)
}
}
}
},
destructive = true,
)
}
)
@@ -176,7 +176,7 @@ fun ProgressIndicator() {
Modifier
.padding(horizontal = 2.dp)
.size(30.dp),
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
strokeWidth = 2.5.dp
)
}
@@ -116,7 +116,7 @@ fun GroupMemberInfoView(
}
fun removeMemberDialog(groupInfo: GroupInfo, member: GroupMember, chatModel: ChatModel, close: (() -> Unit)? = null) {
AlertManager.shared.showAlertMsg(
AlertManager.shared.showAlertDialog(
title = generalGetString(R.string.button_remove_member),
text = generalGetString(R.string.member_will_be_removed_from_group_cannot_be_undone),
confirmText = generalGetString(R.string.remove_member_confirmation),
@@ -128,7 +128,8 @@ fun removeMemberDialog(groupInfo: GroupInfo, member: GroupMember, chatModel: Cha
}
close?.invoke()
}
}
},
destructive = true,
)
}
@@ -238,7 +239,7 @@ fun GroupMemberInfoHeader(member: GroupMember) {
ProfileImage(size = 192.dp, member.image, color = if (isInDarkTheme()) GroupDark else SettingsSecondaryLight)
Row(verticalAlignment = Alignment.CenterVertically) {
if (member.verified) {
Icon(painterResource(R.drawable.ic_verified_user), null, Modifier.padding(end = 6.dp, top = 4.dp).size(24.dp), tint = HighOrLowlight)
Icon(painterResource(R.drawable.ic_verified_user), null, Modifier.padding(end = 6.dp, top = 4.dp).size(24.dp), tint = MaterialTheme.colors.secondary)
}
Text(
member.displayName, style = MaterialTheme.typography.h1.copy(fontWeight = FontWeight.Normal),
@@ -123,7 +123,7 @@ private fun FeatureSection(
SectionView {
val on = enableFeature.value == GroupFeatureEnabled.ON
val icon = if (on) feature.iconFilled() else feature.icon
val iconTint = if (on) SimplexGreen else HighOrLowlight
val iconTint = if (on) SimplexGreen else MaterialTheme.colors.secondary
val timedOn = feature == GroupFeature.TimedMessages && enableFeature.value == GroupFeatureEnabled.ON
if (groupInfo.canEdit) {
PreferenceToggleWithIcon(
@@ -157,10 +157,10 @@ private fun FeatureSection(
private fun ResetSaveButtons(reset: () -> Unit, save: () -> Unit, disabled: Boolean) {
SectionView {
SectionItemView(reset, disabled = disabled) {
Text(stringResource(R.string.reset_verb), color = if (disabled) HighOrLowlight else MaterialTheme.colors.primary)
Text(stringResource(R.string.reset_verb), color = if (disabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary)
}
SectionItemView(save, disabled = disabled) {
Text(stringResource(R.string.save_and_notify_group_members), color = if (disabled) HighOrLowlight else MaterialTheme.colors.primary)
Text(stringResource(R.string.save_and_notify_group_members), color = if (disabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary)
}
}
}
@@ -114,7 +114,7 @@ fun GroupProfileLayout(
) {
Box(contentAlignment = Alignment.TopEnd) {
Box(contentAlignment = Alignment.Center) {
ProfileImage(108.dp, profileImage.value, color = HighOrLowlight.copy(alpha = 0.1f))
ProfileImage(108.dp, profileImage.value, color = MaterialTheme.colors.secondary.copy(alpha = 0.1f))
EditImageButton { scope.launch { bottomSheetModalState.show() } }
}
if (profileImage.value != null) {
@@ -163,7 +163,7 @@ fun GroupProfileLayout(
} else {
Text(
stringResource(R.string.save_group_profile),
color = HighOrLowlight
color = MaterialTheme.colors.secondary
)
}
}
@@ -79,7 +79,7 @@ private fun GroupWelcomeLayout(
private fun SaveButton(save: () -> Unit, disabled: Boolean) {
SectionView {
SectionItemView(save, disabled = disabled) {
Text(stringResource(R.string.save_and_update_group_profile), color = if (disabled) HighOrLowlight else MaterialTheme.colors.primary)
Text(stringResource(R.string.save_and_update_group_profile), color = if (disabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary)
}
}
}
@@ -1,8 +1,7 @@
package chat.simplex.app.views.chat.item
import androidx.compose.foundation.layout.*
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -35,15 +34,15 @@ fun CICallItemView(cInfo: ChatInfo, cItem: ChatItem, status: CICallStatus, durat
CICallStatus.Negotiated -> ConnectingCallIcon()
CICallStatus.Progress -> Icon(painterResource(R.drawable.ic_phone_in_talk_filled), stringResource(R.string.icon_descr_call_progress), tint = SimplexGreen)
CICallStatus.Ended -> Row {
Icon(painterResource(R.drawable.ic_call_end), stringResource(R.string.icon_descr_call_ended), tint = HighOrLowlight, modifier = Modifier.padding(end = 4.dp))
Text(durationText(duration), color = HighOrLowlight)
Icon(painterResource(R.drawable.ic_call_end), stringResource(R.string.icon_descr_call_ended), tint = MaterialTheme.colors.secondary, modifier = Modifier.padding(end = 4.dp))
Text(durationText(duration), color = MaterialTheme.colors.secondary)
}
CICallStatus.Error -> {}
}
Text(
cItem.timestampText,
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
fontSize = 14.sp,
modifier = Modifier.padding(start = 3.dp)
)
@@ -55,7 +54,7 @@ fun AcceptCallButton(cInfo: ChatInfo, acceptCall: (Contact) -> Unit) {
if (cInfo is ChatInfo.Direct) {
SimpleButton(stringResource(R.string.answer_call), painterResource(R.drawable.ic_ring_volume)) { acceptCall(cInfo.contact) }
} else {
Icon(painterResource(R.drawable.ic_ring_volume), stringResource(R.string.answer_call), tint = HighOrLowlight)
Icon(painterResource(R.drawable.ic_ring_volume), stringResource(R.string.answer_call), tint = MaterialTheme.colors.secondary)
}
// if case let .direct(contact) = chatInfo {
// Button {
@@ -13,8 +13,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.app.model.ChatItem
import chat.simplex.app.ui.theme.HighOrLowlight
import chat.simplex.app.ui.theme.SimpleXTheme
import chat.simplex.app.ui.theme.*
@Composable
fun CIEventView(ci: ChatItem) {
@@ -22,28 +21,25 @@ fun CIEventView(ci: ChatItem) {
fun chatEventTextView(text: AnnotatedString) {
Text(text, style = MaterialTheme.typography.body1.copy(lineHeight = 22.sp))
}
Surface {
Row(
Modifier.padding(horizontal = 6.dp, vertical = 6.dp),
verticalAlignment = Alignment.CenterVertically
) {
val memberDisplayName = ci.memberDisplayName
if (memberDisplayName != null) {
chatEventTextView(
buildAnnotatedString {
withStyle(chatEventStyle) { append(memberDisplayName) }
append(" ")
}.plus(chatEventText(ci))
)
} else {
chatEventTextView(chatEventText(ci))
}
Row(
Modifier.padding(horizontal = 6.dp, vertical = 6.dp),
verticalAlignment = Alignment.CenterVertically
) {
val memberDisplayName = ci.memberDisplayName
if (memberDisplayName != null) {
chatEventTextView(
buildAnnotatedString {
withStyle(chatEventStyle) { append(memberDisplayName) }
append(" ")
}.plus(chatEventText(ci))
)
} else {
chatEventTextView(chatEventText(ci))
}
}
}
val chatEventStyle = SpanStyle(fontSize = 12.sp, fontWeight = FontWeight.Light, color = HighOrLowlight)
val chatEventStyle = SpanStyle(fontSize = 12.sp, fontWeight = FontWeight.Light, color = CurrentColors.value.colors.secondary)
fun chatEventText(ci: ChatItem): AnnotatedString =
buildAnnotatedString {
@@ -11,7 +11,6 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.app.R
import chat.simplex.app.model.*
import chat.simplex.app.ui.theme.HighOrLowlight
import chat.simplex.app.views.helpers.generalGetString
@Composable
@@ -27,7 +26,7 @@ fun CIFeaturePreferenceView(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
Icon(feature.icon, feature.text, Modifier.size(18.dp), tint = HighOrLowlight)
Icon(feature.icon, feature.text, Modifier.size(18.dp), tint = MaterialTheme.colors.secondary)
if (contact != null && allowed != FeatureAllowed.NO && contact.allowsFeature(feature) && !contact.userAllowsFeature(feature)) {
val acceptStyle = SpanStyle(color = MaterialTheme.colors.primary, fontSize = 12.sp)
val setParam = feature == ChatFeature.TimedMessages && contact.mergedPreferences.timedMessages.userPreference.pref.ttl == null
@@ -48,7 +47,7 @@ fun CIFeaturePreferenceView(
)
} else {
Text(chatItem.content.text + " " + chatItem.timestampText,
fontSize = 12.sp, fontWeight = FontWeight.Light, color = HighOrLowlight)
fontSize = 12.sp, fontWeight = FontWeight.Light, color = MaterialTheme.colors.secondary)
}
}
}
@@ -194,7 +194,7 @@ fun CIFileView(
)
Text(
formatBytes(file.fileSize) + metaReserve,
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
fontSize = 14.sp,
maxLines = 1
)
@@ -8,7 +8,6 @@ import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
@@ -69,12 +68,14 @@ fun CIGroupInvitationView(
}
}
val sentColor = CurrentColors.collectAsState().value.appColors.sentMessage
val receivedColor = CurrentColors.collectAsState().value.appColors.receivedMessage
Surface(
modifier = if (action) Modifier.clickable(onClick = {
joinGroup(groupInvitation.groupId)
}) else Modifier,
shape = RoundedCornerShape(18.dp),
color = if (sent) SentColorLight else ReceivedColorLight,
color = if (sent) sentColor else receivedColor,
) {
Box(
Modifier
@@ -105,7 +106,7 @@ fun CIGroupInvitationView(
}
Text(
ci.timestampText,
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
fontSize = 14.sp,
modifier = Modifier.padding(start = 3.dp)
)
@@ -14,11 +14,11 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.app.model.*
import chat.simplex.app.ui.theme.HighOrLowlight
import chat.simplex.app.ui.theme.CurrentColors
import kotlinx.datetime.Clock
@Composable
fun CIMetaView(chatItem: ChatItem, timedMessagesTTL: Int?, metaColor: Color = HighOrLowlight) {
fun CIMetaView(chatItem: ChatItem, timedMessagesTTL: Int?, metaColor: Color = MaterialTheme.colors.secondary) {
Row(Modifier.padding(start = 3.dp), verticalAlignment = Alignment.CenterVertically) {
if (chatItem.isDeletedContent) {
Text(
@@ -72,7 +72,7 @@ fun reserveSpaceForMeta(meta: CIMeta, chatTTL: Int?): String {
res += TimedMessagesPreference.shortTtlText(ttl)
}
}
if (meta.statusIcon(HighOrLowlight) != null || !meta.disappearing) {
if (meta.statusIcon(CurrentColors.value.colors.secondary) != null || !meta.disappearing) {
res += iconSpace
}
return res + meta.timestampText
@@ -137,7 +137,7 @@ private fun DurationText(text: State<String>, padding: PaddingValues) {
Modifier
.padding(padding)
.widthIn(min = minWidth),
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
fontSize = 16.sp,
maxLines = 1
)
@@ -156,9 +156,11 @@ private fun PlayPauseButton(
pause: () -> Unit,
longClick: () -> Unit
) {
val sentColor = CurrentColors.collectAsState().value.appColors.sentMessage
val receivedColor = CurrentColors.collectAsState().value.appColors.receivedMessage
Surface(
Modifier.drawRingModifier(angle, strokeColor, strokeWidth),
color = if (sent) SentColorLight else ReceivedColorLight,
color = if (sent) sentColor else receivedColor,
shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50))
) {
Box(
@@ -174,7 +176,7 @@ private fun PlayPauseButton(
if (audioPlaying) painterResource(R.drawable.ic_pause_filled) else painterResource(R.drawable.ic_play_arrow_filled),
contentDescription = null,
Modifier.size(36.dp),
tint = if (error) WarningOrange else if (!enabled) HighOrLowlight else MaterialTheme.colors.primary
tint = if (error) WarningOrange else if (!enabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
)
}
}
@@ -247,7 +247,7 @@ fun ChatItemView(
val ct = if (cInfo is ChatInfo.Direct) cInfo.contact else null
CIFeaturePreferenceView(cItem, ct, c.feature, c.allowed, acceptFeature)
}
is CIContent.SndChatPreference -> CIChatFeatureView(cItem, c.feature, HighOrLowlight, icon = c.feature.icon,)
is CIContent.SndChatPreference -> CIChatFeatureView(cItem, c.feature, MaterialTheme.colors.secondary, icon = c.feature.icon,)
is CIContent.RcvGroupFeature -> CIChatFeatureView(cItem, c.groupFeature, c.preference.enable.iconColor)
is CIContent.SndGroupFeature -> CIChatFeatureView(cItem, c.groupFeature, c.preference.enable.iconColor)
is CIContent.RcvChatFeatureRejected -> CIChatFeatureView(cItem, c.feature, Color.Red)
@@ -380,13 +380,13 @@ fun deleteMessageAlertDialog(chatItem: ChatItem, questionText: String, deleteMes
TextButton(onClick = {
deleteMessage(chatItem.id, CIDeleteMode.cidmInternal)
AlertManager.shared.hideAlert()
}) { Text(stringResource(R.string.for_me_only)) }
}) { Text(stringResource(R.string.for_me_only), color = MaterialTheme.colors.error) }
if (chatItem.meta.editable) {
Spacer(Modifier.padding(horizontal = 4.dp))
TextButton(onClick = {
deleteMessage(chatItem.id, CIDeleteMode.cidmBroadcast)
AlertManager.shared.hideAlert()
}) { Text(stringResource(R.string.for_everybody)) }
}) { Text(stringResource(R.string.for_everybody), color = MaterialTheme.colors.error) }
}
}
}
@@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
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.*
@@ -13,15 +14,16 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.app.model.ChatItem
import chat.simplex.app.ui.theme.HighOrLowlight
import chat.simplex.app.ui.theme.SimpleXTheme
import chat.simplex.app.ui.theme.*
@Composable
fun DeletedItemView(ci: ChatItem, timedMessagesTTL: Int?, showMember: Boolean = false) {
val sent = ci.chatDir.sent
val sentColor = CurrentColors.collectAsState().value.appColors.sentMessage
val receivedColor = CurrentColors.collectAsState().value.appColors.receivedMessage
Surface(
shape = RoundedCornerShape(18.dp),
color = if (sent) SentColorLight else ReceivedColorLight,
color = if (sent) sentColor else receivedColor,
) {
Row(
Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
@@ -30,7 +32,7 @@ fun DeletedItemView(ci: ChatItem, timedMessagesTTL: Int?, showMember: Boolean =
Text(
buildAnnotatedString {
appendSender(this, if (showMember) ci.memberDisplayName else null, true)
withStyle(SpanStyle(fontStyle = FontStyle.Italic, color = HighOrLowlight)) { append(ci.content.text) }
withStyle(SpanStyle(fontStyle = FontStyle.Italic, color = MaterialTheme.colors.secondary)) { append(ci.content.text) }
},
style = MaterialTheme.typography.body1.copy(lineHeight = 22.sp),
modifier = Modifier.padding(end = 8.dp)
@@ -29,11 +29,6 @@ import chat.simplex.app.views.helpers.*
import kotlinx.datetime.Clock
import kotlin.math.min
val SentColorLight = Color(0x1E45B8FF)
val ReceivedColorLight = Color(0x20B1B0B5)
val SentQuoteColorLight = Color(0x2545B8FF)
val ReceivedQuoteColorLight = Color(0x25B1B0B5)
@Composable
fun FramedItemView(
chatInfo: ChatInfo,
@@ -54,6 +49,9 @@ fun FramedItemView(
return if (chatInfo is ChatInfo.Group) chatInfo.groupInfo.membership else null
}
@Composable
fun Color.toQuote(): Color = if (isInDarkTheme()) lighter(0.12f) else darker(0.12f)
@Composable
fun ciQuotedMsgView(qi: CIQuote) {
Box(
@@ -70,9 +68,11 @@ fun FramedItemView(
@Composable
fun FramedItemHeader(caption: String, italic: Boolean, icon: Painter? = null) {
val sentColor = CurrentColors.collectAsState().value.appColors.sentMessage
val receivedColor = CurrentColors.collectAsState().value.appColors.receivedMessage
Row(
Modifier
.background(if (sent) SentQuoteColorLight else ReceivedQuoteColorLight)
.background(if (sent) sentColor.toQuote() else receivedColor.toQuote())
.fillMaxWidth()
.padding(start = 8.dp, top = 6.dp, end = 12.dp, bottom = if (ci.quotedItem == null) 6.dp else 0.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp),
@@ -87,7 +87,7 @@ fun FramedItemView(
}
Text(
buildAnnotatedString {
withStyle(SpanStyle(fontSize = 12.sp, fontStyle = if (italic) FontStyle.Italic else FontStyle.Normal, color = HighOrLowlight)) {
withStyle(SpanStyle(fontSize = 12.sp, fontStyle = if (italic) FontStyle.Italic else FontStyle.Normal, color = MaterialTheme.colors.secondary)) {
append(caption)
}
},
@@ -100,9 +100,11 @@ fun FramedItemView(
@Composable
fun ciQuoteView(qi: CIQuote) {
val sentColor = CurrentColors.collectAsState().value.appColors.sentMessage
val receivedColor = CurrentColors.collectAsState().value.appColors.receivedMessage
Row(
Modifier
.background(if (sent) SentQuoteColorLight else ReceivedQuoteColorLight)
.background(if (sent) sentColor.toQuote() else receivedColor.toQuote())
.fillMaxWidth()
.combinedClickable(
onLongClick = { showMenu.value = true },
@@ -163,16 +165,18 @@ fun FramedItemView(
val transparentBackground = (ci.content.msgContent is MsgContent.MCImage || ci.content.msgContent is MsgContent.MCVideo) &&
!ci.meta.isLive && ci.content.text.isEmpty() && ci.quotedItem == null
val sentColor = CurrentColors.collectAsState().value.appColors.sentMessage
val receivedColor = CurrentColors.collectAsState().value.appColors.receivedMessage
Box(Modifier
.clip(RoundedCornerShape(18.dp))
.background(
when {
transparentBackground -> Color.Transparent
sent -> SentColorLight
else -> ReceivedColorLight
sent -> sentColor
else -> receivedColor
}
)) {
var metaColor = HighOrLowlight
var metaColor = MaterialTheme.colors.secondary
Box(contentAlignment = Alignment.BottomEnd) {
Column(Modifier.width(IntrinsicSize.Max)) {
PriorityLayout(Modifier, CHAT_IMAGE_LAYOUT_ID) {
@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
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.graphics.Color
@@ -18,6 +19,7 @@ import androidx.compose.ui.unit.sp
import chat.simplex.app.R
import chat.simplex.app.model.ChatItem
import chat.simplex.app.model.MsgErrorType
import chat.simplex.app.ui.theme.CurrentColors
import chat.simplex.app.ui.theme.SimpleXTheme
import chat.simplex.app.views.helpers.AlertManager
import chat.simplex.app.views.helpers.generalGetString
@@ -50,10 +52,11 @@ fun IntegrityErrorItemView(msgError: MsgErrorType, ci: ChatItem, timedMessagesTT
@Composable
fun CIMsgError(ci: ChatItem, timedMessagesTTL: Int?, showMember: Boolean = false, onClick: () -> Unit) {
val receivedColor = CurrentColors.collectAsState().value.appColors.receivedMessage
Surface(
Modifier.clickable(onClick = onClick),
shape = RoundedCornerShape(18.dp),
color = ReceivedColorLight,
color = receivedColor,
) {
Row(
Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
@@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
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.*
@@ -16,15 +17,16 @@ import androidx.compose.ui.unit.sp
import chat.simplex.app.R
import chat.simplex.app.model.CIDeleted
import chat.simplex.app.model.ChatItem
import chat.simplex.app.ui.theme.HighOrLowlight
import chat.simplex.app.ui.theme.SimpleXTheme
import chat.simplex.app.ui.theme.*
import chat.simplex.app.views.helpers.generalGetString
@Composable
fun MarkedDeletedItemView(ci: ChatItem, timedMessagesTTL: Int?, showMember: Boolean = false) {
val sentColor = CurrentColors.collectAsState().value.appColors.sentMessage
val receivedColor = CurrentColors.collectAsState().value.appColors.receivedMessage
Surface(
shape = RoundedCornerShape(18.dp),
color = if (ci.chatDir.sent) SentColorLight else ReceivedColorLight,
color = if (ci.chatDir.sent) sentColor else receivedColor,
) {
Row(
Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
@@ -47,7 +49,7 @@ private fun MarkedDeletedText(text: String) {
Text(
buildAnnotatedString {
// appendSender(this, if (showMember) ci.memberDisplayName else null, true) // TODO font size
withStyle(SpanStyle(fontSize = 12.sp, fontStyle = FontStyle.Italic, color = HighOrLowlight)) { append(text) }
withStyle(SpanStyle(fontSize = 12.sp, fontStyle = FontStyle.Italic, color = MaterialTheme.colors.secondary)) { append(text) }
},
style = MaterialTheme.typography.body1.copy(lineHeight = 22.sp),
modifier = Modifier.padding(end = 8.dp),
@@ -22,7 +22,7 @@ import androidx.compose.ui.unit.*
import androidx.core.text.BidiFormatter
import chat.simplex.app.TAG
import chat.simplex.app.model.*
import chat.simplex.app.ui.theme.HighOrLowlight
import chat.simplex.app.ui.theme.CurrentColors
import chat.simplex.app.views.helpers.detectGesture
import kotlinx.coroutines.*
@@ -57,7 +57,7 @@ private val typingIndicators: List<AnnotatedString> = listOf(
private fun typingIndicator(recent: Boolean, @IntRange (from = 0, to = 4) typingIdx: Int): AnnotatedString = buildAnnotatedString {
pushStyle(SpanStyle(color = HighOrLowlight, fontFamily = FontFamily.Monospace, letterSpacing = (-1).sp))
pushStyle(SpanStyle(color = CurrentColors.value.colors.secondary, fontFamily = FontFamily.Monospace, letterSpacing = (-1).sp))
append(if (recent) typingIndicators[typingIdx] else noTyping)
}
@@ -228,7 +228,7 @@ fun ClickableText(
}
}
}, shouldConsumeEvent = { pos ->
var consume = false
var consume = false
layoutResult.value?.let { layoutResult ->
consume = shouldConsumeEvent(layoutResult.getOffsetForPosition(pos))
}
@@ -328,7 +328,7 @@ fun ContactConnectionMenuItems(chatInfo: ChatInfo.ContactConnection, chatModel:
@Composable
private fun InvalidDataView() {
Row {
ProfileImage(72.dp, null, R.drawable.ic_account_circle_filled, HighOrLowlight)
ProfileImage(72.dp, null, R.drawable.ic_account_circle_filled, MaterialTheme.colors.secondary)
Column(
modifier = Modifier
.padding(horizontal = 8.dp)
@@ -466,7 +466,8 @@ fun deleteContactConnectionAlert(connection: PendingContactConnection, chatModel
onSuccess()
}
}
}
},
destructive = true,
)
}
@@ -484,6 +485,7 @@ fun pendingContactAlertDialog(chatInfo: ChatInfo, chatModel: ChatModel) {
}
}
},
destructive = true,
dismissText = generalGetString(R.string.cancel_verb),
)
}
@@ -84,7 +84,7 @@ fun ChatListView(chatModel: ChatModel, setPerformLA: (Boolean, FragmentActivity)
hoveredElevation = 0.dp,
focusedElevation = 0.dp,
),
backgroundColor = if (!stopped) MaterialTheme.colors.primary else HighOrLowlight,
backgroundColor = if (!stopped) MaterialTheme.colors.primary else MaterialTheme.colors.secondary,
contentColor = Color.White
) {
Icon(if (!newChatSheetState.collectAsState().value.isVisible()) painterResource(R.drawable.ic_edit_filled) else painterResource(R.drawable.ic_close), stringResource(R.string.add_contact_or_create_group))
@@ -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)
@@ -105,7 +104,7 @@ fun ChatListView(chatModel: ChatModel, setPerformLA: (Boolean, FragmentActivity)
if (!stopped && !newChatSheetState.collectAsState().value.isVisible()) {
OnboardingButtons(showNewChatSheet)
}
Text(stringResource(R.string.you_have_no_chats), Modifier.align(Alignment.Center), color = HighOrLowlight)
Text(stringResource(R.string.you_have_no_chats), Modifier.align(Alignment.Center), color = MaterialTheme.colors.secondary)
}
}
}
@@ -136,7 +135,7 @@ private fun OnboardingButtons(openNewChatSheet: () -> Unit) {
}
Spacer(Modifier.height(DEFAULT_PADDING))
ConnectButton(generalGetString(R.string.tap_to_start_new_chat), openNewChatSheet)
val color = MaterialTheme.colors.primary
val color = MaterialTheme.colors.primaryVariant
Canvas(modifier = Modifier.width(40.dp).height(10.dp), onDraw = {
val trianglePath = Path().apply {
moveTo(0.dp.toPx(), 0f)
@@ -159,7 +158,7 @@ private fun ConnectButton(text: String, onClick: () -> Unit) {
onClick,
shape = RoundedCornerShape(21.dp),
colors = ButtonDefaults.textButtonColors(
backgroundColor = MaterialTheme.colors.primary
backgroundColor = MaterialTheme.colors.primaryVariant
),
elevation = null,
contentPadding = PaddingValues(horizontal = DEFAULT_PADDING, vertical = DEFAULT_PADDING_HALF),
@@ -282,7 +281,7 @@ private fun ProgressIndicator() {
Modifier
.padding(horizontal = 2.dp)
.size(30.dp),
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
strokeWidth = 2.5.dp
)
}
@@ -46,7 +46,7 @@ fun ChatPreviewView(
painterResource(R.drawable.ic_cancel_filled),
stringResource(R.string.icon_descr_group_inactive),
Modifier.size(18.dp).background(MaterialTheme.colors.background, CircleShape),
tint = HighOrLowlight
tint = MaterialTheme.colors.secondary
)
}
@@ -76,7 +76,7 @@ fun ChatPreviewView(
@Composable
fun VerifiedIcon() {
Icon(painterResource(R.drawable.ic_verified_user), null, Modifier.size(19.dp).padding(end = 3.dp, top = 1.dp), tint = HighOrLowlight)
Icon(painterResource(R.drawable.ic_verified_user), null, Modifier.size(19.dp).padding(end = 3.dp, top = 1.dp), tint = MaterialTheme.colors.secondary)
}
fun messageDraft(draft: ComposeState): Pair<AnnotatedString, Map<String, InlineTextContent>> {
@@ -110,7 +110,7 @@ fun ChatPreviewView(
"attachmentIcon" to InlineTextContent(
Placeholder(20.sp, 20.sp, PlaceholderVerticalAlign.TextCenter)
) {
Icon(if (attachment?.first != null) painterResource(attachment.first) else painterResource(R.drawable.ic_edit_note), null, tint = HighOrLowlight)
Icon(if (attachment?.first != null) painterResource(attachment.first) else painterResource(R.drawable.ic_edit_note), null, tint = MaterialTheme.colors.secondary)
}
)
return text to inlineContent
@@ -124,12 +124,12 @@ fun ChatPreviewView(
if (cInfo.contact.verified) {
VerifiedIcon()
}
chatPreviewTitleText(if (cInfo.ready) Color.Unspecified else HighOrLowlight)
chatPreviewTitleText(if (cInfo.ready) Color.Unspecified else MaterialTheme.colors.secondary)
}
is ChatInfo.Group ->
when (cInfo.groupInfo.membership.memberStatus) {
GroupMemberStatus.MemInvited -> chatPreviewTitleText(if (chat.chatInfo.incognito) Indigo else MaterialTheme.colors.primary)
GroupMemberStatus.MemAccepted -> chatPreviewTitleText(HighOrLowlight)
GroupMemberStatus.MemAccepted -> chatPreviewTitleText(MaterialTheme.colors.secondary)
else -> chatPreviewTitleText()
}
else -> chatPreviewTitleText()
@@ -170,12 +170,12 @@ fun ChatPreviewView(
when (cInfo) {
is ChatInfo.Direct ->
if (!cInfo.ready) {
Text(stringResource(R.string.contact_connection_pending), color = HighOrLowlight)
Text(stringResource(R.string.contact_connection_pending), color = MaterialTheme.colors.secondary)
}
is ChatInfo.Group ->
when (cInfo.groupInfo.membership.memberStatus) {
GroupMemberStatus.MemInvited -> Text(groupInvitationPreviewText(chatModelIncognito, currentUserProfileDisplayName, cInfo.groupInfo))
GroupMemberStatus.MemAccepted -> Text(stringResource(R.string.group_connection_pending), color = HighOrLowlight)
GroupMemberStatus.MemAccepted -> Text(stringResource(R.string.group_connection_pending), color = MaterialTheme.colors.secondary)
else -> {}
}
else -> {}
@@ -208,7 +208,7 @@ fun ChatPreviewView(
) {
Text(
ts,
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
style = MaterialTheme.typography.body2,
modifier = Modifier.padding(bottom = 5.dp)
)
@@ -221,10 +221,10 @@ fun ChatPreviewView(
) {
Text(
if (n > 0) unreadCountStr(n) else "",
color = MaterialTheme.colors.onPrimary,
color = Color.White,
fontSize = 11.sp,
modifier = Modifier
.background(if (stopped || showNtfsIcon) HighOrLowlight else MaterialTheme.colors.primary, shape = CircleShape)
.background(if (stopped || showNtfsIcon) MaterialTheme.colors.secondary else MaterialTheme.colors.primaryVariant, shape = CircleShape)
.badgeLayout()
.padding(horizontal = 3.dp)
.padding(vertical = 1.dp)
@@ -238,7 +238,7 @@ fun ChatPreviewView(
Icon(
painterResource(R.drawable.ic_notifications_off_filled),
contentDescription = generalGetString(R.string.notifications),
tint = HighOrLowlight,
tint = MaterialTheme.colors.secondary,
modifier = Modifier
.padding(horizontal = 3.dp)
.padding(vertical = 1.dp)
@@ -280,7 +280,7 @@ fun ChatStatusImage(s: NetworkStatus?) {
Icon(
painterResource(R.drawable.ic_error),
contentDescription = descr,
tint = HighOrLowlight,
tint = MaterialTheme.colors.secondary,
modifier = Modifier
.size(19.dp)
)
@@ -289,7 +289,7 @@ fun ChatStatusImage(s: NetworkStatus?) {
Modifier
.padding(horizontal = 2.dp)
.size(15.dp),
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
strokeWidth = 1.5.dp
)
}
@@ -34,7 +34,7 @@ fun ContactConnectionView(contactConnection: PendingContactConnection) {
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.h3,
fontWeight = FontWeight.Bold,
color = HighOrLowlight
color = MaterialTheme.colors.secondary
)
val height = with(LocalDensity.current) { 46.sp.toDp() }
Text(contactConnection.description, Modifier.heightIn(min = height), maxLines = 2, color = if (isInDarkTheme()) MessagePreviewDark else MessagePreviewLight)
@@ -45,7 +45,7 @@ fun ContactConnectionView(contactConnection: PendingContactConnection) {
) {
Text(
ts,
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
style = MaterialTheme.typography.body2,
modifier = Modifier.padding(bottom = 5.dp)
)
@@ -42,7 +42,7 @@ fun ContactRequestView(chatModelIncognito: Boolean, contactRequest: ChatInfo.Con
) {
Text(
ts,
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
style = MaterialTheme.typography.body2,
modifier = Modifier.padding(bottom = 5.dp)
)
@@ -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,6 @@ fun ShareListView(chatModel: ChatModel, stopped: Boolean) {
Column(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colors.background)
) {
if (chatModel.chats.isNotEmpty()) {
ShareList(chatModel, search = searchInList)
@@ -54,7 +52,7 @@ fun ShareListView(chatModel: ChatModel, stopped: Boolean) {
@Composable
private fun EmptyList() {
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(stringResource(R.string.you_have_no_chats), color = HighOrLowlight)
Text(stringResource(R.string.you_have_no_chats), color = MaterialTheme.colors.secondary)
}
}
@@ -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())) {
@@ -169,7 +169,7 @@ fun UserProfilePickerItem(u: User, unreadCount: Int = 0, padding: PaddingValues
if (u.activeUser) {
Icon(painterResource(R.drawable.ic_done_filled), null, Modifier.size(20.dp), tint = MaterialTheme.colors.onBackground)
} else if (u.hidden) {
Icon(painterResource(R.drawable.ic_lock), null, Modifier.size(20.dp), tint = HighOrLowlight)
Icon(painterResource(R.drawable.ic_lock), null, Modifier.size(20.dp), tint = MaterialTheme.colors.secondary)
} else if (unreadCount > 0) {
Box(
contentAlignment = Alignment.Center
@@ -179,13 +179,13 @@ fun UserProfilePickerItem(u: User, unreadCount: Int = 0, padding: PaddingValues
color = Color.White,
fontSize = 11.sp,
modifier = Modifier
.background(if (u.showNtfs) MaterialTheme.colors.primary else HighOrLowlight, shape = CircleShape)
.background(MaterialTheme.colors.primaryVariant, shape = CircleShape)
.padding(2.dp)
.badgeLayout()
)
}
} else if (!u.showNtfs) {
Icon(painterResource(R.drawable.ic_notifications_off), null, Modifier.size(20.dp), tint = HighOrLowlight)
Icon(painterResource(R.drawable.ic_notifications_off), null, Modifier.size(20.dp), tint = MaterialTheme.colors.secondary)
} else {
Box(Modifier.size(20.dp))
}
@@ -116,7 +116,8 @@ private fun deleteArchiveAlert(m: ChatModel, archivePath: String) {
} else {
Log.e(TAG, "deleteArchiveAlert delete() error")
}
}
},
destructive = true,
)
}
@@ -112,7 +112,7 @@ fun DatabaseEncryptionView(m: ChatModel) {
Modifier
.padding(horizontal = 2.dp)
.size(30.dp),
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
strokeWidth = 2.5.dp
)
}
@@ -211,7 +211,7 @@ fun DatabaseEncryptionLayout(
)
SectionItemViewSpaceBetween(onClickUpdate, disabled = disabled, minHeight = TextFieldDefaults.MinHeight) {
Text(generalGetString(R.string.update_database_passphrase), color = if (disabled) HighOrLowlight else MaterialTheme.colors.primary)
Text(generalGetString(R.string.update_database_passphrase), color = if (disabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary)
}
}
@@ -244,7 +244,7 @@ fun encryptDatabaseSavedAlert(onConfirm: () -> Unit) {
text = generalGetString(R.string.database_will_be_encrypted_and_passphrase_stored) + "\n" + storeSecurelySaved(),
confirmText = generalGetString(R.string.encrypt_database),
onConfirm = onConfirm,
destructive = false,
destructive = true,
)
}
@@ -292,7 +292,7 @@ fun SavePassphraseSetting(
Icon(
if (storedKey) painterResource(R.drawable.ic_vpn_key_filled) else painterResource(R.drawable.ic_vpn_key_off_filled),
stringResource(R.string.save_passphrase_in_keychain),
tint = if (storedKey) SimplexGreen else HighOrLowlight
tint = if (storedKey) SimplexGreen else MaterialTheme.colors.secondary
)
Spacer(Modifier.padding(horizontal = 4.dp))
Text(
@@ -360,7 +360,7 @@ fun PassphraseField(
if (showKey) painterResource(R.drawable.ic_visibility_off_filled) else painterResource(R.drawable.ic_visibility_filled)
} else painterResource(R.drawable.ic_error)
val iconColor = if (valid) {
if (showStrength && key.value.isNotEmpty()) PassphraseStrength.check(key.value).color else HighOrLowlight
if (showStrength && key.value.isNotEmpty()) PassphraseStrength.check(key.value).color else MaterialTheme.colors.secondary
} else Color.Red
val keyboard = LocalSoftwareKeyboardController.current
val keyboardOptions = KeyboardOptions(
@@ -417,7 +417,7 @@ fun PassphraseField(
TextFieldDefaults.TextFieldDecorationBox(
value = state.value.text,
innerTextField = innerTextField,
placeholder = { Text(placeholder, color = HighOrLowlight) },
placeholder = { Text(placeholder, color = MaterialTheme.colors.secondary) },
singleLine = true,
enabled = enabled,
isError = !valid,
@@ -180,7 +180,7 @@ fun DatabaseErrorView(
Modifier
.padding(horizontal = 2.dp)
.size(30.dp),
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
strokeWidth = 2.5.dp
)
}
@@ -114,7 +114,7 @@ fun DatabaseView(
Modifier
.padding(horizontal = 2.dp)
.size(30.dp),
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
strokeWidth = 2.5.dp
)
}
@@ -185,7 +185,7 @@ fun DatabaseLayout(
.drawable.ic_lock),
stringResource(R.string.database_passphrase),
click = showSettingsModal() { DatabaseEncryptionView(it) },
iconColor = if (unencrypted) WarningOrange else HighOrLowlight,
iconColor = if (unencrypted) WarningOrange else MaterialTheme.colors.secondary,
disabled = operationsDisabled
)
AppDataBackupPreference(privacyFullBackup, initialRandomDBPassphrase)
@@ -250,7 +250,7 @@ fun DatabaseLayout(
) {
Text(
stringResource(if (users.size > 1) R.string.delete_files_and_media_for_all_users else R.string.delete_files_and_media_all),
color = if (deleteFilesDisabled) HighOrLowlight else Color.Red
color = if (deleteFilesDisabled) MaterialTheme.colors.secondary else Color.Red
)
}
}
@@ -270,7 +270,7 @@ fun DatabaseLayout(
private fun AppDataBackupPreference(privacyFullBackup: SharedPreference<Boolean>, initialRandomDBPassphrase: SharedPreference<Boolean>) {
SettingsPreferenceItem(
painterResource(R.drawable.ic_backup),
iconColor = HighOrLowlight,
iconColor = MaterialTheme.colors.secondary,
pref = privacyFullBackup,
text = stringResource(R.string.full_backup)
) {
@@ -294,7 +294,8 @@ private fun setChatItemTTLAlert(
text = generalGetString(R.string.enable_automatic_deletion_message),
confirmText = generalGetString(R.string.delete_messages),
onConfirm = { setCiTTL(m, selectedChatItemTTL, progressIndicator, appFilesCountAndSize, context) },
onDismiss = { selectedChatItemTTL.value = m.chatItemTTL.value }
onDismiss = { selectedChatItemTTL.value = m.chatItemTTL.value },
destructive = true,
)
}
@@ -543,7 +544,8 @@ private fun importArchiveAlert(
title = generalGetString(R.string.import_database_question),
text = generalGetString(R.string.your_current_chat_database_will_be_deleted_and_replaced_with_the_imported_one),
confirmText = generalGetString(R.string.import_database_confirmation),
onConfirm = { importArchive(m, context, importedArchiveUri, appFilesCountAndSize, progressIndicator) }
onConfirm = { importArchive(m, context, importedArchiveUri, appFilesCountAndSize, progressIndicator) },
destructive = true,
)
}
@@ -608,7 +610,8 @@ private fun deleteChatAlert(m: ChatModel, progressIndicator: MutableState<Boolea
title = generalGetString(R.string.delete_chat_profile_question),
text = generalGetString(R.string.delete_chat_profile_action_cannot_be_undone_warning),
confirmText = generalGetString(R.string.delete_verb),
onConfirm = { deleteChat(m, progressIndicator) }
onConfirm = { deleteChat(m, progressIndicator) },
destructive = true,
)
}
@@ -37,7 +37,6 @@ class AlertManager {
) {
showAlert {
AlertDialog(
backgroundColor = if (isInDarkTheme()) DarkGrayBackground else MaterialTheme.colors.background,
onDismissRequest = this::hideAlert,
title = alertTitle(title),
text = alertText(text),
@@ -56,7 +55,7 @@ class AlertManager {
Dialog(onDismissRequest = this::hideAlert) {
Column(
Modifier
.background(if (isInDarkTheme()) DarkGrayBackground else MaterialTheme.colors.background, RoundedCornerShape(corner = CornerSize(25.dp)))
.background(MaterialTheme.colors.surface, RoundedCornerShape(corner = CornerSize(25.dp)))
.padding(bottom = DEFAULT_PADDING)
) {
Text(
@@ -67,7 +66,7 @@ class AlertManager {
)
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) {
if (text != null) {
Text(text, Modifier.fillMaxWidth().padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING * 1.5f), fontSize = 16.sp, textAlign = TextAlign.Center, color = HighOrLowlight)
Text(text, Modifier.fillMaxWidth().padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING * 1.5f), fontSize = 16.sp, textAlign = TextAlign.Center, color = MaterialTheme.colors.secondary)
}
buttons()
}
@@ -106,7 +105,6 @@ class AlertManager {
}) { Text(confirmText, color = if (destructive) MaterialTheme.colors.error else Color.Unspecified) }
}
},
backgroundColor = if (isInDarkTheme()) DarkGrayBackground else MaterialTheme.colors.background,
shape = RoundedCornerShape(corner = CornerSize(25.dp))
)
}
@@ -142,7 +140,6 @@ class AlertManager {
}) { Text(confirmText, color = if (destructive) Color.Red else Color.Unspecified, textAlign = TextAlign.End) }
}
},
backgroundColor = if (isInDarkTheme()) DarkGrayBackground else MaterialTheme.colors.background,
shape = RoundedCornerShape(corner = CornerSize(25.dp))
)
}
@@ -150,7 +147,7 @@ class AlertManager {
fun showAlertMsg(
title: String, text: String? = null,
confirmText: String = generalGetString(R.string.ok), onConfirm: (() -> Unit)? = null
confirmText: String = generalGetString(R.string.ok)
) {
showAlert {
AlertDialog(
@@ -163,23 +160,19 @@ class AlertManager {
horizontalArrangement = Arrangement.Center
) {
TextButton(onClick = {
onConfirm?.invoke()
hideAlert()
}) { Text(confirmText, color = Color.Unspecified) }
}
},
backgroundColor = if (isInDarkTheme()) DarkGrayBackground else MaterialTheme.colors.background,
shape = RoundedCornerShape(corner = CornerSize(25.dp))
)
}
}
fun showAlertMsg(
title: Int,
text: Int? = null,
confirmText: Int = R.string.ok,
onConfirm: (() -> Unit)? = null
) = showAlertMsg(generalGetString(title), if (text != null) generalGetString(text) else null, generalGetString(confirmText), onConfirm)
) = showAlertMsg(generalGetString(title), if (text != null) generalGetString(text) else null, generalGetString(confirmText))
@Composable
fun showInView() {
@@ -212,7 +205,7 @@ private fun alertText(text: String?): (@Composable () -> Unit)? {
Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
fontSize = 16.sp,
color = HighOrLowlight
color = MaterialTheme.colors.secondary
)
})
}
@@ -20,7 +20,7 @@ import chat.simplex.app.model.ChatInfo
import chat.simplex.app.ui.theme.SimpleXTheme
@Composable
fun ChatInfoImage(chatInfo: ChatInfo, size: Dp, iconColor: Color = MaterialTheme.colors.secondary) {
fun ChatInfoImage(chatInfo: ChatInfo, size: Dp, iconColor: Color = MaterialTheme.colors.secondaryVariant) {
val icon =
if (chatInfo is ChatInfo.Group) R.drawable.ic_supervised_user_circle_filled
else R.drawable.ic_account_circle_filled
@@ -28,7 +28,7 @@ fun ChatInfoImage(chatInfo: ChatInfo, size: Dp, iconColor: Color = MaterialTheme
}
@Composable
fun IncognitoImage(size: Dp, iconColor: Color = MaterialTheme.colors.secondary) {
fun IncognitoImage(size: Dp, iconColor: Color = MaterialTheme.colors.secondaryVariant) {
Box(Modifier.size(size)) {
Icon(
painterResource(R.drawable.ic_theater_comedy_filled), stringResource(R.string.incognito),
@@ -43,7 +43,7 @@ fun ProfileImage(
size: Dp,
image: String? = null,
icon: Int = R.drawable.ic_account_circle_filled,
color: Color = MaterialTheme.colors.secondary
color: Color = MaterialTheme.colors.secondaryVariant
) {
Box(Modifier.size(size)) {
if (image == null) {
@@ -3,9 +3,12 @@ package chat.simplex.app.views.helpers
import android.content.res.Configuration
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
@@ -41,14 +44,20 @@ fun CloseSheetBar(close: (() -> Unit)?, endButtons: @Composable RowScope.() -> U
@Composable
fun AppBarTitle(title: String, withPadding: Boolean = true) {
val theme = CurrentColors.collectAsState()
val titleColor = CurrentColors.collectAsState().value.appColors.title
val brush = if (theme.value.base == DefaultTheme.SIMPLEX)
Brush.linearGradient(listOf(titleColor.darker(0.2f), titleColor.lighter(0.35f)), Offset(0f, Float.POSITIVE_INFINITY), Offset(Float.POSITIVE_INFINITY, 0f))
else // color is not updated when changing themes if I pass null here
Brush.linearGradient(listOf(titleColor, titleColor), Offset(0f, Float.POSITIVE_INFINITY), Offset(Float.POSITIVE_INFINITY, 0f))
Text(
title,
Modifier
.fillMaxWidth()
.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,
style = MaterialTheme.typography.h1.copy(brush = brush),
color = MaterialTheme.colors.primaryVariant,
textAlign = TextAlign.Center
)
}
@@ -22,7 +22,6 @@ import androidx.compose.ui.text.input.*
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.app.R
import chat.simplex.app.ui.theme.HighOrLowlight
import chat.simplex.app.views.database.PassphraseStrength
import chat.simplex.app.views.database.validKey
import kotlinx.coroutines.delay
@@ -137,7 +136,7 @@ fun DefaultConfigurableTextField(
if (showKey) painterResource(R.drawable.ic_visibility_off_filled) else painterResource(R.drawable.ic_visibility_filled)
} else painterResource(R.drawable.ic_error)
val iconColor = if (valid) {
if (showPasswordStrength && state.value.text.isNotEmpty()) PassphraseStrength.check(state.value.text).color else HighOrLowlight
if (showPasswordStrength && state.value.text.isNotEmpty()) PassphraseStrength.check(state.value.text).color else MaterialTheme.colors.secondary
} else Color.Red
val keyboard = LocalSoftwareKeyboardController.current
val keyboardOptions = KeyboardOptions(
@@ -189,7 +188,7 @@ fun DefaultConfigurableTextField(
TextFieldDefaults.TextFieldDecorationBox(
value = state.value.text,
innerTextField = innerTextField,
placeholder = { Text(placeholder, color = HighOrLowlight) },
placeholder = { Text(placeholder, color = MaterialTheme.colors.secondary) },
singleLine = true,
enabled = enabled,
isError = !valid,
@@ -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,6 @@ fun DefaultDropdownMenu(
dropdownMenuItems: (@Composable () -> Unit)?
) {
MaterialTheme(
colors = MaterialTheme.colors.copy(surface = if (isInDarkTheme()) Color(0xFF0B0B0B) else MaterialTheme.colors.background),
shapes = MaterialTheme.shapes.copy(medium = RoundedCornerShape(corner = CornerSize(25.dp)))
) {
DropdownMenu(
@@ -27,6 +27,7 @@ fun DefaultDropdownMenu(
onDismissRequest = { showMenu.value = false },
Modifier
.widthIn(min = 250.dp)
.background(MaterialTheme.colors.surface)
.padding(vertical = 4.dp),
offset = offset,
) {
@@ -42,11 +43,13 @@ fun ExposedDropdownMenuBoxScope.DefaultExposedDropdownMenu(
dropdownMenuItems: (@Composable () -> Unit)?
) {
MaterialTheme(
colors = MaterialTheme.colors.copy(surface = if (isInDarkTheme()) Color(0xFF080808) else MaterialTheme.colors.background),
shapes = MaterialTheme.shapes.copy(medium = RoundedCornerShape(corner = CornerSize(25.dp)))
) {
ExposedDropdownMenu(
modifier = Modifier.widthIn(min = 200.dp).then(modifier),
modifier = Modifier
.widthIn(min = 200.dp)
.background(MaterialTheme.colors.surface)
.then(modifier),
expanded = expanded.value,
onDismissRequest = {
expanded.value = false
@@ -9,7 +9,6 @@ import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.*
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import chat.simplex.app.ui.theme.HighOrLowlight
@Composable
fun DefaultSwitch(
@@ -20,12 +19,12 @@ fun DefaultSwitch(
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
colors: SwitchColors = SwitchDefaults.colors(
checkedThumbColor = MaterialTheme.colors.primary,
uncheckedThumbColor = HighOrLowlight,
uncheckedThumbColor = MaterialTheme.colors.secondary,
checkedTrackAlpha = 0.0f,
uncheckedTrackAlpha = 0.0f,
)
) {
val color = if (checked) MaterialTheme.colors.primary.copy(alpha = 0.3f) else HighOrLowlight.copy(alpha = 0.3f)
val color = if (checked) MaterialTheme.colors.primary.copy(alpha = 0.3f) else MaterialTheme.colors.secondary.copy(alpha = 0.3f)
val size = with(LocalDensity.current) { Size(46.dp.toPx(), 28.dp.toPx()) }
val offset = with(LocalDensity.current) { Offset(1.dp.toPx(), 10.dp.toPx()) }
val radius = with(LocalDensity.current) { 28.dp.toPx() }
@@ -47,7 +47,7 @@ fun DefaultTopAppBar(
fun NavigationButtonBack(onButtonClicked: (() -> Unit)?) {
IconButton(onButtonClicked ?: {}, enabled = onButtonClicked != null) {
Icon(
painterResource(R.drawable.ic_arrow_back_ios_new), stringResource(R.string.back), tint = if (onButtonClicked != null) MaterialTheme.colors.primary else HighOrLowlight
painterResource(R.drawable.ic_arrow_back_ios_new), stringResource(R.string.back), tint = if (onButtonClicked != null) MaterialTheme.colors.primary else MaterialTheme.colors.secondary
)
}
}
@@ -23,7 +23,7 @@ fun <T> ExposedDropDownSettingRow(
selection: State<T>,
label: String? = null,
icon: Painter? = null,
iconTint: Color = HighOrLowlight,
iconTint: Color = MaterialTheme.colors.secondary,
enabled: State<Boolean> = mutableStateOf(true),
onSelected: (T) -> Unit
) {
@@ -46,13 +46,13 @@ fun <T> ExposedDropDownSettingRow(
Modifier.widthIn(max = maxWidth),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = HighOrLowlight
color = MaterialTheme.colors.secondary
)
Spacer(Modifier.size(12.dp))
Icon(
if (!expanded.value) painterResource(R.drawable.ic_expand_more) else painterResource(R.drawable.ic_expand_less),
generalGetString(R.string.icon_descr_more_button),
tint = HighOrLowlight
tint = MaterialTheme.colors.secondary
)
}
DefaultExposedDropdownMenu(
@@ -80,10 +80,16 @@ suspend fun PointerInputScope.detectGesture(
pressScope.release()
}
} catch (_: PointerEventTimeoutCancellationException) {
onLongPress?.invoke(down.position)
if (shouldConsume)
consumeUntilUp()
pressScope.release()
if (onLongPress != null) {
onLongPress(down.position)
if (shouldConsume)
consumeUntilUp()
pressScope.cancel()
} else {
if (shouldConsume)
consumeUntilUp()
pressScope.release()
}
}
}
}
@@ -7,6 +7,7 @@ import androidx.compose.foundation.background
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.graphics.asImageBitmap
@@ -19,9 +20,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.app.R
import chat.simplex.app.model.LinkPreview
import chat.simplex.app.ui.theme.HighOrLowlight
import chat.simplex.app.ui.theme.SimpleXTheme
import chat.simplex.app.views.chat.item.SentColorLight
import chat.simplex.app.ui.theme.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jsoup.Jsoup
@@ -79,8 +78,9 @@ suspend fun getLinkPreview(url: String): LinkPreview? {
@Composable
fun ComposeLinkView(linkPreview: LinkPreview?, cancelPreview: () -> Unit) {
val sentColor = CurrentColors.collectAsState().value.appColors.sentMessage
Row(
Modifier.fillMaxWidth().padding(top = 8.dp).background(SentColorLight),
Modifier.fillMaxWidth().padding(top = 8.dp).background(sentColor),
verticalAlignment = Alignment.CenterVertically
) {
if (linkPreview == null) {
@@ -90,7 +90,7 @@ fun ComposeLinkView(linkPreview: LinkPreview?, cancelPreview: () -> Unit) {
) {
CircularProgressIndicator(
Modifier.size(16.dp),
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
strokeWidth = 2.dp
)
}
@@ -134,7 +134,7 @@ fun ChatItemLinkView(linkPreview: LinkPreview) {
if (linkPreview.description != "") {
Text(linkPreview.description, maxLines = 12, overflow = TextOverflow.Ellipsis, fontSize = 14.sp, lineHeight = 20.sp)
}
Text(linkPreview.uri, maxLines = 1, overflow = TextOverflow.Ellipsis, fontSize = 12.sp, color = HighOrLowlight)
Text(linkPreview.uri, maxLines = 1, overflow = TextOverflow.Ellipsis, fontSize = 12.sp, color = MaterialTheme.colors.secondary)
}
}
}
@@ -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() }
}
@@ -25,7 +25,7 @@ fun SectionView(title: String? = null, padding: PaddingValues = PaddingValues(),
Column {
if (title != null) {
Text(
title, color = HighOrLowlight, style = MaterialTheme.typography.body2,
title, color = MaterialTheme.colors.secondary, style = MaterialTheme.typography.body2,
modifier = Modifier.padding(start = DEFAULT_PADDING, bottom = 5.dp), fontSize = 12.sp
)
}
@@ -37,7 +37,7 @@ fun SectionView(title: String? = null, padding: PaddingValues = PaddingValues(),
fun SectionView(
title: String,
icon: Painter,
iconTint: Color = HighOrLowlight,
iconTint: Color = MaterialTheme.colors.secondary,
leadingIcon: Boolean = false,
padding: PaddingValues = PaddingValues(),
content: (@Composable ColumnScope.() -> Unit)
@@ -46,7 +46,7 @@ fun SectionView(
val iconSize = with(LocalDensity.current) { 21.sp.toDp() }
Row(Modifier.padding(start = DEFAULT_PADDING, bottom = 5.dp), verticalAlignment = Alignment.CenterVertically) {
if (leadingIcon) Icon(icon, null, Modifier.padding(end = DEFAULT_PADDING_HALF).size(iconSize), tint = iconTint)
Text(title, color = HighOrLowlight, style = MaterialTheme.typography.body2, fontSize = 12.sp)
Text(title, color = MaterialTheme.colors.secondary, style = MaterialTheme.typography.body2, fontSize = 12.sp)
if (!leadingIcon) Icon(icon, null, Modifier.padding(start = DEFAULT_PADDING_HALF).size(iconSize), tint = iconTint)
}
Column(Modifier.padding(padding).fillMaxWidth()) { content() }
@@ -147,7 +147,7 @@ fun <T> SectionItemWithValue(
values: List<ValueTitle<T>>,
label: String? = null,
icon: Painter? = null,
iconTint: Color = HighOrLowlight,
iconTint: Color = MaterialTheme.colors.secondary,
enabled: State<Boolean> = mutableStateOf(true),
onSelected: () -> Unit
) {
@@ -161,7 +161,7 @@ fun <T> SectionItemWithValue(
values.first { it.value == currentValue.value }.title + (if (label != null) " $label" else ""),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = HighOrLowlight
color = MaterialTheme.colors.secondary
)
}
}
@@ -177,7 +177,7 @@ fun SectionTextFooter(text: AnnotatedString) {
Text(
text,
Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, top = DEFAULT_PADDING_HALF).fillMaxWidth(0.9F),
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
lineHeight = 18.sp,
fontSize = 14.sp
)
@@ -228,10 +228,10 @@ fun InfoRow(title: String, value: String, icon: Painter? = null, iconTint: Color
SectionItemViewSpaceBetween {
Row {
val iconSize = with(LocalDensity.current) { 21.sp.toDp() }
if (icon != null) Icon(icon, title, Modifier.padding(end = 8.dp).size(iconSize), tint = iconTint ?: HighOrLowlight)
if (icon != null) Icon(icon, title, Modifier.padding(end = 8.dp).size(iconSize), tint = iconTint ?: MaterialTheme.colors.secondary)
Text(title)
}
Text(value, color = HighOrLowlight)
Text(value, color = MaterialTheme.colors.secondary)
}
}
@@ -247,7 +247,7 @@ fun InfoRowEllipsis(title: String, value: String, onClick: () -> Unit) {
.widthIn(max = (configuration.screenWidthDp / 2).dp),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = HighOrLowlight
color = MaterialTheme.colors.secondary
)
}
}
@@ -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
@@ -54,10 +55,10 @@ fun SimpleButton(
) {
SimpleButtonFrame(click, disabled = disabled) {
Icon(
icon, text, tint = if (disabled) HighOrLowlight else color,
icon, text, tint = if (disabled) MaterialTheme.colors.secondary else color,
modifier = Modifier.padding(end = 8.dp)
)
Text(text, style = MaterialTheme.typography.caption, color = if (disabled) HighOrLowlight else color)
Text(text, style = MaterialTheme.typography.caption, color = if (disabled) MaterialTheme.colors.secondary else color)
}
}
@@ -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,
@@ -18,7 +18,6 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.unit.*
import chat.simplex.app.ui.theme.DEFAULT_PADDING
import chat.simplex.app.ui.theme.HighOrLowlight
@Composable
fun TextEditor(
@@ -41,11 +40,11 @@ fun TextEditor(
autoCorrect = false
),
modifier = modifier,
cursorBrush = SolidColor(HighOrLowlight),
cursorBrush = SolidColor(MaterialTheme.colors.secondary),
decorationBox = { innerTextField ->
Surface(
shape = if (border) RoundedCornerShape(10.dp) else RectangleShape,
border = if (border) BorderStroke(1.dp, MaterialTheme.colors.secondary) else null
border = if (border) BorderStroke(1.dp, MaterialTheme.colors.secondaryVariant) else null
) {
Row(
Modifier.background(background),
@@ -37,6 +37,8 @@ import androidx.core.text.HtmlCompat
import chat.simplex.app.*
import chat.simplex.app.R
import chat.simplex.app.model.*
import chat.simplex.app.ui.theme.ThemeOverrides
import com.charleskorn.kaml.decodeFromStream
import kotlinx.coroutines.*
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
@@ -388,6 +390,22 @@ fun getDrawableFromUri(uri: Uri, withAlertOnException: Boolean = true): Drawable
}
}
fun getThemeFromUri(uri: Uri, withAlertOnException: Boolean = true): ThemeOverrides? {
SimplexApp.context.contentResolver.openInputStream(uri).use {
runCatching {
return yaml.decodeFromStream<ThemeOverrides>(it!!)
}.onFailure {
if (withAlertOnException) {
AlertManager.shared.showAlertMsg(
title = generalGetString(R.string.import_theme_error),
text = generalGetString(R.string.import_theme_error_desc),
)
}
}
}
return null
}
fun saveImage(context: Context, uri: Uri): String? {
val bitmap = getBitmapFromUri(uri) ?: return null
return saveImage(context, bitmap)
@@ -566,6 +584,9 @@ fun getBitmapFromVideo(uri: Uri, timestamp: Long? = null, random: Boolean = true
fun Color.darker(factor: Float = 0.1f): Color =
Color(max(red * (1 - factor), 0f), max(green * (1 - factor), 0f), max(blue * (1 - factor), 0f), alpha)
fun Color.lighter(factor: Float = 0.1f): Color =
Color(min(red * (1 + factor), 1f), min(green * (1 + factor), 1f), min(blue * (1 + factor), 1f), alpha)
fun ByteArray.toBase64String() = Base64.encodeToString(this, Base64.DEFAULT)
fun String.toByteArrayFromBase64() = Base64.decode(this, Base64.DEFAULT)
@@ -17,7 +17,6 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.*
import chat.simplex.app.ui.theme.HighOrLowlight
@Composable
fun PasscodeEntry(
@@ -129,7 +128,7 @@ private fun PasswordDigit(size: Dp, d: Int, password: MutableState<String>) {
fontSize = 30.sp,
letterSpacing = (-0.5).sp
),
color = HighOrLowlight
color = MaterialTheme.colors.secondary
)
}
}
@@ -137,7 +136,7 @@ private fun PasswordDigit(size: Dp, d: Int, password: MutableState<String>) {
@Composable
private fun PasswordEdit(size: Dp, image: Painter, action: () -> Unit) {
PasswordButton(size, action) {
Icon(image, null, tint = HighOrLowlight)
Icon(image, null, tint = MaterialTheme.colors.secondary)
}
}
@@ -63,7 +63,7 @@ fun AddContactLayout(connReq: String, connIncognito: Boolean, share: () -> Unit)
.size(36.dp)
.padding(4.dp)
.align(Alignment.CenterHorizontally),
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
strokeWidth = 3.dp
)
}
@@ -113,7 +113,7 @@ fun InfoAboutIncognito(chatModelIncognito: Boolean, supportedIncognito: Boolean
Icon(
painterResource(R.drawable.ic_info),
stringResource(R.string.incognito),
tint = HighOrLowlight,
tint = MaterialTheme.colors.secondary,
modifier = Modifier.padding(end = 10.dp).size(20.dp)
)
Text(offText, textAlign = if (centered) TextAlign.Center else TextAlign.Left, style = MaterialTheme.typography.body2)
@@ -141,7 +141,7 @@ fun AddGroupLayout(createGroup: (GroupProfile) -> Unit, close: () -> Unit) {
}
.padding(8.dp))
} else {
CreateGroupButton(HighOrLowlight, Modifier.padding(8.dp))
CreateGroupButton(MaterialTheme.colors.secondary, Modifier.padding(8.dp))
}
LaunchedEffect(Unit) {
delay(300)
@@ -159,7 +159,7 @@ fun CreateGroupButton(color: Color, modifier: Modifier) {
Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End
) {
Surface(shape = RoundedCornerShape(20.dp)) {
Surface(shape = RoundedCornerShape(20.dp), color = Color.Transparent) {
Row(modifier, verticalAlignment = Alignment.CenterVertically) {
Text(stringResource(R.string.create_profile_button), style = MaterialTheme.typography.caption, color = color, fontWeight = FontWeight.Bold)
Icon(painterResource(R.drawable.ic_arrow_forward_ios), stringResource(R.string.create_profile_button), tint = color)
@@ -4,12 +4,12 @@ import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.sp
import chat.simplex.app.R
import chat.simplex.app.model.ChatModel
import chat.simplex.app.ui.theme.HighOrLowlight
enum class ConnectViaLinkTab {
SCAN, PASTE
@@ -44,7 +44,7 @@ fun ConnectViaLinkView(m: ChatModel, close: () -> Unit) {
}
TabRow(
selectedTabIndex = selection.value.ordinal,
backgroundColor = MaterialTheme.colors.background,
backgroundColor = Color.Transparent,
contentColor = MaterialTheme.colors.primary,
) {
tabTitles.forEachIndexed { index, it ->
@@ -62,7 +62,7 @@ fun ConnectViaLinkView(m: ChatModel, close: () -> Unit) {
)
},
selectedContentColor = MaterialTheme.colors.primary,
unselectedContentColor = HighOrLowlight,
unselectedContentColor = MaterialTheme.colors.secondary,
)
}
}
@@ -5,13 +5,13 @@ import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.sp
import chat.simplex.app.R
import chat.simplex.app.model.ChatModel
import chat.simplex.app.ui.theme.DEFAULT_PADDING
import chat.simplex.app.ui.theme.HighOrLowlight
import chat.simplex.app.views.helpers.ModalManager
import chat.simplex.app.views.helpers.withApi
import chat.simplex.app.views.usersettings.UserAddressView
@@ -67,7 +67,7 @@ fun CreateLinkView(m: ChatModel, initialSelection: CreateLinkTab) {
}
TabRow(
selectedTabIndex = selection.value.ordinal,
backgroundColor = MaterialTheme.colors.background,
backgroundColor = Color.Transparent,
contentColor = MaterialTheme.colors.primary,
) {
tabTitles.forEachIndexed { index, it ->
@@ -84,7 +84,7 @@ fun CreateLinkView(m: ChatModel, initialSelection: CreateLinkTab) {
)
},
selectedContentColor = MaterialTheme.colors.primary,
unselectedContentColor = HighOrLowlight,
unselectedContentColor = MaterialTheme.colors.secondary,
)
}
}
@@ -150,7 +150,7 @@ private fun NewChatSheetLayout(
hoveredElevation = 0.dp,
focusedElevation = 0.dp,
),
backgroundColor = if (!stopped) MaterialTheme.colors.primary else HighOrLowlight,
backgroundColor = if (!stopped) MaterialTheme.colors.primary else MaterialTheme.colors.secondary,
contentColor = Color.White
) {
Icon(
@@ -173,14 +173,14 @@ fun ActionButton(
disabled: Boolean = false,
click: () -> Unit = {}
) {
Surface(shape = RoundedCornerShape(18.dp)) {
Surface(shape = RoundedCornerShape(18.dp), color = Color.Transparent) {
Column(
Modifier
.clickable(onClick = click)
.padding(8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
val tint = if (disabled) HighOrLowlight else MaterialTheme.colors.primary
val tint = if (disabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
Icon(
icon, text,
tint = tint,
@@ -226,7 +226,7 @@ fun ActionButton(
.padding(8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
val tint = if (disabled) HighOrLowlight else tint
val tint = if (disabled) MaterialTheme.colors.secondary else tint
Icon(
icon, text,
tint = tint,
@@ -45,7 +45,7 @@ fun PasteToConnectView(chatModel: ChatModel, close: () -> Unit) {
}
}
if (linkType == ConnectionLinkType.GROUP) {
AlertManager.shared.showAlertMsg(
AlertManager.shared.showAlertDialog(
title = generalGetString(R.string.connect_via_group_link),
text = generalGetString(R.string.you_will_join_group),
confirmText = generalGetString(R.string.connect_via_link_verb),
@@ -48,7 +48,7 @@ fun ScanToConnectView(chatModel: ChatModel, close: () -> Unit) {
}
}
if (linkType == ConnectionLinkType.GROUP) {
AlertManager.shared.showAlertMsg(
AlertManager.shared.showAlertDialog(
title = generalGetString(R.string.connect_via_group_link),
text = generalGetString(R.string.you_will_join_group),
confirmText = generalGetString(R.string.connect_via_link_verb),
@@ -31,7 +31,6 @@ fun CreateProfile(chatModel: ChatModel, close: () -> Unit) {
Box(
modifier = Modifier
.fillMaxSize()
.background(color = MaterialTheme.colors.background)
.padding(20.dp)
) {
CreateProfilePanel(chatModel, close)
@@ -53,7 +53,7 @@ fun SetNotificationsMode(m: ChatModel) {
private fun NotificationButton(currentMode: MutableState<NotificationsMode>, mode: NotificationsMode, @StringRes title: Int, @StringRes description: Int) {
TextButton(
onClick = { currentMode.value = mode },
border = BorderStroke(1.dp, color = if (currentMode.value == mode) MaterialTheme.colors.primary else HighOrLowlight.copy(alpha = 0.5f)),
border = BorderStroke(1.dp, color = if (currentMode.value == mode) MaterialTheme.colors.primary else MaterialTheme.colors.secondary.copy(alpha = 0.5f)),
shape = RoundedCornerShape(35.dp),
) {
Column(Modifier.padding(horizontal = 14.dp).padding(top = 4.dp, bottom = 8.dp)) {
@@ -61,7 +61,7 @@ private fun NotificationButton(currentMode: MutableState<NotificationsMode>, mod
stringResource(title),
style = MaterialTheme.typography.h2,
fontWeight = FontWeight.Medium,
color = if (currentMode.value == mode) MaterialTheme.colors.primary else HighOrLowlight,
color = if (currentMode.value == mode) MaterialTheme.colors.primary else MaterialTheme.colors.secondary,
modifier = Modifier.padding(bottom = 8.dp).align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center
)
@@ -1,6 +1,7 @@
package chat.simplex.app.views.onboarding
import android.content.res.Configuration
import android.os.Build
import androidx.annotation.IntegerRes
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
@@ -9,6 +10,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
@@ -18,6 +20,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import chat.simplex.app.BuildConfig
import chat.simplex.app.R
import chat.simplex.app.model.ChatModel
import chat.simplex.app.ui.theme.*
@@ -47,7 +50,7 @@ fun WhatsNewView(viaSettings: Boolean = false, close: () -> Unit) {
horizontalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.padding(bottom = 4.dp)
) {
Icon(icon, stringResource(titleId), tint = HighOrLowlight)
Icon(icon, stringResource(titleId), tint = MaterialTheme.colors.secondary)
Text(
generalGetString(titleId),
maxLines = 1,
@@ -71,7 +74,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 +90,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 +116,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)
@@ -60,7 +60,7 @@ private fun AcceptRequestsLayout(
PreferenceToggleWithIcon(
stringResource(R.string.incognito),
if (autoAcceptState.value.incognito) painterResource(R.drawable.ic_theater_comedy_filled) else painterResource(R.drawable.ic_theater_comedy),
if (autoAcceptState.value.incognito) Indigo else HighOrLowlight,
if (autoAcceptState.value.incognito) Indigo else MaterialTheme.colors.secondary,
autoAcceptState.value.incognito,
) {
autoAcceptState.value = AutoAcceptState(autoAcceptState.value.enable, it, autoAcceptState.value.welcomeText)
@@ -81,7 +81,7 @@ private fun AcceptRequestsLayout(
Spacer(Modifier.height(DEFAULT_PADDING))
if (autoAcceptState.value.enable) {
Text(
stringResource(R.string.section_title_welcome_message), color = HighOrLowlight, style = MaterialTheme.typography.body2,
stringResource(R.string.section_title_welcome_message), color = MaterialTheme.colors.secondary, style = MaterialTheme.typography.body2,
modifier = Modifier.padding(start = DEFAULT_PADDING, bottom = 5.dp), fontSize = 12.sp
)
TextEditor(Modifier.padding(horizontal = DEFAULT_PADDING).height(160.dp), text = welcomeText)
@@ -184,13 +184,13 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
}
} else {
SectionItemView {
Text("TCP_KEEPIDLE", color = HighOrLowlight)
Text("TCP_KEEPIDLE", color = MaterialTheme.colors.secondary)
}
SectionItemView {
Text("TCP_KEEPINTVL", color = HighOrLowlight)
Text("TCP_KEEPINTVL", color = MaterialTheme.colors.secondary)
}
SectionItemView {
Text("TCP_KEEPCNT", color = HighOrLowlight)
Text("TCP_KEEPCNT", color = MaterialTheme.colors.secondary)
}
}
}
@@ -208,7 +208,7 @@ fun ResetToDefaultsButton(reset: () -> Unit, disabled: Boolean) {
modifier.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically
) {
val color = if (disabled) HighOrLowlight else MaterialTheme.colors.primary
val color = if (disabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
Text(stringResource(R.string.network_options_reset_to_defaults), color = color)
}
}
@@ -256,14 +256,14 @@ fun IntSettingRow(title: String, selection: MutableState<Int>, values: List<Int>
"${selection.value} $label",
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = HighOrLowlight
color = MaterialTheme.colors.secondary
)
Spacer(Modifier.size(4.dp))
Icon(
if (!expanded.value) painterResource(R.drawable.ic_expand_more) else painterResource(R.drawable.ic_expand_less),
generalGetString(R.string.invite_to_group_button),
modifier = Modifier.padding(start = 8.dp),
tint = HighOrLowlight
tint = MaterialTheme.colors.secondary
)
}
DefaultExposedDropdownMenu(
@@ -317,14 +317,14 @@ fun TimeoutSettingRow(title: String, selection: MutableState<Long>, values: List
"${df.format(selection.value / 1_000_000.toDouble())} $label",
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = HighOrLowlight
color = MaterialTheme.colors.secondary
)
Spacer(Modifier.size(4.dp))
Icon(
if (!expanded.value) painterResource(R.drawable.ic_expand_more) else painterResource(R.drawable.ic_expand_less),
generalGetString(R.string.invite_to_group_button),
modifier = Modifier.padding(start = 8.dp),
tint = HighOrLowlight
tint = MaterialTheme.colors.secondary
)
}
DefaultExposedDropdownMenu(
@@ -376,18 +376,18 @@ fun FooterButton(icon: Painter, title: String, action: () -> Unit, disabled: Boo
Icon(
icon,
title,
tint = if (disabled) HighOrLowlight else MaterialTheme.colors.primary
tint = if (disabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
)
Text(
title,
color = if (disabled) HighOrLowlight else MaterialTheme.colors.primary
color = if (disabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
)
}
}
}
fun showUpdateNetworkSettingsDialog(action: () -> Unit) {
AlertManager.shared.showAlertMsg(
AlertManager.shared.showAlertDialog(
title = generalGetString(R.string.update_network_settings_question),
text = generalGetString(R.string.updating_settings_will_reconnect_client_to_all_servers),
confirmText = generalGetString(R.string.update_network_settings_confirmation),
@@ -1,16 +1,23 @@
package chat.simplex.app.views.usersettings
import SectionBottomSpacer
import SectionCustomFooter
import SectionDividerSpaced
import SectionItemView
import SectionItemViewSpaceBetween
import SectionSpacer
import SectionView
import android.app.Activity
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
import android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
import android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED
import android.net.Uri
import android.util.Log
import android.widget.Toast
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyRow
@@ -32,13 +39,15 @@ import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toBitmap
import chat.simplex.app.*
import chat.simplex.app.R
import chat.simplex.app.model.ChatModel
import chat.simplex.app.model.SharedPreference
import chat.simplex.app.model.*
import chat.simplex.app.ui.theme.*
import chat.simplex.app.views.helpers.*
import com.godaddy.android.colorpicker.*
import kotlinx.coroutines.delay
import kotlinx.serialization.encodeToString
import java.io.BufferedOutputStream
import java.util.*
import kotlin.collections.ArrayList
enum class AppIcon(val resId: Int) {
DEFAULT(R.mipmap.icon),
@@ -46,7 +55,7 @@ enum class AppIcon(val resId: Int) {
}
@Composable
fun AppearanceView(m: ChatModel) {
fun AppearanceView(m: ChatModel, showSettingsModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit)) {
val appIcon = remember { mutableStateOf(findEnabledIcon()) }
fun setAppIcon(newIcon: AppIcon) {
@@ -69,10 +78,12 @@ fun AppearanceView(m: ChatModel) {
AppearanceLayout(
appIcon,
m.controller.appPrefs.appLanguage,
m.controller.appPrefs.systemDarkTheme,
changeIcon = ::setAppIcon,
editPrimaryColor = { primary ->
showSettingsModal = showSettingsModal,
editColor = { name, initialColor ->
ModalManager.shared.showModalCloseable { close ->
ColorEditor(primary, close)
ColorEditor(name, initialColor, close)
}
},
)
@@ -81,8 +92,10 @@ fun AppearanceView(m: ChatModel) {
@Composable fun AppearanceLayout(
icon: MutableState<AppIcon>,
languagePref: SharedPreference<String?>,
systemDarkTheme: SharedPreference<String?>,
changeIcon: (AppIcon) -> Unit,
editPrimaryColor: (Color) -> Unit,
showSettingsModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit),
editColor: (ThemeColor, Color) -> Unit,
) {
Column(
Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
@@ -127,7 +140,7 @@ fun AppearanceView(m: ChatModel) {
contentDescription = "",
contentScale = ContentScale.Fit,
modifier = Modifier
.shadow(if (item == icon.value) 1.dp else 0.dp, ambientColor = colors.secondary)
.shadow(if (item == icon.value) 1.dp else 0.dp, ambientColor = colors.secondaryVariant)
.size(70.dp)
.clickable { changeIcon(item) }
.padding(10.dp)
@@ -144,26 +157,108 @@ 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) {
DarkThemeSelector(remember { systemDarkTheme.state }) {
ThemeManager.changeDarkTheme(it, darkTheme)
}
}
}
SectionItemView(showSettingsModal { _ -> CustomizeThemeView(editColor) }) { Text(stringResource(R.string.customize_theme_title)) }
SectionBottomSpacer()
}
}
@Composable
fun CustomizeThemeView(editColor: (ThemeColor, Color) -> Unit) {
Column(
Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
) {
val currentTheme by CurrentColors.collectAsState()
AppBarTitle(stringResource(R.string.customize_theme_title))
SectionView(stringResource(R.string.theme_colors_section_title)) {
SectionItemViewSpaceBetween({ editColor(ThemeColor.PRIMARY, currentTheme.colors.primary) }) {
val title = generalGetString(R.string.color_primary)
Text(title)
Icon(painterResource(R.drawable.ic_circle_filled), title, tint = colors.primary)
}
SectionItemViewSpaceBetween({ editColor(ThemeColor.PRIMARY_VARIANT, currentTheme.colors.primaryVariant) }) {
val title = generalGetString(R.string.color_primary_variant)
Text(title)
Icon(painterResource(R.drawable.ic_circle_filled), title, tint = colors.primaryVariant)
}
SectionItemViewSpaceBetween({ editColor(ThemeColor.SECONDARY, currentTheme.colors.secondary) }) {
val title = generalGetString(R.string.color_secondary)
Text(title)
Icon(painterResource(R.drawable.ic_circle_filled), title, tint = colors.secondary)
}
SectionItemViewSpaceBetween({ editColor(ThemeColor.SECONDARY_VARIANT, currentTheme.colors.secondaryVariant) }) {
val title = generalGetString(R.string.color_secondary_variant)
Text(title)
Icon(painterResource(R.drawable.ic_circle_filled), title, tint = colors.secondaryVariant)
}
SectionItemViewSpaceBetween({ editColor(ThemeColor.BACKGROUND, currentTheme.colors.background) }) {
val title = generalGetString(R.string.color_background)
Text(title)
Icon(painterResource(R.drawable.ic_circle_filled), title, tint = colors.background)
}
SectionItemViewSpaceBetween({ editColor(ThemeColor.SURFACE, currentTheme.colors.surface) }) {
val title = generalGetString(R.string.color_surface)
Text(title)
Icon(painterResource(R.drawable.ic_circle_filled), title, tint = colors.surface)
}
SectionItemViewSpaceBetween({ editColor(ThemeColor.TITLE, currentTheme.appColors.title) }) {
val title = generalGetString(R.string.color_title)
Text(title)
Icon(painterResource(R.drawable.ic_circle_filled), title, tint = currentTheme.appColors.title)
}
SectionItemViewSpaceBetween({ editColor(ThemeColor.SENT_MESSAGE, currentTheme.appColors.sentMessage) }) {
val title = generalGetString(R.string.color_sent_message)
Text(title)
Icon(painterResource(R.drawable.ic_circle_filled), title, tint = currentTheme.appColors.sentMessage)
}
SectionItemViewSpaceBetween({ editColor(ThemeColor.RECEIVED_MESSAGE, currentTheme.appColors.receivedMessage) }) {
val title = generalGetString(R.string.color_received_message)
Text(title)
Icon(painterResource(R.drawable.ic_circle_filled), title, tint = currentTheme.appColors.receivedMessage)
}
}
if (currentTheme.first.primary != LightColorPalette.primary) {
SectionCustomFooter(PaddingValues(start = 7.dp, end = 7.dp, top = 5.dp)) {
TextButton(
onClick = {
ThemeManager.saveAndApplyPrimaryColor(LightColorPalette.primary)
},
) {
Text(generalGetString(R.string.reset_color))
val isInDarkTheme = isInDarkTheme()
if (currentTheme.base.hasChangedAnyColor(currentTheme.colors, currentTheme.appColors)) {
SectionItemView({ ThemeManager.resetAllThemeColors(darkForSystemTheme = isInDarkTheme) }) {
Text(generalGetString(R.string.reset_color), color = colors.primary)
}
}
SectionSpacer()
SectionView {
val context = LocalContext.current
val theme = remember { mutableStateOf(null as String?) }
val exportThemeLauncher = rememberSaveThemeLauncher(context, theme)
SectionItemView({
val overrides = ThemeManager.currentThemeOverridesForExport(isInDarkTheme)
theme.value = yaml.encodeToString<ThemeOverrides>(overrides)
exportThemeLauncher.launch("simplex.theme")
}) {
Text(generalGetString(R.string.export_theme), color = colors.primary)
}
val importThemeLauncher = rememberGetContentLauncher { uri: Uri? ->
if (uri != null) {
val theme = getThemeFromUri(uri)
if (theme != null) {
ThemeManager.saveAndApplyThemeOverrides(theme, isInDarkTheme)
}
}
}
// Can not limit to YAML mime type since it's unsupported by Android
SectionItemView({ importThemeLauncher.launch("*/*") }) {
Text(generalGetString(R.string.import_theme), color = colors.primary)
}
}
SectionBottomSpacer()
}
@@ -171,6 +266,7 @@ fun AppearanceView(m: ChatModel) {
@Composable
fun ColorEditor(
name: ThemeColor,
initialColor: Color,
close: () -> Unit,
) {
@@ -178,17 +274,17 @@ fun ColorEditor(
Modifier
.fillMaxWidth()
) {
AppBarTitle(stringResource(R.string.color_primary))
AppBarTitle(name.text)
var currentColor by remember { mutableStateOf(initialColor) }
ColorPicker(initialColor) {
currentColor = it
}
SectionSpacer()
val isInDarkTheme = isInDarkTheme()
TextButton(
onClick = {
ThemeManager.saveAndApplyPrimaryColor(currentColor)
ThemeManager.saveAndApplyThemeColor(name, currentColor, isInDarkTheme)
close()
},
Modifier.align(Alignment.CenterHorizontally),
@@ -206,7 +302,7 @@ fun ColorPicker(initialColor: Color, onColorChanged: (Color) -> Unit) {
modifier = Modifier
.fillMaxWidth()
.height(300.dp),
showAlphaBar = false,
showAlphaBar = true,
onColorChanged = { color: HsvColor ->
onColorChanged(color.toColor())
}
@@ -241,9 +337,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,10 +350,55 @@ 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.SIMPLEX.name to generalGetString(R.string.theme_simplex))
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)))
//}
@Composable
private fun rememberSaveThemeLauncher(cxt: Context, theme: MutableState<String?>): ManagedActivityResultLauncher<String, Uri?> =
rememberLauncherForActivityResult(
contract = ActivityResultContracts.CreateDocument(),
onResult = { destination ->
try {
destination?.let {
val theme = theme.value
if (theme != null) {
val contentResolver = cxt.contentResolver
contentResolver.openOutputStream(destination)?.let { stream ->
BufferedOutputStream(stream).use { outputStream ->
theme.byteInputStream().use { it.copyTo(outputStream) }
}
Toast.makeText(cxt, generalGetString(R.string.file_saved), Toast.LENGTH_SHORT).show()
}
}
}
} catch (e: Error) {
Toast.makeText(cxt, generalGetString(R.string.error_saving_file), Toast.LENGTH_SHORT).show()
Log.e(TAG, "rememberSaveThemeLauncher error saving theme $e")
} finally {
theme.value = null
}
}
)
private fun findEnabledIcon(): AppIcon = AppIcon.values().first { icon ->
SimplexApp.context.packageManager.getComponentEnabledSetting(
ComponentName(BuildConfig.APPLICATION_ID, "chat.simplex.app.MainActivity_${icon.name.lowercase()}")
@@ -271,8 +412,10 @@ fun PreviewAppearanceSettings() {
AppearanceLayout(
icon = remember { mutableStateOf(AppIcon.DARK_BLUE) },
languagePref = SharedPreference({ null }, {}),
systemDarkTheme = SharedPreference({ null }, {}),
changeIcon = {},
editPrimaryColor = {},
showSettingsModal = { {} },
editColor = { _, _ -> },
)
}
}
@@ -16,7 +16,6 @@ import androidx.compose.ui.res.stringResource
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.views.helpers.*
@Composable
@@ -108,7 +107,7 @@ fun SharedPreferenceToggleWithIcon(
) {
val prefState = preferenceState ?: remember { mutableStateOf(preference.get()) }
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Text(text, Modifier.padding(end = 4.dp), color = if (stopped) HighOrLowlight else Color.Unspecified)
Text(text, Modifier.padding(end = 4.dp), color = if (stopped) MaterialTheme.colors.secondary else Color.Unspecified)
Icon(
icon,
null,
@@ -77,7 +77,7 @@ private fun HiddenProfileLayout(
PassphraseField(confirmHidePassword, stringResource(R.string.confirm_password), isValid = { confirmValid }, dependsOn = hidePassword)
}
SectionItemViewSpaceBetween({ saveProfilePassword(hidePassword.value) }, disabled = saveDisabled, minHeight = TextFieldDefaults.MinHeight) {
Text(generalGetString(R.string.save_profile_password), color = if (saveDisabled) HighOrLowlight else MaterialTheme.colors.primary)
Text(generalGetString(R.string.save_profile_password), color = if (saveDisabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary)
}
}
SectionTextFooter(stringResource(R.string.to_reveal_profile_enter_password))
@@ -59,7 +59,7 @@ fun NetworkAndServersView(
showCustomModal = showCustomModal,
toggleSocksProxy = { enable ->
if (enable) {
AlertManager.shared.showAlertMsg(
AlertManager.shared.showAlertDialog(
title = generalGetString(R.string.network_enable_socks),
text = generalGetString(R.string.network_enable_socks_info),
confirmText = generalGetString(R.string.confirm_verb),
@@ -73,7 +73,7 @@ fun NetworkAndServersView(
}
)
} else {
AlertManager.shared.showAlertMsg(
AlertManager.shared.showAlertDialog(
title = generalGetString(R.string.network_disable_socks),
text = generalGetString(R.string.network_disable_socks_info),
confirmText = generalGetString(R.string.confirm_verb),
@@ -207,7 +207,7 @@ fun UseSocksProxySwitch(
Icon(
painterResource(R.drawable.ic_settings_ethernet),
stringResource(R.string.network_socks_toggle),
tint = HighOrLowlight
tint = MaterialTheme.colors.secondary
)
TextIconSpaced(false)
if (networkUseSocksProxy.value) {
@@ -88,7 +88,7 @@ fun NotificationsSettingsLayout(
modes.first { it.value == notificationsMode.value }.title,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = HighOrLowlight
color = MaterialTheme.colors.secondary
)
}
SettingsActionItemWithContent(null, stringResource(R.string.settings_notification_preview_mode_title), { showPage(CurrentPage.NOTIFICATION_PREVIEW_MODE) }) {
@@ -96,7 +96,7 @@ fun NotificationsSettingsLayout(
previewModes.first { it.value == notificationPreviewMode.value }.title,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = HighOrLowlight
color = MaterialTheme.colors.secondary
)
}
}
@@ -112,7 +112,7 @@ private fun TimedMessagesFeatureSection(allowFeature: State<FeatureAllowed>, onS
PreferenceToggleWithIcon(
ChatFeature.TimedMessages.text,
ChatFeature.TimedMessages.icon,
HighOrLowlight,
MaterialTheme.colors.secondary,
allowFeature.value == FeatureAllowed.ALWAYS || allowFeature.value == FeatureAllowed.YES,
extraPadding = false,
onSelected
@@ -125,10 +125,10 @@ private fun TimedMessagesFeatureSection(allowFeature: State<FeatureAllowed>, onS
private fun ResetSaveButtons(reset: () -> Unit, save: () -> Unit, disabled: Boolean) {
SectionView {
SectionItemView(reset, disabled = disabled) {
Text(stringResource(R.string.reset_verb), color = if (disabled) HighOrLowlight else MaterialTheme.colors.primary)
Text(stringResource(R.string.reset_verb), color = if (disabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary)
}
SectionItemView(save, disabled = disabled) {
Text(stringResource(R.string.save_and_notify_contacts), color = if (disabled) HighOrLowlight else MaterialTheme.colors.primary)
Text(stringResource(R.string.save_and_notify_contacts), color = if (disabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary)
}
}
}
@@ -60,7 +60,7 @@ fun ProtocolServerView(m: ChatModel, server: ServerCfg, serverProtocol: ServerPr
Modifier
.padding(horizontal = 2.dp)
.size(30.dp),
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
strokeWidth = 2.5.dp
)
}
@@ -107,7 +107,7 @@ private fun PresetServer(
Modifier.padding(start = DEFAULT_PADDING, top = 5.dp, end = DEFAULT_PADDING, bottom = 10.dp),
style = TextStyle(
fontFamily = FontFamily.Monospace, fontSize = 16.sp,
color = HighOrLowlight
color = MaterialTheme.colors.secondary
)
)
}
@@ -172,13 +172,13 @@ private fun UseServerSection(
) {
SectionView(stringResource(R.string.smp_servers_use_server).uppercase()) {
SectionItemViewSpaceBetween(testServer, disabled = !valid || testing) {
Text(stringResource(R.string.smp_servers_test_server), color = if (valid && !testing) MaterialTheme.colors.onBackground else HighOrLowlight)
Text(stringResource(R.string.smp_servers_test_server), color = if (valid && !testing) MaterialTheme.colors.onBackground else MaterialTheme.colors.secondary)
ShowTestStatus(server)
}
val enabled = rememberUpdatedState(server.enabled)
PreferenceToggle(stringResource(R.string.smp_servers_use_server_for_new_conn), enabled.value) { onUpdate(server.copy(enabled = it)) }
SectionItemView(onDelete, disabled = testing) {
Text(stringResource(R.string.smp_servers_delete_server), color = if (testing) HighOrLowlight else MaterialTheme.colors.error)
Text(stringResource(R.string.smp_servers_delete_server), color = if (testing) MaterialTheme.colors.secondary else MaterialTheme.colors.error)
}
}
}
@@ -166,7 +166,7 @@ fun ProtocolServersView(m: ChatModel, serverProtocol: ServerProtocol, close: ()
Modifier
.padding(horizontal = 2.dp)
.size(30.dp),
color = HighOrLowlight,
color = MaterialTheme.colors.secondary,
strokeWidth = 2.5.dp
)
}
@@ -207,8 +207,8 @@ private fun ProtocolServersLayout(
stringResource(R.string.smp_servers_add),
addServer,
disabled = testing,
textColor = if (testing) HighOrLowlight else MaterialTheme.colors.primary,
iconColor = if (testing) HighOrLowlight else MaterialTheme.colors.primary
textColor = if (testing) MaterialTheme.colors.secondary else MaterialTheme.colors.primary,
iconColor = if (testing) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
)
}
SectionTextFooter(
@@ -225,14 +225,14 @@ private fun ProtocolServersLayout(
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
SectionView {
SectionItemView(resetServers, disabled = serversUnchanged) {
Text(stringResource(R.string.reset_verb), color = if (!serversUnchanged) MaterialTheme.colors.onBackground else HighOrLowlight)
Text(stringResource(R.string.reset_verb), color = if (!serversUnchanged) MaterialTheme.colors.onBackground else MaterialTheme.colors.secondary)
}
val testServersDisabled = testing || allServersDisabled
SectionItemView(testServers, disabled = testServersDisabled) {
Text(stringResource(R.string.smp_servers_test_servers), color = if (!testServersDisabled) MaterialTheme.colors.onBackground else HighOrLowlight)
Text(stringResource(R.string.smp_servers_test_servers), color = if (!testServersDisabled) MaterialTheme.colors.onBackground else MaterialTheme.colors.secondary)
}
SectionItemView(saveSMPServers, disabled = saveDisabled) {
Text(stringResource(R.string.smp_servers_save), color = if (!saveDisabled) MaterialTheme.colors.onBackground else HighOrLowlight)
Text(stringResource(R.string.smp_servers_save), color = if (!saveDisabled) MaterialTheme.colors.onBackground else MaterialTheme.colors.secondary)
}
}
SectionDividerSpaced(maxBottomPadding = false)
@@ -248,15 +248,15 @@ private fun ProtocolServerView(serverProtocol: ServerProtocol, srv: ServerCfg, s
val address = parseServerAddress(srv.server)
when {
address == null || !address.valid || address.serverProtocol != serverProtocol || !uniqueAddress(srv, address, servers) -> InvalidServer()
!srv.enabled -> Icon(painterResource(R.drawable.ic_do_not_disturb_on), null, tint = HighOrLowlight)
!srv.enabled -> Icon(painterResource(R.drawable.ic_do_not_disturb_on), null, tint = MaterialTheme.colors.secondary)
else -> ShowTestStatus(srv)
}
Spacer(Modifier.padding(horizontal = 4.dp))
val text = address?.hostnames?.firstOrNull() ?: srv.server
if (srv.enabled) {
Text(text, color = if (disabled) HighOrLowlight else MaterialTheme.colors.onBackground, maxLines = 1)
Text(text, color = if (disabled) MaterialTheme.colors.secondary else MaterialTheme.colors.onBackground, maxLines = 1)
} else {
Text(text, maxLines = 1, color = HighOrLowlight)
Text(text, maxLines = 1, color = MaterialTheme.colors.secondary)
}
}
@@ -70,7 +70,8 @@ fun RTCServersView(
resetRTCServers()
isUserRTCServers = false
userRTCServersStr.value = ""
}
},
destructive = true,
)
} else {
isUserRTCServers = false
@@ -154,7 +155,7 @@ fun RTCServersLayout(
.height(160.dp)
.fillMaxWidth(),
shape = RoundedCornerShape(10.dp),
border = BorderStroke(1.dp, MaterialTheme.colors.secondary)
border = BorderStroke(1.dp, MaterialTheme.colors.secondaryVariant)
) {
SelectionContainer(
Modifier.verticalScroll(rememberScrollState())
@@ -8,7 +8,6 @@ import SectionView
import TextIconSpaced
import android.content.Context
import android.content.res.Configuration
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
@@ -97,7 +96,7 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean, FragmentActivity)
}
}
Box(
Modifier.fillMaxSize(),
Modifier.fillMaxSize().background(MaterialTheme.colors.background),
contentAlignment = Alignment.Center
) {
SimpleButton(
@@ -134,8 +133,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()
@@ -160,7 +160,7 @@ fun SettingsLayout(
SettingsActionItem(painterResource(R.drawable.ic_wifi_tethering), stringResource(R.string.network_and_servers), showSettingsModal { NetworkAndServersView(it, showModal, showSettingsModal, showCustomModal) }, disabled = stopped, extraPadding = true)
SettingsActionItem(painterResource(R.drawable.ic_videocam), stringResource(R.string.settings_audio_video_calls), showSettingsModal { CallSettingsView(it, showModal) }, disabled = stopped, extraPadding = true)
SettingsActionItem(painterResource(R.drawable.ic_lock), stringResource(R.string.privacy_and_security), showSettingsModal { PrivacySettingsView(it, showSettingsModal, setPerformLA) }, disabled = stopped, extraPadding = true)
SettingsActionItem(painterResource(R.drawable.ic_light_mode), stringResource(R.string.appearance_settings), showSettingsModal { AppearanceView(it) }, disabled = stopped, extraPadding = true)
SettingsActionItem(painterResource(R.drawable.ic_light_mode), stringResource(R.string.appearance_settings), showSettingsModal { AppearanceView(it, showSettingsModal) }, extraPadding = true)
DatabaseItem(encrypted, showSettingsModal { DatabaseView(it, showSettingsModal) }, stopped)
}
SectionDividerSpaced()
@@ -199,7 +199,7 @@ fun SettingsIncognitoActionItem(
) {
SettingsPreferenceItemWithInfo(
if (incognito.value) painterResource(R.drawable.ic_theater_comedy_filled) else painterResource(R.drawable.ic_theater_comedy),
if (incognito.value) Indigo else HighOrLowlight,
if (incognito.value) Indigo else MaterialTheme.colors.secondary,
stringResource(R.string.incognito),
stopped,
onClickInfo,
@@ -237,7 +237,7 @@ fun MaintainIncognitoState(chatModel: ChatModel) {
Icon(
painterResource(R.drawable.ic_database),
contentDescription = stringResource(R.string.database_passphrase_and_export),
tint = if (encrypted) HighOrLowlight else WarningOrange,
tint = if (encrypted) MaterialTheme.colors.secondary else WarningOrange,
)
TextIconSpaced(true)
Text(stringResource(R.string.database_passphrase_and_export))
@@ -282,10 +282,10 @@ fun ChatLockItem(
click = showSettingsModal { SimplexLockView(chatModel, currentLAMode, setPerformLA) },
icon = if (performLA.value) painterResource(R.drawable.ic_lock_filled) else painterResource(R.drawable.ic_lock),
text = stringResource(R.string.chat_lock),
iconColor = if (performLA.value) SimplexGreen else HighOrLowlight,
iconColor = if (performLA.value) SimplexGreen else MaterialTheme.colors.secondary,
extraPadding = false,
) {
Text(if (performLA.value) remember { currentLAMode.state }.value.text else generalGetString(androidx.compose.ui.R.string.off), color = HighOrLowlight)
Text(if (performLA.value) remember { currentLAMode.state }.value.text else generalGetString(androidx.compose.ui.R.string.off), color = MaterialTheme.colors.secondary)
}
}
@@ -294,7 +294,7 @@ fun ChatLockItem(
Icon(
painterResource(R.drawable.ic_keyboard),
contentDescription = "GitHub",
tint = HighOrLowlight,
tint = MaterialTheme.colors.secondary,
)
TextIconSpaced(extraPadding = true)
Text(generalGetString(R.string.contribute), color = MaterialTheme.colors.primary)
@@ -310,7 +310,7 @@ fun ChatLockItem(
Icon(
painterResource(R.drawable.ic_star),
contentDescription = "Google Play",
tint = HighOrLowlight,
tint = MaterialTheme.colors.secondary,
)
TextIconSpaced(extraPadding = true)
Text(generalGetString(R.string.rate_the_app), color = MaterialTheme.colors.primary)
@@ -322,7 +322,7 @@ fun ChatLockItem(
Icon(
painter = painterResource(id = R.drawable.ic_github),
contentDescription = "GitHub",
tint = HighOrLowlight,
tint = MaterialTheme.colors.secondary,
)
TextIconSpaced(extraPadding = true)
Text(generalGetString(R.string.star_on_github), color = MaterialTheme.colors.primary)
@@ -334,7 +334,7 @@ fun ChatLockItem(
Icon(
painter = painterResource(id = R.drawable.ic_outline_terminal),
contentDescription = stringResource(R.string.chat_console),
tint = HighOrLowlight,
tint = MaterialTheme.colors.secondary,
)
TextIconSpaced()
Text(stringResource(R.string.chat_console))
@@ -346,7 +346,7 @@ fun ChatLockItem(
Icon(
painter = painterResource(id = R.drawable.ic_github),
contentDescription = "GitHub",
tint = HighOrLowlight,
tint = MaterialTheme.colors.secondary,
)
TextIconSpaced()
Text(generalGetString(R.string.install_simplex_chat_for_terminal), color = MaterialTheme.colors.primary)
@@ -361,15 +361,15 @@ fun ChatLockItem(
Text("v${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
}
@Composable fun ProfilePreview(profileOf: NamedChat, size: Dp = 60.dp, color: Color = MaterialTheme.colors.secondary, stopped: Boolean = false) {
ProfileImage(size = size, image = profileOf.image, color = color)
@Composable fun ProfilePreview(profileOf: NamedChat, size: Dp = 60.dp, iconColor: Color = MaterialTheme.colors.secondaryVariant, textColor: Color = MaterialTheme.colors.onBackground, stopped: Boolean = false) {
ProfileImage(size = size, image = profileOf.image, color = iconColor)
Spacer(Modifier.padding(horizontal = 8.dp))
Column(Modifier.height(size), verticalArrangement = Arrangement.Center) {
Text(
profileOf.displayName,
style = MaterialTheme.typography.caption,
fontWeight = FontWeight.Bold,
color = if (stopped) HighOrLowlight else Color.Unspecified,
color = if (stopped) MaterialTheme.colors.secondary else textColor,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
@@ -377,7 +377,7 @@ fun ChatLockItem(
Text(
profileOf.fullName,
Modifier.padding(vertical = 5.dp),
color = if (stopped) HighOrLowlight else Color.Unspecified,
color = if (stopped) MaterialTheme.colors.secondary else textColor,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
@@ -386,16 +386,16 @@ fun ChatLockItem(
}
@Composable
fun SettingsActionItem(icon: Painter, text: String, click: (() -> Unit)? = null, textColor: Color = Color.Unspecified, iconColor: Color = HighOrLowlight, disabled: Boolean = false, extraPadding: Boolean = false) {
fun SettingsActionItem(icon: Painter, text: String, click: (() -> Unit)? = null, textColor: Color = Color.Unspecified, iconColor: Color = MaterialTheme.colors.secondary, disabled: Boolean = false, extraPadding: Boolean = false) {
SectionItemView(click, disabled = disabled, extraPadding = extraPadding) {
Icon(icon, text, tint = if (disabled) HighOrLowlight else iconColor)
Icon(icon, text, tint = if (disabled) MaterialTheme.colors.secondary else iconColor)
TextIconSpaced(extraPadding)
Text(text, color = if (disabled) HighOrLowlight else textColor)
Text(text, color = if (disabled) MaterialTheme.colors.secondary else textColor)
}
}
@Composable
fun SettingsActionItemWithContent(icon: Painter?, text: String? = null, click: (() -> Unit)? = null, iconColor: Color = HighOrLowlight, disabled: Boolean = false, extraPadding: Boolean = false, content: @Composable RowScope.() -> Unit) {
fun SettingsActionItemWithContent(icon: Painter?, text: String? = null, click: (() -> Unit)? = null, iconColor: Color = MaterialTheme.colors.secondary, disabled: Boolean = false, extraPadding: Boolean = false, content: @Composable RowScope.() -> Unit) {
SectionItemView(
click,
extraPadding = extraPadding,
@@ -406,11 +406,11 @@ fun SettingsActionItemWithContent(icon: Painter?, text: String? = null, click: (
disabled = disabled
) {
if (icon != null) {
Icon(icon, text, Modifier, tint = if (disabled) HighOrLowlight else iconColor)
Icon(icon, text, Modifier, tint = if (disabled) MaterialTheme.colors.secondary else iconColor)
TextIconSpaced(extraPadding)
}
if (text != null) {
Text(text, Modifier.weight(1f), color = if (disabled) HighOrLowlight else MaterialTheme.colors.onBackground)
Text(text, Modifier.weight(1f), color = if (disabled) MaterialTheme.colors.secondary else MaterialTheme.colors.onBackground)
Spacer(Modifier.width(DEFAULT_PADDING))
}
content()
@@ -422,7 +422,7 @@ fun SettingsPreferenceItem(
icon: Painter?,
text: String,
pref: SharedPreference<Boolean>,
iconColor: Color = HighOrLowlight,
iconColor: Color = MaterialTheme.colors.secondary,
enabled: Boolean = true,
onChange: ((Boolean) -> Unit)? = null,
) {
@@ -464,12 +464,12 @@ fun PreferenceToggle(
fun PreferenceToggleWithIcon(
text: String,
icon: Painter? = null,
iconColor: Color? = HighOrLowlight,
iconColor: Color? = MaterialTheme.colors.secondary,
checked: Boolean,
extraPadding: Boolean = false,
onChange: (Boolean) -> Unit = {},
) {
SettingsActionItemWithContent(icon, text, iconColor = iconColor ?: HighOrLowlight, extraPadding = extraPadding) {
SettingsActionItemWithContent(icon, text, iconColor = iconColor ?: MaterialTheme.colors.secondary, extraPadding = extraPadding) {
DefaultSwitch(
checked = checked,
onCheckedChange = {
@@ -44,7 +44,7 @@ fun UserAddressView(chatModel: ChatModel) {
}
},
deleteAddress = {
AlertManager.shared.showAlertMsg(
AlertManager.shared.showAlertDialog(
title = generalGetString(R.string.delete_address__question),
text = generalGetString(R.string.all_your_contacts_will_remain_connected),
confirmText = generalGetString(R.string.delete_verb),
@@ -53,7 +53,8 @@ fun UserAddressView(chatModel: ChatModel) {
chatModel.controller.apiDeleteUserAddress()
chatModel.userAddress.value = null
}
}
},
destructive = true,
)
}
)
@@ -115,7 +115,7 @@ fun UserProfileLayout(
) {
Box(contentAlignment = Alignment.TopEnd) {
Box(contentAlignment = Alignment.Center) {
ProfileImage(108.dp, profileImage.value, color = HighOrLowlight.copy(alpha = 0.1f))
ProfileImage(108.dp, profileImage.value, color = MaterialTheme.colors.secondary.copy(alpha = 0.1f))
EditImageButton { scope.launch { bottomSheetModalState.show() } }
}
if (profileImage.value != null) {
@@ -156,7 +156,7 @@ fun UserProfileLayout(
saveColor = MaterialTheme.colors.primary
} else {
saveModifier = Modifier
saveColor = HighOrLowlight
saveColor = MaterialTheme.colors.secondary
}
Text(
stringResource(R.string.save_and_notify_contacts),

Some files were not shown because too many files have changed in this diff Show More