From f1f853c18efa7eb82c8a01dc15bc35fd961da65e Mon Sep 17 00:00:00 2001 From: Avently <7953703+avently@users.noreply.github.com> Date: Tue, 30 Apr 2024 01:28:20 +0700 Subject: [PATCH] android, desktop: wallpapers --- .../simplex/common/platform/Files.android.kt | 1 + .../views/usersettings/Appearance.android.kt | 3 + .../chat/simplex/common/model/ChatModel.kt | 4 +- .../chat/simplex/common/model/SimpleXAPI.kt | 16 ++++ .../chat/simplex/common/platform/Files.kt | 13 +++ .../chat/simplex/common/ui/theme/Theme.kt | 7 +- .../simplex/common/ui/theme/ThemeManager.kt | 7 +- .../simplex/common/views/chat/ChatView.kt | 12 ++- .../views/helpers/ChatViewBackground.kt | 84 ++++++++++++++++++ .../simplex/common/views/helpers/Utils.kt | 46 ++++++++++ .../common/views/usersettings/Appearance.kt | 47 ++++++++++ .../commonMain/resources/MR/base/strings.xml | 7 ++ .../resources/MR/images/background_cat@4x.png | Bin 0 -> 30182 bytes .../simplex/common/platform/Files.desktop.kt | 1 + .../views/usersettings/Appearance.desktop.kt | 3 + 15 files changed, 241 insertions(+), 10 deletions(-) create mode 100644 apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ChatViewBackground.kt create mode 100644 apps/multiplatform/common/src/commonMain/resources/MR/images/background_cat@4x.png diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Files.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Files.android.kt index dfc8c1d4e7..79dceda1b3 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Files.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Files.android.kt @@ -15,6 +15,7 @@ actual val dataDir: File = androidAppContext.dataDir actual val tmpDir: File = androidAppContext.getDir("temp", Application.MODE_PRIVATE) actual val filesDir: File = File(dataDir.absolutePath + File.separator + "files") actual val appFilesDir: File = File(filesDir.absolutePath + File.separator + "app_files") +actual val appearanceDir: File = File(filesDir.absolutePath + File.separator + "appearance") actual val coreTmpDir: File = File(filesDir.absolutePath + File.separator + "temp_files") actual val dbAbsolutePrefixPath: String = dataDir.absolutePath + File.separator + "files" diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/usersettings/Appearance.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/usersettings/Appearance.android.kt index 5a60e1d1b0..256e38e009 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/usersettings/Appearance.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/usersettings/Appearance.android.kt @@ -150,6 +150,9 @@ fun AppearanceScope.AppearanceLayout( SectionDividerSpaced(maxTopPadding = true) ThemesSection(systemDarkTheme, showSettingsModal, editColor) + + SectionDividerSpaced(maxTopPadding = true) + BackgroundImageSection() SectionBottomSpacer() } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index 3838cd45a6..73a8574231 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -4,7 +4,7 @@ import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.runtime.snapshots.SnapshotStateMap -import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.* import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.font.* import androidx.compose.ui.text.style.TextDecoration @@ -137,6 +137,8 @@ object ChatModel { val processedCriticalError: ProcessedErrors = ProcessedErrors(60_000) val processedInternalError: ProcessedErrors = ProcessedErrors(20_000) + val backgroundImage by lazy { mutableStateOf(getBackgroundImageOrDefault()) } + fun getUser(userId: Long): User? = if (currentUser.value?.userId == userId) { currentUser.value } else { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt index 494f400fbc..755678c15f 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt @@ -1,8 +1,10 @@ package chat.simplex.common.model +import androidx.compose.material.MaterialTheme import chat.simplex.common.views.helpers.* import androidx.compose.runtime.* import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.painter.Painter import chat.simplex.common.model.ChatController.getNetCfg import chat.simplex.common.model.ChatController.setNetCfg @@ -11,6 +13,7 @@ import chat.simplex.common.model.ChatModel.changingActiveUserMutex import dev.icerock.moko.resources.compose.painterResource import chat.simplex.common.platform.* import chat.simplex.common.ui.theme.* +import chat.simplex.common.ui.theme.ThemeManager.toReadableHex import chat.simplex.common.views.call.* import chat.simplex.common.views.migration.MigrationFileLinkData import chat.simplex.common.views.onboarding.OnboardingStage @@ -172,6 +175,18 @@ class AppPreferences { json.decodeFromString(MapSerializer(String.serializer(), ThemeOverrides.serializer()), it) }, settingsThemes) val profileImageCornerRadius = mkFloatPreference(SHARED_PREFS_PROFILE_IMAGE_CORNER_RADIUS, 22.5f) + private val _backgroundImageType = mkStrPreference(SHARED_PREFS_BACKGROUND_IMAGE, json.encodeToString(BackgroundImageType.default)) + val backgroundImageType: SharedPreference = SharedPreference( + get = fun(): BackgroundImageType { + val value = _backgroundImageType.get() ?: return BackgroundImageType.default + return try { + json.decodeFromString(value) + } catch (e: Throwable) { + BackgroundImageType.default + } + }, + set = fun(type: BackgroundImageType) { _backgroundImageType.set(json.encodeToString(type)) } + ) val whatsNewVersion = mkStrPreference(SHARED_PREFS_WHATS_NEW_VERSION, null) val lastMigratedVersionCode = mkIntPreference(SHARED_PREFS_LAST_MIGRATED_VERSION_CODE, 0) @@ -339,6 +354,7 @@ class AppPreferences { private const val SHARED_PREFS_SYSTEM_DARK_THEME = "SystemDarkTheme" private const val SHARED_PREFS_THEMES = "Themes" private const val SHARED_PREFS_PROFILE_IMAGE_CORNER_RADIUS = "ProfileImageCornerRadius" + private const val SHARED_PREFS_BACKGROUND_IMAGE = "BackgroundImage" private const val SHARED_PREFS_WHATS_NEW_VERSION = "WhatsNewVersion" private const val SHARED_PREFS_LAST_MIGRATED_VERSION_CODE = "LastMigratedVersionCode" private const val SHARED_PREFS_CUSTOM_DISAPPEARING_MESSAGE_TIME = "CustomDisappearingMessageTime" diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Files.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Files.kt index c788a6902e..57f268f064 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Files.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Files.kt @@ -14,6 +14,7 @@ expect val dataDir: File expect val tmpDir: File expect val filesDir: File expect val appFilesDir: File +expect val appearanceDir: File expect val coreTmpDir: File expect val dbAbsolutePrefixPath: String @@ -78,6 +79,18 @@ fun getAppFilePath(fileName: String): String { } } +fun getBackgroundImageFilePath(fileName: String): String { + val rh = chatModel.currentRemoteHost.value + val s = File.separator + val path = if (rh == null) { + appearanceDir.absolutePath + s + fileName + } else { + remoteHostsDir.absolutePath + s + rh.storePath + s + "simplex_v1_appearance" + s + fileName + } + File(path).parentFile.mkdirs() + return path +} + fun getLoadedFilePath(file: CIFile?): String? { val f = file?.fileSource?.filePath return if (f != null && file.loaded) { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/Theme.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/Theme.kt index 62acc13bfe..e6709d056a 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/Theme.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/Theme.kt @@ -10,6 +10,8 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.dp import chat.simplex.common.model.ChatController import chat.simplex.common.platform.isInNightMode +import chat.simplex.common.ui.theme.ThemeManager.colorFromReadableHex +import chat.simplex.common.ui.theme.ThemeManager.toReadableHex import chat.simplex.common.views.helpers.* import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.serialization.SerialName @@ -145,11 +147,6 @@ data class ThemeColors( } } -private fun String.colorFromReadableHex(): Color = - Color(this.replace("#", "").toLongOrNull(16) ?: Color.White.toArgb().toLong()) - -private fun Color.toReadableHex(): String = "#" + Integer.toHexString(toArgb()) - @Serializable data class ThemeOverrides ( val base: DefaultTheme, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/ThemeManager.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/ThemeManager.kt index 49d3203455..4a25af8355 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/ThemeManager.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/ThemeManager.kt @@ -152,6 +152,9 @@ object ThemeManager { appPrefs.themeOverrides.set(overrides) CurrentColors.value = currentColors(!CurrentColors.value.colors.isLight) } -} -private fun Color.toReadableHex(): String = "#" + Integer.toHexString(toArgb()) + fun String.colorFromReadableHex(): Color = + Color(this.replace("#", "").toLongOrNull(16) ?: Color.White.toArgb().toLong()) + + fun Color.toReadableHex(): String = "#" + Integer.toHexString(toArgb()) +} diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt index ff5364adc2..b3d3b09ee5 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt @@ -13,17 +13,18 @@ import androidx.compose.runtime.saveable.mapSaver import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.* import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.graphics.* import androidx.compose.ui.platform.* import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.text.* import androidx.compose.ui.unit.* import chat.simplex.common.model.* +import chat.simplex.common.model.ChatController.appPrefs import chat.simplex.common.model.ChatModel.controller import chat.simplex.common.ui.theme.* import chat.simplex.common.views.call.* @@ -34,6 +35,7 @@ import chat.simplex.common.views.helpers.* import chat.simplex.common.model.GroupInfo import chat.simplex.common.platform.* import chat.simplex.common.platform.AudioPlayer +import chat.simplex.common.ui.theme.ThemeManager.toReadableHex import chat.simplex.common.views.newchat.ContactConnectionInfoView import chat.simplex.res.MR import kotlinx.coroutines.* @@ -551,6 +553,8 @@ fun ChatLayout( ) { val scope = rememberCoroutineScope() val attachmentDisabled = remember { derivedStateOf { composeState.value.attachmentDisabled } } + val backgroundImage = remember { chatModel.backgroundImage } + val backgroundImageType = remember { appPrefs.backgroundImageType.state } Box( Modifier .fillMaxWidth() @@ -596,9 +600,13 @@ fun ChatLayout( floatingActionButton = { floatingButton.value() }, contentColor = LocalContentColor.current, drawerContentColor = LocalContentColor.current, + backgroundColor = Color.Unspecified ) { contentPadding -> + val primaryColor = MaterialTheme.colors.primary BoxWithConstraints(Modifier .fillMaxHeight() + .background(MaterialTheme.colors.background) + .drawBehind { chatViewBackground(backgroundImage.value, backgroundImageType.value, primaryColor) } .padding(contentPadding) ) { ChatItemsList( diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ChatViewBackground.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ChatViewBackground.kt new file mode 100644 index 0000000000..f2f2bd573c --- /dev/null +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ChatViewBackground.kt @@ -0,0 +1,84 @@ +package chat.simplex.common.views.helpers + +import androidx.compose.material.MaterialTheme +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.* +import androidx.compose.ui.graphics.drawscope.DrawScope +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntSize +import chat.simplex.res.MR +import chat.simplex.common.ui.theme.ThemeManager.colorFromReadableHex +import chat.simplex.common.ui.theme.ThemeManager.toReadableHex +import dev.icerock.moko.resources.ImageResource +import dev.icerock.moko.resources.StringResource +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlin.math.max +import kotlin.math.roundToInt + +@Serializable +enum class PredefinedBackgroundImage(val res: ImageResource, val filename: String, val text: StringResource, val type: BackgroundImageType) { + @SerialName("cat") CAT(MR.images.background_cat, "background_cat", MR.strings.background_cat, BackgroundImageType.Repeated(false, "background_cat", 1f, null)); + + companion object { + fun from(filename: String): PredefinedBackgroundImage? = + entries.firstOrNull { it.filename == filename } + } +} + +@Serializable +enum class BackgroundImageScale(val contentScale: ContentScale) { + @SerialName("crop") CROP(ContentScale.Crop), + @SerialName("fit") FIT(ContentScale.Fit), + @SerialName("fillWidth") FILL_WIDTH(ContentScale.FillWidth), + @SerialName("fillHeight") FILL_HEIGHT(ContentScale.FillHeight), + @SerialName("fillBounds") FILL_BOUNDS((ContentScale.FillBounds)) +} + +@Serializable +sealed class BackgroundImageType { + abstract val custom: Boolean + abstract val filename: String + @Serializable @SerialName("repeated") data class Repeated(override val custom: Boolean = true, override val filename: String, val scale: Float, val tint: String?): BackgroundImageType() + @Serializable @SerialName("static") data class Static(override val custom: Boolean = true, override val filename: String, val scale: BackgroundImageScale, val tint: String?): BackgroundImageType() + + val tintColor: Color? by lazy { + when (this) { + is Repeated -> tint?.colorFromReadableHex() + is Static -> tint?.colorFromReadableHex() + } + } + + fun toPredefined(): PredefinedBackgroundImage? = + when (this) { + is Repeated -> if (!custom) PredefinedBackgroundImage.from(filename) else null + is Static -> if (!custom) PredefinedBackgroundImage.from(filename) else null + } + + companion object { + val default: BackgroundImageType = + Repeated(custom = false, PredefinedBackgroundImage.CAT.filename, 1f, null) + } +} + +fun DrawScope.chatViewBackground(image: ImageBitmap, imageType: BackgroundImageType, defaultTint: Color) { + if (imageType is BackgroundImageType.Repeated) { + val scale = imageType.scale + for (h in 0..(size.height / image.height / scale).roundToInt()) { + for (w in 0..(size.width / image.width / scale).roundToInt()) { + drawImage( + image, + dstOffset = IntOffset(x = (w * image.width * scale).roundToInt(), y = (h * image.height * scale).roundToInt()), + dstSize = IntSize((image.width * scale).roundToInt(), (image.height * scale).roundToInt()), + colorFilter = ColorFilter.tint(imageType.tintColor ?: defaultTint) + ) + } + } + } else if (imageType is BackgroundImageType.Static) { + val scale = imageType.scale.contentScale.computeScaleFactor(Size(image.width.toFloat(), image.height.toFloat()), Size(size.width, size.height)) + val scaledWidth = (image.width * scale.scaleX).roundToInt() + val scaledHeight = (image.height * scale.scaleY).roundToInt() + drawImage(image, dstOffset = IntOffset(x = (max(0f, size.width - scaledWidth) / 2).roundToInt(), y = (max(0f, size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight)) + } +} \ No newline at end of file diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Utils.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Utils.kt index 9a6e3a0f9a..c69bbf63a6 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Utils.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Utils.kt @@ -16,6 +16,7 @@ import com.charleskorn.kaml.decodeFromStream import dev.icerock.moko.resources.StringResource import kotlinx.coroutines.* import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.decodeFromStream import java.io.* import java.net.URI import java.nio.file.Files @@ -156,6 +157,34 @@ fun getThemeFromUri(uri: URI, withAlertOnException: Boolean = true): ThemeOverri return null } +fun getBackgroundImageFromUri(uri: URI, withAlertOnException: Boolean = true): Pair? { + uri.inputStream().use { + runCatching { + return uri.toFile().name to loadImageBitmap(it!!) + }.onFailure { + if (withAlertOnException) { + AlertManager.shared.showAlertMsg( + title = generalGetString(MR.strings.import_background_image_error), + text = generalGetString(MR.strings.import_background_image_desc), + ) + } + } + } + return null +} + +fun getBackgroundImageOrDefault(): ImageBitmap { + val type = appPreferences.backgroundImageType.get() + val res = if (type.custom) { + File(getBackgroundImageFilePath(type.filename)).inputStream().use { + loadImageBitmap(it) + } + } else { + PredefinedBackgroundImage.from(type.filename)?.res?.image?.toComposeImageBitmap() + } + return res ?: BackgroundImageType.default.toPredefined()!!.res.image.toComposeImageBitmap() +} + fun saveImage(uri: URI): CryptoFile? { val bitmap = getBitmapFromUri(uri) ?: return null return saveImage(bitmap) @@ -280,6 +309,23 @@ fun saveFileFromUri(uri: URI, withAlertOnException: Boolean = true): CryptoFile? } } +fun saveBackgroundImage(uri: URI): Pair? { + val res = getBackgroundImageFromUri(uri, true) ?: return null + val destFile = File(getBackgroundImageFilePath(res.first)) + val inputStream = uri.inputStream() + try { + Files.copy(inputStream!!, destFile.toPath()) + } catch (e: Exception) { + Log.e(TAG, "Error saving background image: ${e.stackTraceToString()}") + return null + } + return res +} + +fun removeBackgroundImage(fileName: String) { + File(getBackgroundImageFilePath(fileName)).delete() +} + fun createTmpFileAndDelete(onCreated: (File) -> T): T { val tmpFile = File(tmpDir, UUID.randomUUID().toString()) tmpFile.deleteOnExit() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/Appearance.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/Appearance.kt index 78e24e5e7e..2162c90f1d 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/Appearance.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/Appearance.kt @@ -19,14 +19,18 @@ import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource import androidx.compose.ui.unit.dp import chat.simplex.common.model.* +import chat.simplex.common.model.ChatController.appPrefs import chat.simplex.common.ui.theme.* import chat.simplex.common.views.helpers.* import chat.simplex.common.model.ChatModel import chat.simplex.common.platform.* +import chat.simplex.common.ui.theme.ThemeManager.toReadableHex +import chat.simplex.common.views.chat.SendReceipts import chat.simplex.res.MR import com.godaddy.android.colorpicker.* import kotlinx.serialization.encodeToString import java.net.URI +import java.nio.file.Files import java.util.* import kotlin.collections.ArrayList @@ -87,6 +91,49 @@ object AppearanceScope { SectionItemView(showSettingsModal { _ -> CustomizeThemeView(editColor) }) { Text(stringResource(MR.strings.customize_theme_title)) } } + @Composable + fun BackgroundImageSection() { + SectionView(stringResource(MR.strings.settings_section_title_background_image).uppercase()) { + val pref = remember { appPrefs.backgroundImageType.state } + val state = remember { + val type = appPrefs.backgroundImageType.get() + mutableStateOf(if (type.custom) "" else type.filename) + } + val values = remember { + PredefinedBackgroundImage.entries.map { it.filename to generalGetString(it.text) } + ("" to generalGetString(MR.strings.background_choose_own_image)) + } + val importBackgroundImageLauncher = rememberFileChooserLauncher(true) { to: URI? -> + if (to != null) { + val res = saveBackgroundImage(to) + if (res != null) { + val (filename, backgroundImage) = res + state.value = "" + chatModel.backgroundImage.value = backgroundImage + appPrefs.backgroundImageType.set(BackgroundImageType.Static(custom = true, filename, BackgroundImageScale.CROP, null)) + } + } + } + ExposedDropDownSettingRow( + stringResource(MR.strings.settings_section_title_background_image), + values, + state, + enabled = remember { mutableStateOf(true) }, + onSelected = { filename -> + if (filename.isEmpty()) { + withLongRunningApi { importBackgroundImageLauncher.launch("image/*") } + } else { + if (state.value.isEmpty()) { + removeBackgroundImage(appPrefs.backgroundImageType.get().filename) + } + state.value = filename + appPrefs.backgroundImageType.set(PredefinedBackgroundImage.from(filename)!!.type) + chatModel.backgroundImage.value = getBackgroundImageOrDefault() + } + } + ) + } + } + @Composable fun CustomizeThemeView(editColor: (ThemeColor, Color) -> Unit) { ColumnWithScrollBar( diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml index 6ca14aace2..cdc79e2759 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -1034,6 +1034,7 @@ APP ICON THEMES Profile images + Background image MESSAGES AND FILES CALLS Network connection @@ -1519,6 +1520,8 @@ Import theme Import theme error Make sure the file has correct YAML syntax. Export theme to have an example of the theme file structure. + Import background image error + Make sure the file can be read correctly. Export theme Reset colors Accent @@ -1531,6 +1534,10 @@ Sent message Received message + + Choose you own… + Cat + You allow Contact allows diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/images/background_cat@4x.png b/apps/multiplatform/common/src/commonMain/resources/MR/images/background_cat@4x.png new file mode 100644 index 0000000000000000000000000000000000000000..5643b00e0ecc990b02ac0e38ebc143ff1cf1222b GIT binary patch literal 30182 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 ESm9}%OXAdjlVwQYtC-gj}3 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 lSHXD`P;Bf_MD2Fo{|DH literal 0 HcmV?d00001 diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Files.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Files.desktop.kt index 9b2368fcd3..43fd42b396 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Files.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Files.desktop.kt @@ -13,6 +13,7 @@ actual val dataDir: File = File(desktopPlatform.dataPath) actual val tmpDir: File = File(System.getProperty("java.io.tmpdir") + File.separator + "simplex").also { it.deleteOnExit() } actual val filesDir: File = File(dataDir.absolutePath + File.separator + "simplex_v1_files") actual val appFilesDir: File = filesDir +actual val appearanceDir: File = File(dataDir.absolutePath + File.separator + "simplex_v1_appearance") actual val coreTmpDir: File = File(dataDir.absolutePath + File.separator + "tmp") actual val dbAbsolutePrefixPath: String = dataDir.absolutePath + File.separator + "simplex_v1" diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/usersettings/Appearance.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/usersettings/Appearance.desktop.kt index 4e4846bc9f..e21ffe24e7 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/usersettings/Appearance.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/usersettings/Appearance.desktop.kt @@ -67,6 +67,9 @@ fun AppearanceScope.AppearanceLayout( SectionDividerSpaced(maxTopPadding = true) ThemesSection(systemDarkTheme, showSettingsModal, editColor) + + SectionDividerSpaced(maxTopPadding = true) + BackgroundImageSection() SectionBottomSpacer() } }