Merge branch 'master' into master-ios

This commit is contained in:
spaced4ndy
2023-04-26 14:35:44 +04:00
284 changed files with 3200 additions and 1585 deletions
+24 -4
View File
@@ -48,7 +48,23 @@
## Join user groups
You can join an English-speaking users group if you want to ask any questions: [#SimpleX-Group-3](https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2Fhpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg%3D%40smp5.simplex.im%2FeDEgekVhh0zIBYvUupGWZ96kiBEMbXwK%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAS7Z3zQyOhZ9o1qzI9OTZRySpkFTagdLMa6Rc7opuNh4%253D%26srv%3Djjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22cG3W1qxZGzyrvHTugx16bg%3D%3D%22%7D)
**Please note**: The groups below are created for the users to be able to ask questions, make suggestions and ask questions about SimpleX Chat only.
You also can:
- criticize the app, and make comparisons with other messengers.
- share new messengers you think could be interesting for privacy, as long as you don't spam.
- share some privacy related publications, infrequently.
- having preliminary approved with the admin in direct message, share the link to a group you created.
You must:
- be polite to other users
- avoid spam (too frequent messages, even if they are relevant)
- avoid any personal attacks or hostility.
- avoid sharing any content that is not relevant to the above (that includes, but is not limited to, discussing politics or any aspects of society other than privacy, security, technology and communications, sharing any content that may be found offensive by other users, etc.).
Messages not following these rules will be deleted, the right to send messages may be revoked, and the access to the new members to the group may be temporarily restricted, to prevent re-joining under a different name - our imperfect group moderation does not have a better solution at the moment.
You can join an English-speaking users group if you want to ask any questions: [#SimpleX-Group-3](https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2Fp-j-D_PrY2UMDchFHEUtbSES0nmzCnvD%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEA3gBfMjB_GDEmKQwjNdqGbnX91yfuZ7nRJgQijsx5Khc%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%2262MvNZ_Ec2mmlS8V0QNtLQ%3D%3D%22%7D)
There are groups in other languages, that we have the apps interface translated into. These groups are for testing, and asking questions to other SimpleX Chat users:
@@ -181,6 +197,8 @@ You can use SimpleX with your own servers and still communicate with people usin
Recent updates:
[Apr 22, 2023. SimpleX Chat: vision and funding, v5.0 released with videos and files up to 1gb](./blog/20230422-simplex-chat-vision-funding-v5-videos-files-passcode.md).
[Mar 28, 2023. v4.6 released - with Android 8+ and ARMv7a support, hidden profiles, community moderation, improved audio/video calls and reduced battery usage](./blog/20230328-simplex-chat-v4-6-hidden-profiles.md).
[Mar 1, 2023. SimpleX File Transfer Protocol send large files efficiently, privately and securely, soon to be integrated into SimpleX Chat apps.](./blog/20230301-simplex-file-transfer-protocol.md).
@@ -295,13 +313,15 @@ If you are considering developing with SimpleX platform please get in touch for
- ✅ Improved audio & video calls.
- ✅ Support older Android OS and 32-bit CPUs.
- ✅ Hidden chat profiles.
- 🏗 Sending and receiving large files via [XFTP protocol](./blog/20230301-simplex-file-transfer-protocol.md).
- 🏗 Video messages.
- Sending and receiving large files via [XFTP protocol](./blog/20230301-simplex-file-transfer-protocol.md).
- Video messages.
- ✅ App access passcode.
- 🏗 Improved Android app UI design.
- 🏗 SMP queue redundancy and rotation (manual is supported).
- 🏗 Reduced battery and traffic usage in large groups.
- Include optional message into connection request sent via contact address.
- Ephemeral/disappearing/OTR conversations with the existing contacts.
- Access password/pin (with optional alternative access password).
- Optional alternative access password.
- Local app files encryption.
- Improved navigation and search in the conversation (expand and scroll to quoted message, scroll to search results, etc.).
- Message delivery confirmation (with sender opt-in or opt-out per contact, TBC).
+1 -1
View File
@@ -119,7 +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 "androidx.compose.material:material-icons-extended:$compose_version"
//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"
implementation "com.google.accompanist:accompanist-insets:0.23.0"
@@ -13,13 +13,12 @@ import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Lock
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.graphicsLayer
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity
@@ -426,7 +425,7 @@ fun MainPage(
) {
SimpleButton(
stringResource(R.string.auth_unlock),
icon = Icons.Outlined.Lock,
icon = painterResource(R.drawable.ic_lock),
click = {
laFailed.value = false
runAuthenticate()
@@ -2,11 +2,10 @@ package chat.simplex.app.model
import android.net.Uri
import androidx.compose.material.MaterialTheme
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.font.*
import androidx.compose.ui.text.style.TextDecoration
@@ -1517,12 +1516,12 @@ data class CIMeta (
val isRcvNew: Boolean get() = itemStatus is CIStatus.RcvNew
fun statusIcon(primaryColor: Color, metaColor: Color = HighOrLowlight): Pair<ImageVector, Color>? =
fun statusIcon(primaryColor: Color, metaColor: Color = HighOrLowlight): Pair<Int, Color>? =
when (itemStatus) {
is CIStatus.SndSent -> Icons.Filled.Check to metaColor
is CIStatus.SndErrorAuth -> Icons.Filled.Close to Color.Red
is CIStatus.SndError -> Icons.Filled.WarningAmber to WarningYellow
is CIStatus.RcvNew -> Icons.Filled.Circle to primaryColor
is CIStatus.SndSent -> R.drawable.ic_check_filled to metaColor
is CIStatus.SndErrorAuth -> R.drawable.ic_close to Color.Red
is CIStatus.SndError -> R.drawable.ic_warning_filled to WarningYellow
is CIStatus.RcvNew -> R.drawable.ic_circle_filled to primaryColor
else -> null
}
@@ -9,14 +9,13 @@ import android.provider.Settings
import android.util.Log
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.*
import chat.simplex.app.views.helpers.*
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.graphics.vector.ImageVector
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
@@ -24,7 +23,6 @@ import chat.simplex.app.*
import chat.simplex.app.R
import chat.simplex.app.ui.theme.*
import chat.simplex.app.views.call.*
import chat.simplex.app.views.helpers.*
import chat.simplex.app.views.newchat.ConnectViaLinkTab
import chat.simplex.app.views.onboarding.OnboardingStage
import chat.simplex.app.views.usersettings.*
@@ -1633,7 +1631,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
title = {
Row {
Icon(
Icons.Outlined.Bolt,
painterResource(R.drawable.ic_bolt),
contentDescription =
if (mode == NotificationsMode.SERVICE) stringResource(R.string.icon_descr_instant_notifications) else stringResource(R.string.periodic_notifications),
)
@@ -1670,7 +1668,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
title = {
Row {
Icon(
Icons.Outlined.Bolt,
painterResource(R.drawable.ic_bolt),
contentDescription =
if (mode == NotificationsMode.SERVICE) stringResource(R.string.icon_descr_instant_notifications) else stringResource(R.string.periodic_notifications),
)
@@ -1701,7 +1699,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
title = {
Row {
Icon(
Icons.Outlined.Bolt,
painterResource(R.drawable.ic_bolt),
contentDescription =
if (mode == NotificationsMode.SERVICE) stringResource(R.string.icon_descr_instant_notifications) else stringResource(R.string.periodic_notifications),
)
@@ -2628,7 +2626,8 @@ sealed class ContactUserPrefTimed {
interface Feature {
// val icon: ImageVector
val text: String
val iconFilled: ImageVector
@Composable
fun iconFilled(): Painter
val hasParam: Boolean
}
@@ -2657,21 +2656,21 @@ enum class ChatFeature: Feature {
Calls -> generalGetString(R.string.audio_video_calls)
}
val icon: ImageVector
get() = when(this) {
TimedMessages -> Icons.Outlined.Timer
FullDelete -> Icons.Outlined.DeleteForever
Voice -> Icons.Outlined.KeyboardVoice
Calls -> Icons.Outlined.Phone
val icon: Painter
@Composable get() = when(this) {
TimedMessages -> painterResource(R.drawable.ic_timer)
FullDelete -> painterResource(R.drawable.ic_delete_forever)
Voice -> painterResource(R.drawable.ic_keyboard_voice)
Calls -> painterResource(R.drawable.ic_call)
}
override val iconFilled: ImageVector
get() = when(this) {
TimedMessages -> Icons.Filled.Timer
FullDelete -> Icons.Filled.DeleteForever
Voice -> Icons.Filled.KeyboardVoice
Calls -> Icons.Filled.Phone
}
@Composable
override fun iconFilled(): Painter = when(this) {
TimedMessages -> painterResource(R.drawable.ic_timer_filled)
FullDelete -> painterResource(R.drawable.ic_delete_forever_filled)
Voice -> painterResource(R.drawable.ic_keyboard_voice_filled)
Calls -> painterResource(R.drawable.ic_call_filled)
}
fun allowDescription(allowed: FeatureAllowed): String =
when (this) {
@@ -2746,21 +2745,21 @@ enum class GroupFeature: Feature {
Voice -> generalGetString(R.string.voice_messages)
}
val icon: ImageVector
get() = when(this) {
TimedMessages -> Icons.Outlined.Timer
DirectMessages -> Icons.Outlined.SwapHorizontalCircle
FullDelete -> Icons.Outlined.DeleteForever
Voice -> Icons.Outlined.KeyboardVoice
val icon: Painter
@Composable get() = when(this) {
TimedMessages -> painterResource(R.drawable.ic_timer)
DirectMessages -> painterResource(R.drawable.ic_swap_horizontal_circle)
FullDelete -> painterResource(R.drawable.ic_delete_forever)
Voice -> painterResource(R.drawable.ic_keyboard_voice)
}
override val iconFilled: ImageVector
get() = when(this) {
TimedMessages -> Icons.Filled.Timer
DirectMessages -> Icons.Filled.SwapHorizontalCircle
FullDelete -> Icons.Filled.DeleteForever
Voice -> Icons.Filled.KeyboardVoice
}
@Composable
override fun iconFilled(): Painter = when(this) {
TimedMessages -> painterResource(R.drawable.ic_timer_filled)
DirectMessages -> painterResource(R.drawable.ic_swap_horizontal_circle_filled)
FullDelete -> painterResource(R.drawable.ic_delete_forever_filled)
Voice -> painterResource(R.drawable.ic_keyboard_voice_filled)
}
fun enableDescription(enabled: GroupFeatureEnabled, canEdit: Boolean): String =
if (canEdit) {
@@ -13,12 +13,12 @@ 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)
val ToolbarLight = Color(220, 220, 220, 12)
val ToolbarDark = Color(80, 80, 80, 12)
val SettingsBackgroundLight = Color(220, 216, 215, 90)
val SettingsSecondaryLight = Color(200, 196, 195, 90)
val GroupDark = Color(80, 80, 80, 60)
val IncomingCallLight = Color(239, 237, 236, 255)
@@ -14,7 +14,7 @@ enum class DefaultTheme {
SYSTEM, DARK, LIGHT
}
val DEFAULT_PADDING = 16.dp
val DEFAULT_PADDING = 20.dp
val DEFAULT_SPACE_AFTER_ICON = 4.dp
val DEFAULT_PADDING_HALF = DEFAULT_PADDING / 2
val DEFAULT_BOTTOM_PADDING = 48.dp
@@ -7,9 +7,6 @@ import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.material.MaterialTheme.colors
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowBackIosNew
import androidx.compose.material.icons.outlined.ArrowForwardIos
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
@@ -17,6 +14,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
@@ -58,7 +56,7 @@ fun CreateProfilePanel(chatModel: ChatModel, close: () -> Unit) {
}
})*/
Column(Modifier.padding(horizontal = DEFAULT_PADDING * 1f)) {
AppBarTitleCentered(stringResource(R.string.create_profile))
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))
@@ -89,7 +87,7 @@ fun CreateProfilePanel(chatModel: ChatModel, close: () -> Unit) {
if (chatModel.users.isEmpty()) {
SimpleButtonDecorated(
text = stringResource(R.string.about_simplex),
icon = Icons.Outlined.ArrowBackIosNew,
icon = painterResource(R.drawable.ic_arrow_back_ios_new),
textDecoration = TextDecoration.None,
fontWeight = FontWeight.Medium
) { chatModel.onboardingStage.value = OnboardingStage.Step1_SimpleXInfo }
@@ -108,7 +106,7 @@ fun CreateProfilePanel(chatModel: ChatModel, close: () -> Unit) {
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(Icons.Outlined.ArrowForwardIos, stringResource(R.string.create_profile_button), tint = createColor)
Icon(painterResource(R.drawable.ic_arrow_forward_ios), stringResource(R.string.create_profile_button), tint = createColor)
}
}
}
@@ -16,17 +16,15 @@ import androidx.activity.compose.BackHandler
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.*
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.vector.ImageVector
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.tooling.preview.Preview
@@ -263,7 +261,7 @@ private fun ActiveCallOverlayLayout(
toggleSound: () -> Unit,
flipCamera: () -> Unit
) {
Column(Modifier.padding(16.dp)) {
Column(Modifier.padding(DEFAULT_PADDING)) {
when (call.peerMedia ?: call.localMedia) {
CallMediaType.Video -> {
CallInfoView(call, alignment = Alignment.Start)
@@ -272,14 +270,14 @@ private fun ActiveCallOverlayLayout(
ToggleAudioButton(call, toggleAudio)
Spacer(Modifier.size(40.dp))
IconButton(onClick = dismiss) {
Icon(Icons.Filled.CallEnd, stringResource(R.string.icon_descr_hang_up), tint = Color.Red, modifier = Modifier.size(64.dp))
Icon(painterResource(R.drawable.ic_call_end_filled), stringResource(R.string.icon_descr_hang_up), tint = Color.Red, modifier = Modifier.size(64.dp))
}
if (call.videoEnabled) {
ControlButton(call, Icons.Filled.FlipCameraAndroid, R.string.icon_descr_flip_camera, flipCamera)
ControlButton(call, Icons.Filled.Videocam, R.string.icon_descr_video_off, toggleVideo)
ControlButton(call, painterResource(R.drawable.ic_flip_camera_android_filled), R.string.icon_descr_flip_camera, flipCamera)
ControlButton(call, painterResource(R.drawable.ic_videocam_filled), R.string.icon_descr_video_off, toggleVideo)
} else {
Spacer(Modifier.size(48.dp))
ControlButton(call, Icons.Outlined.VideocamOff, R.string.icon_descr_video_on, toggleVideo)
ControlButton(call, painterResource(R.drawable.ic_videocam_off), R.string.icon_descr_video_on, toggleVideo)
}
}
}
@@ -297,7 +295,7 @@ private fun ActiveCallOverlayLayout(
Box(Modifier.fillMaxWidth().padding(bottom = DEFAULT_BOTTOM_PADDING), contentAlignment = Alignment.CenterStart) {
Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
IconButton(onClick = dismiss) {
Icon(Icons.Filled.CallEnd, stringResource(R.string.icon_descr_hang_up), tint = Color.Red, modifier = Modifier.size(64.dp))
Icon(painterResource(R.drawable.ic_call_end_filled), stringResource(R.string.icon_descr_hang_up), tint = Color.Red, modifier = Modifier.size(64.dp))
}
}
Box(Modifier.padding(start = 32.dp)) {
@@ -315,7 +313,7 @@ private fun ActiveCallOverlayLayout(
}
@Composable
private fun ControlButton(call: Call, icon: ImageVector, @StringRes iconText: Int, action: () -> Unit, enabled: Boolean = true) {
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))
@@ -328,18 +326,18 @@ private fun ControlButton(call: Call, icon: ImageVector, @StringRes iconText: In
@Composable
private fun ToggleAudioButton(call: Call, toggleAudio: () -> Unit) {
if (call.audioEnabled) {
ControlButton(call, Icons.Outlined.Mic, R.string.icon_descr_audio_off, toggleAudio)
ControlButton(call, painterResource(R.drawable.ic_mic), R.string.icon_descr_audio_off, toggleAudio)
} else {
ControlButton(call, Icons.Outlined.MicOff, R.string.icon_descr_audio_on, toggleAudio)
ControlButton(call, painterResource(R.drawable.ic_mic_off), R.string.icon_descr_audio_on, toggleAudio)
}
}
@Composable
private fun ToggleSoundButton(call: Call, enabled: Boolean, toggleSound: () -> Unit) {
if (call.soundSpeaker) {
ControlButton(call, Icons.Outlined.VolumeUp, R.string.icon_descr_speaker_off, toggleSound, enabled)
ControlButton(call, painterResource(R.drawable.ic_volume_up), R.string.icon_descr_speaker_off, toggleSound, enabled)
} else {
ControlButton(call, Icons.Outlined.VolumeDown, R.string.icon_descr_speaker_on, toggleSound, enabled)
ControlButton(call, painterResource(R.drawable.ic_volume_down), R.string.icon_descr_speaker_on, toggleSound, enabled)
}
}
@@ -17,14 +17,12 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@@ -171,18 +169,18 @@ fun IncomingCallLockScreenAlertLayout(
Text(invitation.contact.chatViewName, style = MaterialTheme.typography.h2)
Spacer(Modifier.fillMaxHeight().weight(1f))
Row {
LockScreenCallButton(stringResource(R.string.reject), Icons.Filled.CallEnd, Color.Red, rejectCall)
LockScreenCallButton(stringResource(R.string.reject), painterResource(R.drawable.ic_call_end_filled), Color.Red, rejectCall)
Spacer(Modifier.size(48.dp))
LockScreenCallButton(stringResource(R.string.ignore), Icons.Filled.Close, MaterialTheme.colors.primary, ignoreCall)
LockScreenCallButton(stringResource(R.string.ignore), painterResource(R.drawable.ic_close), MaterialTheme.colors.primary, ignoreCall)
Spacer(Modifier.size(48.dp))
LockScreenCallButton(stringResource(R.string.accept), Icons.Filled.Check, SimplexGreen, acceptCall)
LockScreenCallButton(stringResource(R.string.accept), painterResource(R.drawable.ic_check_filled), SimplexGreen, acceptCall)
}
} else if (callOnLockScreen == CallOnLockScreen.SHOW) {
SimpleXLogo()
Text(stringResource(R.string.open_simplex_chat_to_accept_call), textAlign = TextAlign.Center, lineHeight = 22.sp)
Text(stringResource(R.string.allow_accepting_calls_from_lock_screen), textAlign = TextAlign.Center, style = MaterialTheme.typography.body2, lineHeight = 22.sp)
Spacer(Modifier.fillMaxHeight().weight(1f))
SimpleButton(text = stringResource(R.string.open_verb), icon = Icons.Filled.Check, click = openApp)
SimpleButton(text = stringResource(R.string.open_verb), icon = painterResource(R.drawable.ic_check_filled), click = openApp)
}
}
}
@@ -199,7 +197,7 @@ private fun SimpleXLogo() {
}
@Composable
private fun LockScreenCallButton(text: String, icon: ImageVector, color: Color, action: () -> Unit) {
private fun LockScreenCallButton(text: String, icon: Painter, color: Color, action: () -> Unit) {
Surface(
shape = RoundedCornerShape(10.dp),
color = Color.Transparent
@@ -213,7 +211,7 @@ private fun LockScreenCallButton(text: String, icon: ImageVector, color: Color,
IconButton(action) {
Icon(icon, text, tint = color, modifier = Modifier.scale(1.75f))
}
Spacer(Modifier.height(16.dp))
Spacer(Modifier.height(DEFAULT_PADDING))
Text(text, style = MaterialTheme.typography.body2, color = HighOrLowlight)
}
}
@@ -4,15 +4,14 @@ import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -52,7 +51,7 @@ fun IncomingCallAlertLayout(
acceptCall: () -> Unit
) {
val color = if (isInDarkTheme()) IncomingCallDark else IncomingCallLight
Column(Modifier.fillMaxWidth().background(color).padding(top = 16.dp, bottom = 16.dp, start = 16.dp, end = 8.dp)) {
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) {
@@ -60,9 +59,9 @@ fun IncomingCallAlertLayout(
ProfilePreview(profileOf = invitation.contact, size = 64.dp, color = Color.White)
}
Row(verticalAlignment = Alignment.CenterVertically) {
CallButton(stringResource(R.string.reject), Icons.Filled.CallEnd, Color.Red, rejectCall)
CallButton(stringResource(R.string.ignore), Icons.Filled.Close, MaterialTheme.colors.primary, ignoreCall)
CallButton(stringResource(R.string.accept), Icons.Filled.Check, SimplexGreen, acceptCall)
CallButton(stringResource(R.string.reject), painterResource(R.drawable.ic_call_end_filled), Color.Red, rejectCall)
CallButton(stringResource(R.string.ignore), painterResource(R.drawable.ic_close), MaterialTheme.colors.primary, ignoreCall)
CallButton(stringResource(R.string.accept), painterResource(R.drawable.ic_check_filled), SimplexGreen, acceptCall)
}
}
}
@@ -70,21 +69,21 @@ fun IncomingCallAlertLayout(
@Composable
fun IncomingCallInfo(invitation: RcvCallInvitation, chatModel: ChatModel) {
@Composable fun CallIcon(icon: ImageVector, descr: String) = Icon(icon, descr, tint = SimplexGreen)
@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)
Spacer(Modifier.width(4.dp))
}
if (invitation.callType.media == CallMediaType.Video) CallIcon(Icons.Filled.Videocam, stringResource(R.string.icon_descr_video_call))
else CallIcon(Icons.Filled.Phone, stringResource(R.string.icon_descr_audio_call))
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)
}
}
@Composable
private fun CallButton(text: String, icon: ImageVector, color: Color, action: () -> Unit) {
private fun CallButton(text: String, icon: Painter, color: Color, action: () -> Unit) {
Surface(
shape = RoundedCornerShape(10.dp),
color = Color.Transparent
@@ -2,7 +2,8 @@ package chat.simplex.app.views.chat
import InfoRow
import InfoRowEllipsis
import SectionDivider
import SectionBottomSpacer
import SectionDividerSpaced
import SectionItemView
import SectionSpacer
import SectionView
@@ -12,9 +13,6 @@ import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
@@ -22,6 +20,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.ClipboardManager
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextStyle
@@ -168,8 +167,7 @@ fun ChatInfoLayout(
Column(
Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.Start
.verticalScroll(rememberScrollState())
) {
Row(
Modifier.fillMaxWidth(),
@@ -179,28 +177,25 @@ fun ChatInfoLayout(
}
LocalAliasEditor(localAlias, updateValue = onLocalAliasChanged)
SectionSpacer()
if (customUserProfile != null) {
SectionSpacer()
SectionView(generalGetString(R.string.incognito).uppercase()) {
InfoRow(generalGetString(R.string.incognito_random_profile), customUserProfile.chatViewName)
}
SectionDividerSpaced()
}
SectionSpacer()
SectionView {
if (connectionCode != null) {
VerifyCodeButton(contact.verified, verifyClicked)
SectionDivider()
}
ContactPreferencesButton(openPreferences)
}
SectionSpacer()
SectionDividerSpaced()
SectionView(title = stringResource(R.string.conn_stats_section_title_servers)) {
SwitchAddressButton(switchContactAddress)
SectionDivider()
if (connStats != null) {
SectionItemView({
AlertManager.shared.showAlertMsg(
@@ -211,32 +206,28 @@ fun ChatInfoLayout(
}
val rcvServers = connStats.rcvServers
if (rcvServers != null && rcvServers.isNotEmpty()) {
SectionDivider()
SimplexServers(stringResource(R.string.receiving_via), rcvServers)
}
val sndServers = connStats.sndServers
if (sndServers != null && sndServers.isNotEmpty()) {
SectionDivider()
SimplexServers(stringResource(R.string.sending_via), sndServers)
}
}
}
SectionSpacer()
SectionDividerSpaced()
SectionView {
ClearChatButton(clearChat)
SectionDivider()
DeleteContactButton(deleteContact)
}
SectionSpacer()
if (developerTools) {
SectionDividerSpaced()
SectionView(title = stringResource(R.string.section_title_for_console)) {
InfoRow(stringResource(R.string.info_row_local_name), chat.chatInfo.localDisplayName)
SectionDivider()
InfoRow(stringResource(R.string.info_row_database_id), chat.chatInfo.apiId.toString())
}
SectionSpacer()
}
SectionBottomSpacer()
}
}
@@ -249,7 +240,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(Icons.Outlined.VerifiedUser, 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 = HighOrLowlight)
}
Text(
contact.profile.displayName, style = MaterialTheme.typography.h1.copy(fontWeight = FontWeight.Normal),
@@ -294,7 +285,7 @@ fun LocalAliasEditor(
)
},
leadingIcon = if (leadingIcon) {
{ Icon(Icons.Default.Edit, null, Modifier.padding(start = 7.dp)) }
{ Icon(painterResource(R.drawable.ic_edit_filled), null, Modifier.padding(start = 7.dp)) }
} else null,
color = HighOrLowlight,
focus = focus,
@@ -331,7 +322,7 @@ private fun NetworkStatusRow(networkStatus: NetworkStatus) {
) {
Text(stringResource(R.string.network_status))
Icon(
Icons.Outlined.Info,
painterResource(R.drawable.ic_info),
stringResource(R.string.network_status),
tint = MaterialTheme.colors.primary
)
@@ -355,12 +346,12 @@ private fun ServerImage(networkStatus: NetworkStatus) {
Box(Modifier.size(18.dp)) {
when (networkStatus) {
is NetworkStatus.Connected ->
Icon(Icons.Filled.Circle, stringResource(R.string.icon_descr_server_status_connected), tint = MaterialTheme.colors.primaryVariant)
Icon(painterResource(R.drawable.ic_circle_filled), stringResource(R.string.icon_descr_server_status_connected), tint = MaterialTheme.colors.primaryVariant)
is NetworkStatus.Disconnected ->
Icon(Icons.Filled.Pending, 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 = HighOrLowlight)
is NetworkStatus.Error ->
Icon(Icons.Filled.Error, stringResource(R.string.icon_descr_server_status_error), tint = HighOrLowlight)
else -> Icon(Icons.Outlined.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 = HighOrLowlight)
else -> Icon(painterResource(R.drawable.ic_circle), stringResource(R.string.icon_descr_server_status_pending), tint = HighOrLowlight)
}
}
}
@@ -385,7 +376,7 @@ fun SwitchAddressButton(onClick: () -> Unit) {
@Composable
fun VerifyCodeButton(contactVerified: Boolean, onClick: () -> Unit) {
SettingsActionItem(
if (contactVerified) Icons.Outlined.VerifiedUser else Icons.Outlined.Shield,
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,
@@ -395,7 +386,7 @@ fun VerifyCodeButton(contactVerified: Boolean, onClick: () -> Unit) {
@Composable
private fun ContactPreferencesButton(onClick: () -> Unit) {
SettingsActionItem(
Icons.Outlined.ToggleOn,
painterResource(R.drawable.ic_toggle_on),
stringResource(R.string.contact_preferences),
click = onClick
)
@@ -404,7 +395,7 @@ private fun ContactPreferencesButton(onClick: () -> Unit) {
@Composable
fun ClearChatButton(onClick: () -> Unit) {
SettingsActionItem(
Icons.Outlined.Restore,
painterResource(R.drawable.ic_settings_backup_restore),
stringResource(R.string.clear_chat_button),
click = onClick,
textColor = WarningOrange,
@@ -415,7 +406,7 @@ fun ClearChatButton(onClick: () -> Unit) {
@Composable
private fun DeleteContactButton(onClick: () -> Unit) {
SettingsActionItem(
Icons.Outlined.Delete,
painterResource(R.drawable.ic_delete),
stringResource(R.string.button_delete_contact),
click = onClick,
textColor = Color.Red,
@@ -12,9 +12,6 @@ import androidx.compose.foundation.lazy.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.mapSaver
import androidx.compose.runtime.saveable.rememberSaveable
@@ -23,6 +20,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.*
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.capitalize
import androidx.compose.ui.text.font.FontWeight
@@ -392,7 +390,7 @@ fun ChatInfoToolbar(
val barButtons = arrayListOf<@Composable RowScope.() -> Unit>()
val menuItems = arrayListOf<@Composable () -> Unit>()
menuItems.add {
ItemAction(stringResource(android.R.string.search_go).capitalize(Locale.current), Icons.Outlined.Search, onClick = {
ItemAction(stringResource(android.R.string.search_go).capitalize(Locale.current), painterResource(R.drawable.ic_search), onClick = {
showMenu.value = false
showSearch = true
})
@@ -404,11 +402,11 @@ fun ChatInfoToolbar(
showMenu.value = false
startCall(CallMediaType.Audio)
}) {
Icon(Icons.Outlined.Phone, stringResource(R.string.icon_descr_more_button), tint = MaterialTheme.colors.primary)
Icon(painterResource(R.drawable.ic_call_500), stringResource(R.string.icon_descr_more_button), tint = MaterialTheme.colors.primary)
}
}
menuItems.add {
ItemAction(stringResource(R.string.icon_descr_video_call).capitalize(Locale.current), Icons.Outlined.Videocam, onClick = {
ItemAction(stringResource(R.string.icon_descr_video_call).capitalize(Locale.current), painterResource(R.drawable.ic_videocam), onClick = {
showMenu.value = false
startCall(CallMediaType.Video)
})
@@ -419,7 +417,7 @@ fun ChatInfoToolbar(
showMenu.value = false
addMembers(chat.chatInfo.groupInfo)
}) {
Icon(Icons.Outlined.PersonAdd, stringResource(R.string.icon_descr_add_members), tint = MaterialTheme.colors.primary)
Icon(painterResource(R.drawable.ic_person_add_500), stringResource(R.string.icon_descr_add_members), tint = MaterialTheme.colors.primary)
}
}
}
@@ -427,7 +425,7 @@ fun ChatInfoToolbar(
menuItems.add {
ItemAction(
if (ntfsEnabled.value) stringResource(R.string.mute_chat) else stringResource(R.string.unmute_chat),
if (ntfsEnabled.value) Icons.Outlined.NotificationsOff else Icons.Outlined.Notifications,
if (ntfsEnabled.value) painterResource(R.drawable.ic_notifications_off) else painterResource(R.drawable.ic_notifications),
onClick = {
showMenu.value = false
// Just to make a delay before changing state of ntfsEnabled, otherwise it will redraw menu item with new value before closing the menu
@@ -441,7 +439,7 @@ fun ChatInfoToolbar(
barButtons.add {
IconButton({ showMenu.value = true }) {
Icon(Icons.Default.MoreVert, stringResource(R.string.icon_descr_more_button), tint = MaterialTheme.colors.primary)
Icon(MoreVertFilled, stringResource(R.string.icon_descr_more_button), tint = MaterialTheme.colors.primary)
}
}
@@ -498,7 +496,7 @@ fun ChatInfoToolbarTitle(cInfo: ChatInfo, imageSize: Dp = 40.dp, iconColor: Colo
@Composable
private fun ContactVerifiedShield() {
Icon(Icons.Outlined.VerifiedUser, 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 = HighOrLowlight)
}
data class CIListState(val scrolled: Boolean, val itemCount: Int, val keyboardState: KeyboardState)
@@ -787,17 +785,17 @@ fun BoxWithConstraintsScope.FloatingButtons(
val showDropDown = remember { mutableStateOf(false) }
TopEndFloatingButton(
Modifier.padding(end = 16.dp, top = 24.dp).align(Alignment.TopEnd),
Modifier.padding(end = DEFAULT_PADDING, top = 24.dp).align(Alignment.TopEnd),
topUnreadCount,
showButtonWithCounter,
onClick = { scope.launch { listState.animateScrollBy(height) } },
onLongClick = { showDropDown.value = true }
)
DefaultDropdownMenu(showDropDown, offset = DpOffset(maxWidth - 16.dp, 24.dp + fabSize)) {
DefaultDropdownMenu(showDropDown, offset = DpOffset(maxWidth - DEFAULT_PADDING, 24.dp + fabSize)) {
ItemAction(
generalGetString(R.string.mark_read),
Icons.Outlined.Check,
painterResource(R.drawable.ic_check),
onClick = {
markRead(
CC.ItemRange(minUnreadItemId, chatItems[chatItems.size - listState.layoutInfo.visibleItemsInfo.lastIndex - 1].id - 1),
@@ -901,7 +899,7 @@ private fun bottomEndFloatingButton(
modifier = Modifier.size(48.dp)
) {
Icon(
imageVector = Icons.Default.KeyboardArrowDown,
painter = painterResource(R.drawable.ic_keyboard_arrow_down),
contentDescription = null,
tint = MaterialTheme.colors.primary
)
@@ -1,12 +1,10 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.InsertDriveFile
import androidx.compose.material.icons.outlined.Close
import androidx.compose.runtime.Composable
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -25,7 +23,7 @@ fun ComposeFileView(fileName: String, cancelFile: () -> Unit, cancelEnabled: Boo
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Filled.InsertDriveFile,
painterResource(R.drawable.ic_draft_filled),
stringResource(R.string.icon_descr_file),
Modifier
.padding(start = 4.dp, end = 2.dp)
@@ -37,7 +35,7 @@ fun ComposeFileView(fileName: String, cancelFile: () -> Unit, cancelEnabled: Boo
if (cancelEnabled) {
IconButton(onClick = cancelFile, modifier = Modifier.padding(0.dp)) {
Icon(
Icons.Outlined.Close,
painterResource(R.drawable.ic_close),
contentDescription = stringResource(R.string.icon_descr_cancel_file_preview),
tint = MaterialTheme.colors.primary,
modifier = Modifier.padding(10.dp)
@@ -6,14 +6,12 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Videocam
import androidx.compose.material.icons.outlined.Close
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
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
@@ -49,7 +47,7 @@ fun ComposeImageView(media: ComposePreview.MediaPreview, cancelImages: () -> Uni
modifier = Modifier.widthIn(max = 80.dp).height(60.dp)
)
Icon(
Icons.Default.Videocam,
painterResource(R.drawable.ic_videocam_filled),
"preview video",
Modifier
.size(20.dp),
@@ -69,7 +67,7 @@ fun ComposeImageView(media: ComposePreview.MediaPreview, cancelImages: () -> Uni
if (cancelEnabled) {
IconButton(onClick = cancelImages) {
Icon(
Icons.Outlined.Close,
painterResource(R.drawable.ic_close),
contentDescription = stringResource(R.string.icon_descr_cancel_image_preview),
tint = MaterialTheme.colors.primary,
)
@@ -19,10 +19,6 @@ import androidx.activity.result.contract.ActivityResultContract
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AttachFile
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.outlined.Reply
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.rememberSaveable
@@ -30,6 +26,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
@@ -652,10 +649,10 @@ fun ComposeView(
fun contextItemView() {
when (val contextItem = composeState.value.contextItem) {
ComposeContextItem.NoContextItem -> {}
is ComposeContextItem.QuotedItem -> ContextItemView(contextItem.chatItem, Icons.Outlined.Reply) {
is ComposeContextItem.QuotedItem -> ContextItemView(contextItem.chatItem, painterResource(R.drawable.ic_reply)) {
composeState.value = composeState.value.copy(contextItem = ComposeContextItem.NoContextItem)
}
is ComposeContextItem.EditingItem -> ContextItemView(contextItem.chatItem, Icons.Filled.Edit) {
is ComposeContextItem.EditingItem -> ContextItemView(contextItem.chatItem, painterResource(R.drawable.ic_edit_filled)) {
clearState()
}
}
@@ -690,7 +687,7 @@ fun ComposeView(
) {
IconButton(showChooseAttachment, enabled = !composeState.value.attachmentDisabled && rememberUpdatedState(chat.userCanSend).value) {
Icon(
Icons.Filled.AttachFile,
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,
modifier = Modifier
@@ -2,13 +2,11 @@ import androidx.compose.animation.core.Animatable
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.Close
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -77,7 +75,7 @@ fun ComposeVoiceView(
},
enabled = finishedRecording) {
Icon(
if (audioPlaying.value) Icons.Filled.Pause else Icons.Filled.PlayArrow,
if (audioPlaying.value) painterResource(R.drawable.ic_pause_filled) else painterResource(R.drawable.ic_play_arrow_filled),
stringResource(R.string.icon_descr_file),
Modifier
.padding(start = 4.dp, end = 2.dp)
@@ -109,7 +107,7 @@ fun ComposeVoiceView(
modifier = Modifier.padding(0.dp)
) {
Icon(
Icons.Outlined.Close,
painterResource(R.drawable.ic_close),
contentDescription = stringResource(R.string.icon_descr_cancel_file_preview),
tint = MaterialTheme.colors.primary,
modifier = Modifier.padding(10.dp)
@@ -1,9 +1,9 @@
package chat.simplex.app.views.chat
import InfoRow
import SectionDivider
import SectionBottomSpacer
import SectionDividerSpaced
import SectionItemView
import SectionSpacer
import SectionTextFooter
import SectionView
import androidx.compose.foundation.*
@@ -12,7 +12,6 @@ import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
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.res.stringResource
@@ -50,7 +49,6 @@ fun ContactPreferencesView(
if (featuresAllowed == currentFeaturesAllowed) close()
else showUnsavedChangesAlert({ savePrefs(close) }, close)
},
background = if (isInDarkTheme()) MaterialTheme.colors.background else SettingsBackgroundLight
) {
ContactPreferencesLayout(
featuresAllowed,
@@ -81,9 +79,7 @@ private fun ContactPreferencesLayout(
Column(
Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState())
.padding(bottom = DEFAULT_PADDING),
horizontalAlignment = Alignment.Start,
.verticalScroll(rememberScrollState()),
) {
AppBarTitle(stringResource(R.string.contact_preferences))
val timedMessages: MutableState<Boolean> = remember(featuresAllowed) { mutableStateOf(featuresAllowed.timedMessagesAllowed) }
@@ -93,27 +89,28 @@ private fun ContactPreferencesLayout(
TimedMessagesFeatureSection(featuresAllowed, contact.mergedPreferences.timedMessages, timedMessages, onTTLUpdated) { allowed, ttl ->
applyPrefs(featuresAllowed.copy(timedMessagesAllowed = allowed, timedMessagesTTL = ttl ?: currentFeaturesAllowed.timedMessagesTTL))
}
SectionSpacer()
SectionDividerSpaced(true, maxBottomPadding = false)
val allowFullDeletion: MutableState<ContactFeatureAllowed> = remember(featuresAllowed) { mutableStateOf(featuresAllowed.fullDelete) }
FeatureSection(ChatFeature.FullDelete, user.fullPreferences.fullDelete.allow, contact.mergedPreferences.fullDelete, allowFullDeletion) {
applyPrefs(featuresAllowed.copy(fullDelete = it))
}
SectionSpacer()
SectionDividerSpaced(true, maxBottomPadding = false)
val allowVoice: MutableState<ContactFeatureAllowed> = remember(featuresAllowed) { mutableStateOf(featuresAllowed.voice) }
FeatureSection(ChatFeature.Voice, user.fullPreferences.voice.allow, contact.mergedPreferences.voice, allowVoice) {
applyPrefs(featuresAllowed.copy(voice = it))
}
SectionSpacer()
SectionDividerSpaced(true, maxBottomPadding = false)
val allowCalls: MutableState<ContactFeatureAllowed> = remember(featuresAllowed) { mutableStateOf(featuresAllowed.calls) }
FeatureSection(ChatFeature.Calls, user.fullPreferences.calls.allow, contact.mergedPreferences.calls, allowCalls) {
applyPrefs(featuresAllowed.copy(calls = it))
}
SectionSpacer()
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
ResetSaveButtons(
reset = reset,
save = savePrefs,
disabled = featuresAllowed == currentFeaturesAllowed
)
SectionBottomSpacer()
}
}
@@ -133,21 +130,18 @@ private fun FeatureSection(
SectionView(
feature.text.uppercase(),
icon = feature.iconFilled,
icon = feature.iconFilled(),
iconTint = if (enabled.forUser) SimplexGreen else if (enabled.forContact) WarningYellow else Color.Red,
leadingIcon = true,
) {
SectionItemView {
ExposedDropDownSettingRow(
generalGetString(R.string.chat_preferences_you_allow),
ContactFeatureAllowed.values(userDefault).map { it to it.text },
allowFeature,
icon = null,
enabled = remember { mutableStateOf(feature != ChatFeature.Calls) },
onSelected = onSelected
)
}
SectionDivider()
ExposedDropDownSettingRow(
generalGetString(R.string.chat_preferences_you_allow),
ContactFeatureAllowed.values(userDefault).map { it to it.text },
allowFeature,
icon = null,
enabled = remember { mutableStateOf(feature != ChatFeature.Calls) },
onSelected = onSelected
)
InfoRow(
generalGetString(R.string.chat_preferences_contact_allows),
pref.contactPreference.allow.text
@@ -172,24 +166,20 @@ private fun TimedMessagesFeatureSection(
SectionView(
ChatFeature.TimedMessages.text.uppercase(),
icon = ChatFeature.TimedMessages.iconFilled,
icon = ChatFeature.TimedMessages.iconFilled(),
iconTint = if (enabled.forUser) SimplexGreen else if (enabled.forContact) WarningYellow else Color.Red,
leadingIcon = true,
) {
SectionItemView {
PreferenceToggle(
generalGetString(R.string.chat_preferences_you_allow),
checked = allowFeature.value,
) { allow ->
onSelected(allow, if (allow) featuresAllowed.timedMessagesTTL ?: 86400 else null)
}
PreferenceToggle(
generalGetString(R.string.chat_preferences_you_allow),
checked = allowFeature.value,
) { allow ->
onSelected(allow, if (allow) featuresAllowed.timedMessagesTTL ?: 86400 else null)
}
SectionDivider()
InfoRow(
generalGetString(R.string.chat_preferences_contact_allows),
pref.contactPreference.allow.text
)
SectionDivider()
if (featuresAllowed.timedMessagesAllowed) {
val ttl = rememberSaveable(featuresAllowed.timedMessagesTTL) { mutableStateOf(featuresAllowed.timedMessagesTTL) }
TimedMessagesTTLPicker(ttl, onTTLUpdated)
@@ -206,7 +196,6 @@ private fun ResetSaveButtons(reset: () -> Unit, save: () -> Unit, disabled: Bool
SectionItemView(reset, disabled = disabled) {
Text(stringResource(R.string.reset_verb), color = if (disabled) HighOrLowlight else MaterialTheme.colors.primary)
}
SectionDivider()
SectionItemView(save, disabled = disabled) {
Text(stringResource(R.string.save_and_notify_contact), color = if (disabled) HighOrLowlight else MaterialTheme.colors.primary)
}
@@ -217,14 +206,12 @@ private fun ResetSaveButtons(reset: () -> Unit, save: () -> Unit, disabled: Bool
fun TimedMessagesTTLPicker(selection: MutableState<Int?>, onSelected: (Int?) -> Unit) {
val ttlValues = TimedMessagesPreference.ttlValues
val values = ttlValues + if (ttlValues.contains(selection.value)) listOf() else listOf(selection.value)
SectionItemView {
ExposedDropDownSettingRow(
generalGetString(R.string.delete_after),
values.map { it to TimedMessagesPreference.ttlText(it) },
selection,
onSelected = onSelected
)
}
ExposedDropDownSettingRow(
generalGetString(R.string.delete_after),
values.map { it to TimedMessagesPreference.ttlText(it) },
selection,
onSelected = onSelected
)
}
private fun showUnsavedChangesAlert(save: () -> Unit, revert: () -> Unit) {
@@ -3,13 +3,11 @@ package chat.simplex.app.views.chat
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.outlined.Close
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -23,7 +21,7 @@ import kotlinx.datetime.Clock
@Composable
fun ContextItemView(
contextItem: ChatItem,
contextIcon: ImageVector,
contextIcon: Painter,
cancelContextItem: () -> Unit
) {
val sent = contextItem.chatDir.sent
@@ -58,7 +56,7 @@ fun ContextItemView(
}
IconButton(onClick = cancelContextItem) {
Icon(
Icons.Outlined.Close,
painterResource(R.drawable.ic_close),
contentDescription = stringResource(R.string.cancel_verb),
tint = MaterialTheme.colors.primary,
modifier = Modifier.padding(10.dp)
@@ -73,8 +71,7 @@ fun PreviewContextItemView() {
SimpleXTheme {
ContextItemView(
contextItem = ChatItem.getSampleData(1, CIDirection.DirectRcv(), Clock.System.now(), "hello"),
contextIcon = Icons.Filled.Edit,
cancelContextItem = {}
)
contextIcon = painterResource(R.drawable.ic_edit_filled)
) {}
}
}
@@ -20,9 +20,6 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.*
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
@@ -30,8 +27,9 @@ import androidx.compose.ui.*
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.*
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.TextStyle
@@ -146,7 +144,7 @@ fun SendMsgView(
}
else -> {
val cs = composeState.value
val icon = if (cs.editing || cs.liveMessage != null) Icons.Filled.Check else Icons.Outlined.ArrowUpward
val icon = if (cs.editing || cs.liveMessage != null) painterResource(R.drawable.ic_check_filled) else painterResource(R.drawable.ic_arrow_upward)
val disabled = !cs.sendEnabled() ||
(!allowedVoiceByPrefs && cs.preview is ComposePreview.VoicePreview) ||
cs.endLiveDisabled
@@ -163,7 +161,7 @@ fun SendMsgView(
) {
ItemAction(
generalGetString(R.string.send_live_message),
Icons.Filled.Bolt,
BoltFilled,
onClick = {
startLiveMessage(scope, sendLiveMessage, updateLiveMessage, sendButtonSize, sendButtonAlpha, composeState, liveMessageAlertShown)
showDropdown.value = false
@@ -297,7 +295,7 @@ private fun BoxScope.DeleteTextButton(composeState: MutableState<ComposeState>)
{ composeState.value = composeState.value.copy(message = "") },
Modifier.align(Alignment.TopEnd).size(36.dp)
) {
Icon(Icons.Filled.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 = HighOrLowlight)
}
}
@@ -354,7 +352,7 @@ private fun RecordVoiceView(recState: MutableState<RecordingState>, stopRecOnNex
private fun DisallowedVoiceButton(enabled: Boolean, onClick: () -> Unit) {
IconButton(onClick, Modifier.size(36.dp), enabled = enabled) {
Icon(
Icons.Outlined.KeyboardVoice,
painterResource(R.drawable.ic_keyboard_voice),
stringResource(R.string.icon_descr_record_voice_message),
tint = HighOrLowlight,
modifier = Modifier
@@ -368,7 +366,7 @@ private fun DisallowedVoiceButton(enabled: Boolean, onClick: () -> Unit) {
private fun VoiceButtonWithoutPermission(onClick: () -> Unit) {
IconButton(onClick, Modifier.size(36.dp)) {
Icon(
Icons.Filled.KeyboardVoice,
painterResource(R.drawable.ic_keyboard_voice_filled),
stringResource(R.string.icon_descr_record_voice_message),
tint = MaterialTheme.colors.primary,
modifier = Modifier
@@ -400,7 +398,7 @@ private fun LockToCurrentOrientationUntilDispose() {
private fun StopRecordButton(onClick: () -> Unit) {
IconButton(onClick, Modifier.size(36.dp)) {
Icon(
Icons.Filled.Stop,
painterResource(R.drawable.ic_stop_filled),
stringResource(R.string.icon_descr_record_voice_message),
tint = MaterialTheme.colors.primary,
modifier = Modifier
@@ -414,7 +412,7 @@ private fun StopRecordButton(onClick: () -> Unit) {
private fun RecordVoiceButton(interactionSource: MutableInteractionSource) {
IconButton({}, Modifier.size(36.dp), interactionSource = interactionSource) {
Icon(
Icons.Filled.KeyboardVoice,
painterResource(R.drawable.ic_keyboard_voice_filled),
stringResource(R.string.icon_descr_record_voice_message),
tint = MaterialTheme.colors.primary,
modifier = Modifier
@@ -435,7 +433,7 @@ private fun CancelLiveMessageButton(
) {
IconButton(onClick, Modifier.size(36.dp)) {
Icon(
Icons.Filled.Close,
painterResource(R.drawable.ic_close),
stringResource(R.string.icon_descr_cancel_live_message),
tint = MaterialTheme.colors.primary,
modifier = Modifier
@@ -447,7 +445,7 @@ private fun CancelLiveMessageButton(
@Composable
private fun SendMsgButton(
icon: ImageVector,
icon: Painter,
sizeDp: Animatable<Float, AnimationVector1D>,
alpha: Animatable<Float, AnimationVector1D>,
enabled: Boolean,
@@ -497,7 +495,7 @@ private fun StartLiveMessageButton(enabled: Boolean, onClick: () -> Unit) {
contentAlignment = Alignment.Center
) {
Icon(
Icons.Filled.Bolt,
BoltFilled,
stringResource(R.string.icon_descr_send_message),
tint = if (enabled) MaterialTheme.colors.primary else HighOrLowlight,
modifier = Modifier
@@ -1,18 +1,17 @@
package chat.simplex.app.views.chat
import SectionBottomSpacer
import SectionView
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Share
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp
@@ -66,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(Icons.Outlined.VerifiedUser, 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 = HighOrLowlight)
Text(String.format(stringResource(R.string.is_verified), displayName))
} else {
Text(String.format(stringResource(R.string.is_not_verified), displayName))
@@ -90,7 +89,7 @@ private fun VerifyCodeLayout(
val context = LocalContext.current
Box(Modifier.weight(1f)) {
IconButton({ shareText(context, connectionCode) }, Modifier.size(20.dp).align(Alignment.CenterStart)) {
Icon(Icons.Filled.Share, null, tint = MaterialTheme.colors.primary)
Icon(painterResource(R.drawable.ic_share_filled), null, tint = MaterialTheme.colors.primary)
}
}
Spacer(Modifier.weight(1f))
@@ -106,16 +105,16 @@ private fun VerifyCodeLayout(
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
if (connectionVerified) {
SimpleButton(generalGetString(R.string.clear_verification), Icons.Outlined.Shield) {
SimpleButton(generalGetString(R.string.clear_verification), painterResource(R.drawable.ic_shield)) {
verifyCode(null) {}
}
} else {
SimpleButton(generalGetString(R.string.scan_code), Icons.Outlined.QrCode) {
SimpleButton(generalGetString(R.string.scan_code), painterResource(R.drawable.ic_qr_code)) {
ModalManager.shared.showModal {
ScanCodeView(verifyCode) { }
}
}
SimpleButton(generalGetString(R.string.mark_code_verified), Icons.Outlined.VerifiedUser) {
SimpleButton(generalGetString(R.string.mark_code_verified), painterResource(R.drawable.ic_verified_user)) {
verifyCode(connectionCode) { verified ->
if (!verified) {
AlertManager.shared.showAlertMsg(
@@ -126,6 +125,7 @@ private fun VerifyCodeLayout(
}
}
}
SectionBottomSpacer()
}
}
@@ -1,7 +1,8 @@
package chat.simplex.app.views.chat.group
import SectionBottomSpacer
import SectionCustomFooter
import SectionDivider
import SectionDividerSpaced
import SectionItemView
import SectionSpacer
import SectionView
@@ -9,15 +10,12 @@ import androidx.activity.compose.BackHandler
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.CheckCircle
import androidx.compose.material.icons.filled.TheaterComedy
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
@@ -105,7 +103,6 @@ fun AddGroupMembersLayout(
Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.Start,
) {
AppBarTitle(stringResource(R.string.button_add_members))
InfoAboutIncognito(
@@ -145,12 +142,8 @@ fun AddGroupMembersLayout(
SectionItemView(openPreferences) {
Text(stringResource(R.string.set_group_preferences))
}
SectionDivider()
}
SectionItemView {
RoleSelectionRow(groupInfo, selectedRole, allowModifyMembers)
}
SectionDivider()
RoleSelectionRow(groupInfo, selectedRole, allowModifyMembers)
if (creatingGroup && selectedContacts.isEmpty()) {
SkipInvitingButton(close)
} else {
@@ -160,13 +153,13 @@ fun AddGroupMembersLayout(
SectionCustomFooter {
InviteSectionFooter(selectedContactsCount = selectedContacts.size, allowModifyMembers, clearSelection)
}
SectionSpacer()
SectionDividerSpaced(maxTopPadding = true)
SectionView(stringResource(R.string.select_contacts)) {
ContactList(contacts = contactsToAdd, selectedContacts, groupInfo, allowModifyMembers, addContact, removeContact)
}
SectionSpacer()
}
SectionBottomSpacer()
}
}
@@ -183,16 +176,15 @@ private fun RoleSelectionRow(groupInfo: GroupInfo, selectedRole: MutableState<Gr
values,
selectedRole,
icon = null,
enabled = rememberUpdatedState(enabled),
onSelected = { selectedRole.value = it }
)
enabled = rememberUpdatedState(enabled)
) { selectedRole.value = it }
}
}
@Composable
fun InviteMembersButton(onClick: () -> Unit, disabled: Boolean) {
SettingsActionItem(
Icons.Outlined.Check,
painterResource(R.drawable.ic_check),
stringResource(R.string.invite_to_group_button),
click = onClick,
textColor = MaterialTheme.colors.primary,
@@ -204,7 +196,7 @@ fun InviteMembersButton(onClick: () -> Unit, disabled: Boolean) {
@Composable
fun SkipInvitingButton(onClick: () -> Unit) {
SettingsActionItem(
Icons.Outlined.Check,
painterResource(R.drawable.ic_check),
stringResource(R.string.skip_inviting_button),
click = onClick,
textColor = MaterialTheme.colors.primary,
@@ -260,9 +252,6 @@ fun ContactList(
checked = selectedContacts.contains(contact.apiId),
enabled = enabled,
)
if (index < contacts.lastIndex) {
SectionDivider()
}
}
}
}
@@ -277,16 +266,16 @@ fun ContactCheckRow(
enabled: Boolean,
) {
val prohibitedToInviteIncognito = !groupInfo.membership.memberIncognito && contact.contactConnIncognito
val icon: ImageVector
val icon: Painter
val iconColor: Color
if (prohibitedToInviteIncognito) {
icon = Icons.Filled.TheaterComedy
icon = painterResource(R.drawable.ic_theater_comedy_filled)
iconColor = HighOrLowlight
} else if (checked) {
icon = Icons.Filled.CheckCircle
icon = painterResource(R.drawable.ic_check_circle_filled)
iconColor = if (enabled) MaterialTheme.colors.primary else HighOrLowlight
} else {
icon = Icons.Outlined.Circle
icon = painterResource(R.drawable.ic_circle)
iconColor = HighOrLowlight
}
SectionItemView(
@@ -1,7 +1,8 @@
package chat.simplex.app.views.chat.group
import InfoRow
import SectionDivider
import SectionBottomSpacer
import SectionDividerSpaced
import SectionItemView
import SectionSpacer
import SectionTextFooter
@@ -11,12 +12,11 @@ import androidx.activity.compose.BackHandler
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
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.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
@@ -160,8 +160,7 @@ fun GroupChatInfoLayout(
Column(
Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.Start
.verticalScroll(rememberScrollState())
) {
Row(
Modifier.fillMaxWidth(),
@@ -173,63 +172,50 @@ fun GroupChatInfoLayout(
SectionView {
if (groupInfo.canEdit) {
SectionItemView(editGroupProfile) { EditGroupProfileButton() }
SectionDivider()
SectionItemView(addOrEditWelcomeMessage) { AddOrEditWelcomeMessage(groupInfo.groupProfile.description) }
SectionDivider()
EditGroupProfileButton(editGroupProfile)
AddOrEditWelcomeMessage(groupInfo.groupProfile.description, addOrEditWelcomeMessage)
}
GroupPreferencesButton(openPreferences)
}
SectionTextFooter(stringResource(R.string.only_group_owners_can_change_prefs))
SectionSpacer()
SectionDividerSpaced(maxTopPadding = true)
SectionView(title = String.format(generalGetString(R.string.group_info_section_title_num_members), members.count() + 1)) {
if (groupInfo.canAddMembers) {
SectionItemView(manageGroupLink) {
if (groupLink == null) {
CreateGroupLinkButton()
} else {
GroupLinkButton()
}
if (groupLink == null) {
CreateGroupLinkButton(manageGroupLink)
} else {
GroupLinkButton(manageGroupLink)
}
SectionDivider()
val onAddMembersClick = if (chat.chatInfo.incognito) ::cantInviteIncognitoAlert else addMembers
SectionItemView(onAddMembersClick) {
val tint = if (chat.chatInfo.incognito) HighOrLowlight else MaterialTheme.colors.primary
AddMembersButton(tint)
}
SectionDivider()
val tint = if (chat.chatInfo.incognito) HighOrLowlight else MaterialTheme.colors.primary
AddMembersButton(tint, onAddMembersClick)
}
SectionItemView(minHeight = 50.dp) {
SectionItemView(minHeight = 54.dp) {
MemberRow(groupInfo.membership, user = true)
}
if (members.isNotEmpty()) {
SectionDivider()
}
MembersList(members, showMemberInfo)
}
SectionSpacer()
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
SectionView {
ClearChatButton(clearChat)
if (groupInfo.canDelete) {
SectionDivider()
SectionItemView(deleteGroup) { DeleteGroupButton() }
DeleteGroupButton(deleteGroup)
}
if (groupInfo.membership.memberCurrent) {
SectionDivider()
SectionItemView(leaveGroup) { LeaveGroupButton() }
LeaveGroupButton(leaveGroup)
}
}
SectionSpacer()
if (developerTools) {
SectionDividerSpaced()
SectionView(title = stringResource(R.string.section_title_for_console)) {
InfoRow(stringResource(R.string.info_row_local_name), groupInfo.localDisplayName)
SectionDivider()
InfoRow(stringResource(R.string.info_row_database_id), groupInfo.apiId.toString())
}
SectionSpacer()
}
SectionBottomSpacer()
}
}
@@ -260,38 +246,31 @@ private fun GroupChatInfoHeader(cInfo: ChatInfo) {
@Composable
private fun GroupPreferencesButton(onClick: () -> Unit) {
SettingsActionItem(
Icons.Outlined.ToggleOn,
painterResource(R.drawable.ic_toggle_on),
stringResource(R.string.group_preferences),
click = onClick
)
}
@Composable
private fun AddMembersButton(tint: Color = MaterialTheme.colors.primary) {
Row(
Modifier.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Outlined.Add,
stringResource(R.string.button_add_members),
tint = tint
)
Spacer(Modifier.size(8.dp))
Text(stringResource(R.string.button_add_members), color = tint)
}
private fun AddMembersButton(tint: Color = MaterialTheme.colors.primary, onClick: () -> Unit) {
SettingsActionItem(
painterResource(R.drawable.ic_add),
stringResource(R.string.button_add_members),
onClick,
iconColor = tint,
textColor = tint
)
}
@Composable
private fun MembersList(members: List<GroupMember>, showMemberInfo: (GroupMember) -> Unit) {
Column {
members.forEachIndexed { index, member ->
SectionItemView({ showMemberInfo(member) }, minHeight = 50.dp) {
Divider()
SectionItemView({ showMemberInfo(member) }, minHeight = 54.dp) {
MemberRow(member)
}
if (index < members.lastIndex) {
SectionDivider()
}
}
}
}
@@ -309,6 +288,7 @@ private fun MemberRow(member: GroupMember, user: Boolean = false) {
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
ProfileImage(size = 46.dp, member.image)
Spacer(Modifier.width(DEFAULT_PADDING_HALF))
Column {
Row(verticalAlignment = Alignment.CenterVertically) {
if (member.verified) {
@@ -339,112 +319,74 @@ private fun MemberRow(member: GroupMember, user: Boolean = false) {
@Composable
private fun MemberVerifiedShield() {
Icon(Icons.Outlined.VerifiedUser, 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 = HighOrLowlight)
}
@Composable
private fun GroupLinkButton() {
Row(
Modifier
.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Outlined.Link,
stringResource(R.string.group_link),
tint = HighOrLowlight
)
Spacer(Modifier.size(8.dp))
Text(stringResource(R.string.group_link))
}
private fun GroupLinkButton(onClick: () -> Unit) {
SettingsActionItem(
painterResource(R.drawable.ic_link),
stringResource(R.string.group_link),
onClick,
iconColor = HighOrLowlight
)
}
@Composable
private fun CreateGroupLinkButton() {
Row(
Modifier
.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Outlined.AddLink,
stringResource(R.string.create_group_link),
tint = HighOrLowlight
)
Spacer(Modifier.size(8.dp))
Text(stringResource(R.string.create_group_link))
}
private fun CreateGroupLinkButton(onClick: () -> Unit) {
SettingsActionItem(
painterResource(R.drawable.ic_add_link),
stringResource(R.string.create_group_link),
onClick,
iconColor = HighOrLowlight
)
}
@Composable
fun EditGroupProfileButton() {
Row(
Modifier
.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Outlined.Edit,
stringResource(R.string.button_edit_group_profile),
tint = HighOrLowlight
)
Spacer(Modifier.size(8.dp))
Text(stringResource(R.string.button_edit_group_profile))
}
fun EditGroupProfileButton(onClick: () -> Unit) {
SettingsActionItem(
painterResource(R.drawable.ic_edit),
stringResource(R.string.button_edit_group_profile),
onClick,
iconColor = HighOrLowlight
)
}
@Composable
private fun AddOrEditWelcomeMessage(welcomeMessage: String?) {
private fun AddOrEditWelcomeMessage(welcomeMessage: String?, onClick: () -> Unit) {
val text = if (welcomeMessage == null) {
stringResource(R.string.button_add_welcome_message)
} else {
stringResource(R.string.button_welcome_message)
}
Row(
Modifier
.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Outlined.MapsUgc,
text,
tint = HighOrLowlight
)
Spacer(Modifier.size(8.dp))
Text(text)
}
SettingsActionItem(
painterResource(R.drawable.ic_maps_ugc),
text,
onClick,
iconColor = HighOrLowlight
)
}
@Composable
private fun LeaveGroupButton() {
Row(
Modifier.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Outlined.Logout,
stringResource(R.string.button_leave_group),
tint = Color.Red
)
Spacer(Modifier.size(8.dp))
Text(stringResource(R.string.button_leave_group), color = Color.Red)
}
private fun LeaveGroupButton(onClick: () -> Unit) {
SettingsActionItem(
painterResource(R.drawable.ic_logout),
stringResource(R.string.button_leave_group),
onClick,
iconColor = Color.Red,
textColor = Color.Red
)
}
@Composable
private fun DeleteGroupButton() {
Row(
Modifier.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Outlined.Delete,
stringResource(R.string.button_delete_group),
tint = Color.Red
)
Spacer(Modifier.size(8.dp))
Text(stringResource(R.string.button_delete_group), color = Color.Red)
}
private fun DeleteGroupButton(onClick: () -> Unit) {
SettingsActionItem(
painterResource(R.drawable.ic_delete),
stringResource(R.string.button_delete_group),
onClick,
iconColor = Color.Red,
textColor = Color.Red
)
}
@Preview
@@ -1,19 +1,18 @@
package chat.simplex.app.views.chat.group
import SectionItemView
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.icons.Icons
import androidx.compose.material.icons.outlined.*
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.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -101,15 +100,12 @@ fun GroupLinkLayout(
) {
Column(
Modifier
.verticalScroll(rememberScrollState())
.padding(start = DEFAULT_PADDING, bottom = DEFAULT_BOTTOM_PADDING, end = DEFAULT_PADDING),
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.Top
.verticalScroll(rememberScrollState()),
) {
AppBarTitle(stringResource(R.string.group_link), false)
AppBarTitle(stringResource(R.string.group_link))
Text(
stringResource(R.string.you_can_share_group_link_anybody_will_be_able_to_connect),
Modifier.padding(bottom = 12.dp),
Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = 12.dp),
lineHeight = 22.sp
)
Column(
@@ -118,11 +114,9 @@ fun GroupLinkLayout(
verticalArrangement = Arrangement.SpaceEvenly
) {
if (groupLink == null) {
SimpleButton(stringResource(R.string.button_create_group_link), icon = Icons.Outlined.AddLink, disabled = creatingLink, click = createLink)
SimpleButton(stringResource(R.string.button_create_group_link), icon = painterResource(R.drawable.ic_add_link), disabled = creatingLink, click = createLink)
} else {
SectionItemView(padding = PaddingValues(bottom = DEFAULT_PADDING)) {
RoleSelectionRow(groupInfo, groupLinkMemberRole)
}
RoleSelectionRow(groupInfo, groupLinkMemberRole)
var initialLaunch by remember { mutableStateOf(true) }
LaunchedEffect(groupLinkMemberRole.value) {
if (!initialLaunch) {
@@ -130,26 +124,27 @@ fun GroupLinkLayout(
}
initialLaunch = false
}
QRCode(groupLink, Modifier.aspectRatio(1f))
QRCode(groupLink, Modifier.aspectRatio(1f).padding(horizontal = DEFAULT_PADDING))
Row(
horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(vertical = 10.dp)
modifier = Modifier.padding(horizontal = DEFAULT_PADDING, vertical = 10.dp)
) {
SimpleButton(
stringResource(R.string.share_link),
icon = Icons.Outlined.Share,
icon = painterResource(R.drawable.ic_share),
click = share
)
SimpleButton(
stringResource(R.string.delete_link),
icon = Icons.Outlined.Delete,
icon = painterResource(R.drawable.ic_delete),
color = Color.Red,
click = deleteLink
)
}
}
}
SectionBottomSpacer()
}
}
@@ -166,9 +161,8 @@ private fun RoleSelectionRow(groupInfo: GroupInfo, selectedRole: MutableState<Gr
values,
selectedRole,
icon = null,
enabled = rememberUpdatedState(enabled),
onSelected = { selectedRole.value = it }
)
enabled = rememberUpdatedState(enabled)
) { selectedRole.value = it }
}
}
@@ -1,20 +1,19 @@
package chat.simplex.app.views.chat.group
import InfoRow
import SectionDivider
import SectionItemView
import SectionBottomSpacer
import SectionDividerSpaced
import SectionSpacer
import SectionView
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
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.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
@@ -152,7 +151,6 @@ fun GroupMemberInfoLayout(
Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.Start
) {
Row(
Modifier.fillMaxWidth(),
@@ -169,9 +167,6 @@ fun GroupMemberInfoLayout(
val chat = getContactChat(contactId)
if ((chat != null && chat.chatInfo is ChatInfo.Direct && chat.chatInfo.contact.directOrUsed) || groupInfo.fullGroupPreferences.directMessages.on) {
OpenChatButton(onClick = { openDirectChat(contactId) })
if (connectionCode != null) {
SectionDivider()
}
}
if (connectionCode != null) {
VerifyCodeButton(member.verified, verifyClicked)
@@ -183,36 +178,30 @@ fun GroupMemberInfoLayout(
SectionView(title = stringResource(R.string.member_info_section_title_member)) {
InfoRow(stringResource(R.string.info_row_group), groupInfo.displayName)
SectionDivider()
val roles = remember { member.canChangeRoleTo(groupInfo) }
if (roles != null) {
SectionItemView {
RoleSelectionRow(roles, newRole, onRoleSelected)
}
RoleSelectionRow(roles, newRole, onRoleSelected)
} else {
InfoRow(stringResource(R.string.role_in_group), member.memberRole.text)
}
val conn = member.activeConn
if (conn != null) {
SectionDivider()
val connLevelDesc =
if (conn.connLevel == 0) stringResource(R.string.conn_level_desc_direct)
else String.format(generalGetString(R.string.conn_level_desc_indirect), conn.connLevel)
InfoRow(stringResource(R.string.info_row_connection), connLevelDesc)
}
}
SectionSpacer()
if (connStats != null) {
SectionDividerSpaced()
SectionView(title = stringResource(R.string.conn_stats_section_title_servers)) {
SwitchAddressButton(switchMemberAddress)
SectionDivider()
val rcvServers = connStats.rcvServers
val sndServers = connStats.sndServers
if ((rcvServers != null && rcvServers.isNotEmpty()) || (sndServers != null && sndServers.isNotEmpty())) {
if (rcvServers != null && rcvServers.isNotEmpty()) {
SimplexServers(stringResource(R.string.receiving_via), rcvServers)
if (sndServers != null && sndServers.isNotEmpty()) {
SectionDivider()
SimplexServers(stringResource(R.string.sending_via), sndServers)
}
} else if (sndServers != null && sndServers.isNotEmpty()) {
@@ -220,24 +209,23 @@ fun GroupMemberInfoLayout(
}
}
}
SectionSpacer()
}
if (member.canBeRemoved(groupInfo)) {
SectionDividerSpaced(maxBottomPadding = false)
SectionView {
RemoveMemberButton(removeMember)
}
SectionSpacer()
}
if (developerTools) {
SectionDividerSpaced()
SectionView(title = stringResource(R.string.section_title_for_console)) {
InfoRow(stringResource(R.string.info_row_local_name), member.localDisplayName)
SectionDivider()
InfoRow(stringResource(R.string.info_row_database_id), member.groupMemberId.toString())
}
SectionSpacer()
}
SectionBottomSpacer()
}
}
@@ -250,7 +238,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(Icons.Outlined.VerifiedUser, 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 = HighOrLowlight)
}
Text(
member.displayName, style = MaterialTheme.typography.h1.copy(fontWeight = FontWeight.Normal),
@@ -273,7 +261,7 @@ fun GroupMemberInfoHeader(member: GroupMember) {
@Composable
fun RemoveMemberButton(onClick: () -> Unit) {
SettingsActionItem(
Icons.Outlined.Delete,
painterResource(R.drawable.ic_delete),
stringResource(R.string.button_remove_member),
click = onClick,
textColor = Color.Red,
@@ -284,7 +272,7 @@ fun RemoveMemberButton(onClick: () -> Unit) {
@Composable
fun OpenChatButton(onClick: () -> Unit) {
SettingsActionItem(
Icons.Outlined.Message,
painterResource(R.drawable.ic_chat),
stringResource(R.string.button_send_direct_message),
click = onClick,
textColor = MaterialTheme.colors.primary,
@@ -1,9 +1,9 @@
package chat.simplex.app.views.chat.group
import InfoRow
import SectionDivider
import SectionBottomSpacer
import SectionDividerSpaced
import SectionItemView
import SectionSpacer
import SectionTextFooter
import SectionView
import androidx.compose.foundation.*
@@ -12,7 +12,6 @@ import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import chat.simplex.app.R
@@ -45,7 +44,6 @@ fun GroupPreferencesView(m: ChatModel, chatId: String, close: () -> Unit,) {
if (preferences == currentPreferences) close()
else showUnsavedChangesAlert({ savePrefs(close) }, close)
},
background = if (isInDarkTheme()) MaterialTheme.colors.background else SettingsBackgroundLight
) {
GroupPreferencesLayout(
preferences,
@@ -73,7 +71,6 @@ private fun GroupPreferencesLayout(
) {
Column(
Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.Start,
) {
AppBarTitle(stringResource(R.string.group_preferences))
val timedMessages = remember(preferences) { mutableStateOf(preferences.timedMessages.enable) }
@@ -87,29 +84,30 @@ private fun GroupPreferencesLayout(
applyPrefs(preferences.copy(timedMessages = TimedMessagesGroupPreference(enable = enable, ttl = currentPreferences.timedMessages.ttl)))
}
}
SectionSpacer()
SectionDividerSpaced(true, maxBottomPadding = false)
val allowDirectMessages = remember(preferences) { mutableStateOf(preferences.directMessages.enable) }
FeatureSection(GroupFeature.DirectMessages, allowDirectMessages, groupInfo, preferences, onTTLUpdated) {
applyPrefs(preferences.copy(directMessages = GroupPreference(enable = it)))
}
SectionSpacer()
SectionDividerSpaced(true, maxBottomPadding = false)
val allowFullDeletion = remember(preferences) { mutableStateOf(preferences.fullDelete.enable) }
FeatureSection(GroupFeature.FullDelete, allowFullDeletion, groupInfo, preferences, onTTLUpdated) {
applyPrefs(preferences.copy(fullDelete = GroupPreference(enable = it)))
}
SectionSpacer()
SectionDividerSpaced(true, maxBottomPadding = false)
val allowVoice = remember(preferences) { mutableStateOf(preferences.voice.enable) }
FeatureSection(GroupFeature.Voice, allowVoice, groupInfo, preferences, onTTLUpdated) {
applyPrefs(preferences.copy(voice = GroupPreference(enable = it)))
}
if (groupInfo.canEdit) {
SectionSpacer()
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
ResetSaveButtons(
reset = reset,
save = savePrefs,
disabled = preferences == currentPreferences
)
}
SectionBottomSpacer()
}
}
@@ -124,22 +122,19 @@ private fun FeatureSection(
) {
SectionView {
val on = enableFeature.value == GroupFeatureEnabled.ON
val icon = if (on) feature.iconFilled else feature.icon
val icon = if (on) feature.iconFilled() else feature.icon
val iconTint = if (on) SimplexGreen else HighOrLowlight
val timedOn = feature == GroupFeature.TimedMessages && enableFeature.value == GroupFeatureEnabled.ON
if (groupInfo.canEdit) {
SectionItemView {
PreferenceToggleWithIcon(
feature.text,
icon,
iconTint,
enableFeature.value == GroupFeatureEnabled.ON,
) { checked ->
onSelected(if (checked) GroupFeatureEnabled.ON else GroupFeatureEnabled.OFF)
}
PreferenceToggleWithIcon(
feature.text,
icon,
iconTint,
enableFeature.value == GroupFeatureEnabled.ON,
) { checked ->
onSelected(if (checked) GroupFeatureEnabled.ON else GroupFeatureEnabled.OFF)
}
if (timedOn) {
SectionDivider()
val ttl = rememberSaveable(preferences.timedMessages) { mutableStateOf(preferences.timedMessages.ttl) }
TimedMessagesTTLPicker(ttl, onTTLUpdated)
}
@@ -151,7 +146,6 @@ private fun FeatureSection(
iconTint = iconTint,
)
if (timedOn) {
SectionDivider()
InfoRow(generalGetString(R.string.delete_after), TimedMessagesPreference.ttlText(preferences.timedMessages.ttl))
}
}
@@ -165,7 +159,6 @@ private fun ResetSaveButtons(reset: () -> Unit, save: () -> Unit, disabled: Bool
SectionItemView(reset, disabled = disabled) {
Text(stringResource(R.string.reset_verb), color = if (disabled) HighOrLowlight else MaterialTheme.colors.primary)
}
SectionDivider()
SectionItemView(save, disabled = disabled) {
Text(stringResource(R.string.save_and_notify_group_members), color = if (disabled) HighOrLowlight else MaterialTheme.colors.primary)
}
@@ -1,5 +1,6 @@
package chat.simplex.app.views.chat.group
import SectionBottomSpacer
import android.content.res.Configuration
import android.net.Uri
import androidx.compose.foundation.*
@@ -64,7 +65,7 @@ fun GroupProfileLayout(
val dataUnchanged =
displayName.value == groupProfile.displayName &&
fullName.value == groupProfile.fullName &&
chosenImage.value == null
groupProfile.image == profileImage.value
val closeWithAlert = {
if (dataUnchanged || !(displayName.value.isNotEmpty() && isValidDisplayName(displayName.value))) {
close()
@@ -167,7 +168,7 @@ fun GroupProfileLayout(
}
}
Spacer(Modifier.height(DEFAULT_BOTTOM_BUTTON_PADDING))
SectionBottomSpacer()
LaunchedEffect(Unit) {
delay(300)
@@ -1,5 +1,6 @@
package chat.simplex.app.views.chat.group
import SectionBottomSpacer
import SectionItemView
import SectionSpacer
import SectionView
@@ -44,7 +45,6 @@ fun GroupWelcomeView(m: ChatModel, groupInfo: GroupInfo, close: () -> Unit) {
if (welcomeText.value == groupInfo.groupProfile.description || (welcomeText.value == "" && groupInfo.groupProfile.description == null)) close()
else showUnsavedChangesAlert({ save(close) }, close)
},
background = if (isInDarkTheme()) MaterialTheme.colors.background else SettingsBackgroundLight
) {
GroupWelcomeLayout(
welcomeText,
@@ -71,6 +71,7 @@ private fun GroupWelcomeLayout(
save = save,
disabled = welcomeText.value == groupInfo.groupProfile.description || (welcomeText.value == "" && groupInfo.groupProfile.description == null)
)
SectionBottomSpacer()
}
}
@@ -3,13 +3,11 @@ 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.icons.Icons
import androidx.compose.material.icons.filled.PhoneInTalk
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
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.dp
import androidx.compose.ui.unit.sp
@@ -24,20 +22,20 @@ fun CICallItemView(cInfo: ChatInfo, cItem: ChatItem, status: CICallStatus, durat
Modifier
.padding(horizontal = 4.dp)
.padding(bottom = 8.dp), horizontalAlignment = Alignment.CenterHorizontally) {
@Composable fun ConnectingCallIcon() = Icon(Icons.Outlined.SettingsPhone, stringResource(R.string.icon_descr_call_connecting), tint = SimplexGreen)
@Composable fun ConnectingCallIcon() = Icon(painterResource(R.drawable.ic_settings_phone), stringResource(R.string.icon_descr_call_connecting), tint = SimplexGreen)
when (status) {
CICallStatus.Pending -> if (sent) {
Icon(Icons.Outlined.Call, stringResource(R.string.icon_descr_call_pending_sent))
Icon(painterResource(R.drawable.ic_call), stringResource(R.string.icon_descr_call_pending_sent))
} else {
AcceptCallButton(cInfo, acceptCall)
}
CICallStatus.Missed -> Icon(Icons.Outlined.Call, stringResource(R.string.icon_descr_call_missed), tint = Color.Red)
CICallStatus.Rejected -> Icon(Icons.Outlined.CallEnd, stringResource(R.string.icon_descr_call_rejected), tint = Color.Red)
CICallStatus.Missed -> Icon(painterResource(R.drawable.ic_call), stringResource(R.string.icon_descr_call_missed), tint = Color.Red)
CICallStatus.Rejected -> Icon(painterResource(R.drawable.ic_call_end), stringResource(R.string.icon_descr_call_rejected), tint = Color.Red)
CICallStatus.Accepted -> ConnectingCallIcon()
CICallStatus.Negotiated -> ConnectingCallIcon()
CICallStatus.Progress -> Icon(Icons.Filled.PhoneInTalk, stringResource(R.string.icon_descr_call_progress), tint = SimplexGreen)
CICallStatus.Progress -> Icon(painterResource(R.drawable.ic_phone_in_talk_filled), stringResource(R.string.icon_descr_call_progress), tint = SimplexGreen)
CICallStatus.Ended -> Row {
Icon(Icons.Outlined.CallEnd, stringResource(R.string.icon_descr_call_ended), tint = HighOrLowlight, modifier = Modifier.padding(end = 4.dp))
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)
}
CICallStatus.Error -> {}
@@ -55,9 +53,9 @@ fun CICallItemView(cInfo: ChatInfo, cItem: ChatItem, status: CICallStatus, durat
@Composable
fun AcceptCallButton(cInfo: ChatInfo, acceptCall: (Contact) -> Unit) {
if (cInfo is ChatInfo.Direct) {
SimpleButton(stringResource(R.string.answer_call), Icons.Outlined.RingVolume) { acceptCall(cInfo.contact) }
SimpleButton(stringResource(R.string.answer_call), painterResource(R.drawable.ic_ring_volume)) { acceptCall(cInfo.contact) }
} else {
Icon(Icons.Outlined.RingVolume, stringResource(R.string.answer_call), tint = HighOrLowlight)
Icon(painterResource(R.drawable.ic_ring_volume), stringResource(R.string.answer_call), tint = HighOrLowlight)
}
// if case let .direct(contact) = chatInfo {
// Button {
@@ -6,7 +6,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.app.model.*
@@ -16,14 +16,14 @@ fun CIChatFeatureView(
chatItem: ChatItem,
feature: Feature,
iconColor: Color,
icon: ImageVector? = null
icon: Painter? = null
) {
Row(
Modifier.padding(horizontal = 6.dp, vertical = 6.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
Icon(icon ?: feature.iconFilled, feature.text, Modifier.size(18.dp), tint = iconColor)
Icon(icon ?: feature.iconFilled(), feature.text, Modifier.size(18.dp), tint = iconColor)
Text(
chatEventText(chatItem),
Modifier,
@@ -6,18 +6,15 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.InsertDriveFile
import androidx.compose.material.icons.outlined.*
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.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.*
import androidx.compose.ui.unit.dp
@@ -39,14 +36,14 @@ fun CIFileView(
@Composable
fun fileIcon(
innerIcon: ImageVector? = null,
innerIcon: Painter? = null,
color: Color = if (isInDarkTheme()) FileDark else FileLight
) {
Box(
contentAlignment = Alignment.Center
) {
Icon(
Icons.Filled.InsertDriveFile,
painterResource(R.drawable.ic_draft_filled),
stringResource(R.string.icon_descr_file),
Modifier.fillMaxSize(),
tint = color
@@ -154,15 +151,15 @@ fun CIFileView(
FileProtocol.XFTP -> progressCircle(file.fileStatus.sndProgress, file.fileStatus.sndTotal)
FileProtocol.SMP -> progressIndicator()
}
is CIFileStatus.SndComplete -> fileIcon(innerIcon = Icons.Filled.Check)
is CIFileStatus.SndCancelled -> fileIcon(innerIcon = Icons.Outlined.Close)
is CIFileStatus.SndError -> fileIcon(innerIcon = Icons.Outlined.Close)
is CIFileStatus.SndComplete -> fileIcon(innerIcon = painterResource(R.drawable.ic_check_filled))
is CIFileStatus.SndCancelled -> fileIcon(innerIcon = painterResource(R.drawable.ic_close))
is CIFileStatus.SndError -> fileIcon(innerIcon = painterResource(R.drawable.ic_close))
is CIFileStatus.RcvInvitation ->
if (fileSizeValid())
fileIcon(innerIcon = Icons.Outlined.ArrowDownward, color = MaterialTheme.colors.primary)
fileIcon(innerIcon = painterResource(R.drawable.ic_arrow_downward), color = MaterialTheme.colors.primary)
else
fileIcon(innerIcon = Icons.Outlined.PriorityHigh, color = WarningOrange)
is CIFileStatus.RcvAccepted -> fileIcon(innerIcon = Icons.Outlined.MoreHoriz)
fileIcon(innerIcon = painterResource(R.drawable.ic_priority_high), color = WarningOrange)
is CIFileStatus.RcvAccepted -> fileIcon(innerIcon = painterResource(R.drawable.ic_more_horiz))
is CIFileStatus.RcvTransfer ->
if (file.fileProtocol == FileProtocol.XFTP && file.fileStatus.rcvProgress < file.fileStatus.rcvTotal) {
progressCircle(file.fileStatus.rcvProgress, file.fileStatus.rcvTotal)
@@ -170,8 +167,8 @@ fun CIFileView(
progressIndicator()
}
is CIFileStatus.RcvComplete -> fileIcon()
is CIFileStatus.RcvCancelled -> fileIcon(innerIcon = Icons.Outlined.Close)
is CIFileStatus.RcvError -> fileIcon(innerIcon = Icons.Outlined.Close)
is CIFileStatus.RcvCancelled -> fileIcon(innerIcon = painterResource(R.drawable.ic_close))
is CIFileStatus.RcvError -> fileIcon(innerIcon = painterResource(R.drawable.ic_close))
}
} else {
fileIcon()
@@ -190,9 +187,7 @@ fun CIFileView(
else
" "
if (file != null) {
Column(
horizontalAlignment = Alignment.Start
) {
Column {
Text(
file.fileName,
maxLines = 1
@@ -1,16 +1,14 @@
package chat.simplex.app.views.chat.item
import android.content.res.Configuration
import android.util.Log
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.SupervisedUserCircle
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
@@ -46,7 +44,7 @@ fun CIGroupInvitationView(
.padding(vertical = 4.dp)
.padding(end = 2.dp)
) {
ProfileImage(size = 60.dp, image = groupInvitation.groupProfile.image, icon = Icons.Filled.SupervisedUserCircle, color = iconColor)
ProfileImage(size = 60.dp, image = groupInvitation.groupProfile.image, icon = R.drawable.ic_supervised_user_circle_filled, color = iconColor)
Spacer(Modifier.padding(horizontal = 3.dp))
Column(
Modifier.defaultMinSize(minHeight = 60.dp),
@@ -89,7 +87,6 @@ fun CIGroupInvitationView(
Modifier
.defaultMinSize(minWidth = 220.dp)
.padding(bottom = 4.dp),
horizontalAlignment = Alignment.Start
) {
groupInfoView()
Column(Modifier.padding(top = 2.dp, start = 5.dp)) {
@@ -8,9 +8,6 @@ import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Icon
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -18,10 +15,10 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.platform.*
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@@ -55,7 +52,7 @@ fun CIImageView(
}
@Composable
fun fileIcon(icon: ImageVector, @StringRes stringId: Int) {
fun fileIcon(icon: Painter, @StringRes stringId: Int) {
Icon(
icon,
stringResource(stringId),
@@ -80,14 +77,14 @@ fun CIImageView(
FileProtocol.SMP -> {}
}
is CIFileStatus.SndTransfer -> progressIndicator()
is CIFileStatus.SndComplete -> fileIcon(Icons.Filled.Check, R.string.icon_descr_image_snd_complete)
is CIFileStatus.SndCancelled -> fileIcon(Icons.Outlined.Close, R.string.icon_descr_file)
is CIFileStatus.SndError -> fileIcon(Icons.Outlined.Close, R.string.icon_descr_file)
is CIFileStatus.RcvInvitation -> fileIcon(Icons.Outlined.ArrowDownward, R.string.icon_descr_asked_to_receive)
is CIFileStatus.RcvAccepted -> fileIcon(Icons.Outlined.MoreHoriz, R.string.icon_descr_waiting_for_image)
is CIFileStatus.SndComplete -> fileIcon(painterResource(R.drawable.ic_check_filled), R.string.icon_descr_image_snd_complete)
is CIFileStatus.SndCancelled -> fileIcon(painterResource(R.drawable.ic_close), R.string.icon_descr_file)
is CIFileStatus.SndError -> fileIcon(painterResource(R.drawable.ic_close), R.string.icon_descr_file)
is CIFileStatus.RcvInvitation -> fileIcon(painterResource(R.drawable.ic_arrow_downward), R.string.icon_descr_asked_to_receive)
is CIFileStatus.RcvAccepted -> fileIcon(painterResource(R.drawable.ic_more_horiz), R.string.icon_descr_waiting_for_image)
is CIFileStatus.RcvTransfer -> progressIndicator()
is CIFileStatus.RcvCancelled -> fileIcon(Icons.Outlined.Close, R.string.icon_descr_file)
is CIFileStatus.RcvError -> fileIcon(Icons.Outlined.Close, R.string.icon_descr_file)
is CIFileStatus.RcvCancelled -> fileIcon(painterResource(R.drawable.ic_close), R.string.icon_descr_file)
is CIFileStatus.RcvError -> fileIcon(painterResource(R.drawable.ic_close), R.string.icon_descr_file)
else -> {}
}
}
@@ -5,12 +5,11 @@ import SectionView
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Share
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.unit.dp
@@ -35,7 +34,7 @@ fun InvalidJSONView(json: String) {
Spacer(Modifier.height(DEFAULT_PADDING))
SectionView {
val context = LocalContext.current
SettingsActionItem(Icons.Outlined.Share, generalGetString(R.string.share_verb), click = {
SettingsActionItem(painterResource(R.drawable.ic_share), generalGetString(R.string.share_verb), click = {
shareText(context, json)
})
}
@@ -2,15 +2,13 @@ package chat.simplex.app.views.chat.item
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Circle
import androidx.compose.material.icons.outlined.Edit
import androidx.compose.material.icons.outlined.Timer
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import chat.simplex.app.R
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -39,11 +37,11 @@ fun CIMetaView(chatItem: ChatItem, timedMessagesTTL: Int?, metaColor: Color = Hi
// changing this function requires updating reserveSpaceForMeta
private fun CIMetaText(meta: CIMeta, chatTTL: Int?, color: Color) {
if (meta.itemEdited) {
StatusIconText(Icons.Outlined.Edit, color)
StatusIconText(painterResource(R.drawable.ic_edit), color)
Spacer(Modifier.width(3.dp))
}
if (meta.disappearing) {
StatusIconText(Icons.Outlined.Timer, color)
StatusIconText(painterResource(R.drawable.ic_timer), color)
val ttl = meta.itemTimed?.ttl
if (ttl != chatTTL) {
Text(TimedMessagesPreference.shortTtlText(ttl), color = color, fontSize = 13.sp)
@@ -53,10 +51,10 @@ private fun CIMetaText(meta: CIMeta, chatTTL: Int?, color: Color) {
val statusIcon = meta.statusIcon(MaterialTheme.colors.primary, color)
if (statusIcon != null) {
val (icon, statusColor) = statusIcon
StatusIconText(icon, statusColor)
StatusIconText(painterResource(icon), statusColor)
Spacer(Modifier.width(4.dp))
} else if (!meta.disappearing) {
StatusIconText(Icons.Filled.Circle, Color.Transparent)
StatusIconText(painterResource(R.drawable.ic_circle_filled), Color.Transparent)
Spacer(Modifier.width(4.dp))
}
Text(meta.timestampText, color = color, fontSize = 13.sp, maxLines = 1, overflow = TextOverflow.Ellipsis)
@@ -81,7 +79,7 @@ fun reserveSpaceForMeta(meta: CIMeta, chatTTL: Int?): String {
}
@Composable
private fun StatusIconText(icon: ImageVector, color: Color) {
private fun StatusIconText(icon: Painter, color: Color) {
Icon(icon, null, Modifier.height(12.dp), tint = color)
}
@@ -9,17 +9,15 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.layout.*
import androidx.compose.ui.platform.*
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.*
import androidx.compose.ui.viewinterop.AndroidView
@@ -163,9 +161,8 @@ private fun BoxScope.PlayButton(error: Boolean = false, onLongClick: () -> Unit,
contentAlignment = Alignment.Center
) {
Icon(
imageVector = Icons.Filled.PlayArrow,
painterResource(R.drawable.ic_play_arrow_filled),
contentDescription = null,
Modifier.size(25.dp),
tint = if (error) WarningOrange else Color.White
)
}
@@ -192,7 +189,7 @@ private fun DurationProgress(file: CIFile, playing: MutableState<Boolean>, durat
color = Color.White
)
/*if (!soundEnabled.value) {
Icon(Icons.Outlined.VolumeOff, null,
Icon(painterResource(R.drawable.ic_volume_off_filled), null,
Modifier.padding(start = 5.dp).size(10.dp),
tint = Color.White
)
@@ -255,7 +252,7 @@ private fun progressIndicator() {
}
@Composable
private fun fileIcon(icon: ImageVector, @StringRes stringId: Int) {
private fun fileIcon(icon: Painter, @StringRes stringId: Int) {
Icon(
icon,
stringResource(stringId),
@@ -298,19 +295,19 @@ private fun loadingIndicator(file: CIFile?) {
FileProtocol.XFTP -> progressCircle(file.fileStatus.sndProgress, file.fileStatus.sndTotal)
FileProtocol.SMP -> progressIndicator()
}
is CIFileStatus.SndComplete -> fileIcon(Icons.Filled.Check, R.string.icon_descr_video_snd_complete)
is CIFileStatus.SndCancelled -> fileIcon(Icons.Outlined.Close, R.string.icon_descr_file)
is CIFileStatus.SndError -> fileIcon(Icons.Outlined.Close, R.string.icon_descr_file)
is CIFileStatus.RcvInvitation -> fileIcon(Icons.Outlined.ArrowDownward, R.string.icon_descr_video_asked_to_receive)
is CIFileStatus.RcvAccepted -> fileIcon(Icons.Outlined.MoreHoriz, R.string.icon_descr_waiting_for_video)
is CIFileStatus.SndComplete -> fileIcon(painterResource(R.drawable.ic_check_filled), R.string.icon_descr_video_snd_complete)
is CIFileStatus.SndCancelled -> fileIcon(painterResource(R.drawable.ic_close), R.string.icon_descr_file)
is CIFileStatus.SndError -> fileIcon(painterResource(R.drawable.ic_close), R.string.icon_descr_file)
is CIFileStatus.RcvInvitation -> fileIcon(painterResource(R.drawable.ic_arrow_downward), R.string.icon_descr_video_asked_to_receive)
is CIFileStatus.RcvAccepted -> fileIcon(painterResource(R.drawable.ic_more_horiz), R.string.icon_descr_waiting_for_video)
is CIFileStatus.RcvTransfer ->
if (file.fileProtocol == FileProtocol.XFTP && file.fileStatus.rcvProgress < file.fileStatus.rcvTotal) {
progressCircle(file.fileStatus.rcvProgress, file.fileStatus.rcvTotal)
} else {
progressIndicator()
}
is CIFileStatus.RcvCancelled -> fileIcon(Icons.Outlined.Close, R.string.icon_descr_file)
is CIFileStatus.RcvError -> fileIcon(Icons.Outlined.Close, R.string.icon_descr_file)
is CIFileStatus.RcvCancelled -> fileIcon(painterResource(R.drawable.ic_close), R.string.icon_descr_file)
is CIFileStatus.RcvError -> fileIcon(painterResource(R.drawable.ic_close), R.string.icon_descr_file)
else -> {}
}
}
@@ -5,8 +5,6 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
@@ -19,7 +17,9 @@ import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.*
import chat.simplex.app.R
import chat.simplex.app.model.*
import chat.simplex.app.ui.theme.*
import chat.simplex.app.views.helpers.*
@@ -171,7 +171,7 @@ private fun PlayPauseButton(
contentAlignment = Alignment.Center
) {
Icon(
imageVector = if (audioPlaying) Icons.Filled.Pause else Icons.Filled.PlayArrow,
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
@@ -200,7 +200,7 @@ private fun VoiceMsgIndicator(
if (hasText) {
IconButton({ if (!audioPlaying) play() else pause() }, Modifier.size(56.dp).drawRingModifier(angle, strokeColor, strokeWidth)) {
Icon(
imageVector = if (audioPlaying) Icons.Filled.Pause else Icons.Filled.PlayArrow,
if (audioPlaying) painterResource(R.drawable.ic_pause_filled) else painterResource(R.drawable.ic_play_arrow_filled),
contentDescription = null,
Modifier.size(36.dp),
tint = MaterialTheme.colors.primary
@@ -6,16 +6,15 @@ import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.outlined.*
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.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.*
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -106,7 +105,7 @@ fun ChatItemView(
fun MsgContentItemDropdownMenu() {
DefaultDropdownMenu(showMenu) {
if (cItem.meta.itemDeleted == null && !live) {
ItemAction(stringResource(R.string.reply_verb), Icons.Outlined.Reply, onClick = {
ItemAction(stringResource(R.string.reply_verb), painterResource(R.drawable.ic_reply), onClick = {
if (composeState.value.editing) {
composeState.value = ComposeState(contextItem = ComposeContextItem.QuotedItem(cItem), useLinkPreviews = useLinkPreviews)
} else {
@@ -115,7 +114,7 @@ fun ChatItemView(
showMenu.value = false
})
}
ItemAction(stringResource(R.string.share_verb), Icons.Outlined.Share, onClick = {
ItemAction(stringResource(R.string.share_verb), painterResource(R.drawable.ic_share), onClick = {
val filePath = getLoadedFilePath(SimplexApp.context, cItem.file)
when {
filePath != null -> shareFile(context, cItem.text, filePath)
@@ -123,7 +122,7 @@ fun ChatItemView(
}
showMenu.value = false
})
ItemAction(stringResource(R.string.copy_verb), Icons.Outlined.ContentCopy, onClick = {
ItemAction(stringResource(R.string.copy_verb), painterResource(R.drawable.ic_content_copy), onClick = {
copyText(context, cItem.content.text)
showMenu.value = false
})
@@ -131,7 +130,7 @@ fun ChatItemView(
val filePath = getLoadedFilePath(context, cItem.file)
if (filePath != null) {
val writePermissionState = rememberPermissionState(permission = Manifest.permission.WRITE_EXTERNAL_STORAGE)
ItemAction(stringResource(R.string.save_verb), Icons.Outlined.SaveAlt, onClick = {
ItemAction(stringResource(R.string.save_verb), painterResource(R.drawable.ic_download), onClick = {
when (cItem.content.msgContent) {
is MsgContent.MCImage -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R || writePermissionState.hasPermission) {
@@ -148,7 +147,7 @@ fun ChatItemView(
}
}
if (cItem.meta.editable && cItem.content.msgContent !is MsgContent.MCVoice && !live) {
ItemAction(stringResource(R.string.edit_verb), Icons.Filled.Edit, onClick = {
ItemAction(stringResource(R.string.edit_verb), painterResource(R.drawable.ic_edit_filled), onClick = {
composeState.value = ComposeState(editingItem = cItem, useLinkPreviews = useLinkPreviews)
showMenu.value = false
})
@@ -156,7 +155,7 @@ fun ChatItemView(
if (cItem.meta.itemDeleted != null && revealed.value) {
ItemAction(
stringResource(R.string.hide_verb),
Icons.Outlined.VisibilityOff,
painterResource(R.drawable.ic_visibility_off),
onClick = {
revealed.value = false
showMenu.value = false
@@ -182,7 +181,7 @@ fun ChatItemView(
if (!cItem.isDeletedContent) {
ItemAction(
stringResource(R.string.reveal_verb),
Icons.Outlined.Visibility,
painterResource(R.drawable.ic_visibility),
onClick = {
revealed.value = true
showMenu.value = false
@@ -270,7 +269,7 @@ fun CancelFileItemAction(
) {
ItemAction(
stringResource(cancelAction.uiActionId),
Icons.Outlined.Close,
painterResource(R.drawable.ic_close),
onClick = {
showMenu.value = false
cancelFileAlertDialog(fileId, cancelFile = cancelFile, cancelAction = cancelAction)
@@ -288,7 +287,7 @@ fun DeleteItemAction(
) {
ItemAction(
stringResource(R.string.delete_verb),
Icons.Outlined.Delete,
painterResource(R.drawable.ic_delete),
onClick = {
showMenu.value = false
deleteMessageAlertDialog(cItem, questionText, deleteMessage = deleteMessage)
@@ -306,7 +305,7 @@ fun ModerateItemAction(
) {
ItemAction(
stringResource(R.string.moderate_verb),
Icons.Outlined.Flag,
painterResource(R.drawable.ic_flag),
onClick = {
showMenu.value = false
moderateMessageAlertDialog(cItem, questionText, deleteMessage = deleteMessage)
@@ -315,6 +314,26 @@ fun ModerateItemAction(
)
}
@Composable
fun ItemAction(text: String, icon: Painter, onClick: () -> Unit, color: Color = Color.Unspecified) {
val finalColor = if (color == Color.Unspecified) {
if (isInDarkTheme()) MenuTextColorDark else Color.Black
} else color
DropdownMenuItem(onClick, contentPadding = PaddingValues(horizontal = DEFAULT_PADDING * 1.5f)) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text,
modifier = Modifier
.fillMaxWidth()
.weight(1F)
.padding(end = 15.dp),
color = finalColor
)
Icon(icon, text, tint = finalColor)
}
}
}
@Composable
fun ItemAction(text: String, icon: ImageVector, onClick: () -> Unit, color: Color = Color.Unspecified) {
val finalColor = if (color == Color.Unspecified) {
@@ -4,10 +4,6 @@ import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.Flag
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -15,9 +11,10 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.layout.*
import androidx.compose.ui.platform.UriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.*
import androidx.compose.ui.text.font.FontStyle
@@ -72,7 +69,7 @@ fun FramedItemView(
}
@Composable
fun FramedItemHeader(caption: String, italic: Boolean, icon: ImageVector? = null) {
fun FramedItemHeader(caption: String, italic: Boolean, icon: Painter? = null) {
Row(
Modifier
.background(if (sent) SentQuoteColorLight else ReceivedQuoteColorLight)
@@ -142,7 +139,7 @@ fun FramedItemView(
ciQuotedMsgView(qi)
}
Icon(
if (qi.content is MsgContent.MCFile) Icons.Filled.InsertDriveFile else Icons.Filled.Mic,
if (qi.content is MsgContent.MCFile) painterResource(R.drawable.ic_draft_filled) else painterResource(R.drawable.ic_mic_filled),
if (qi.content is MsgContent.MCFile) stringResource(R.string.icon_descr_file) else stringResource(R.string.voice_message),
Modifier
.padding(top = 6.dp, end = 4.dp)
@@ -181,9 +178,9 @@ fun FramedItemView(
PriorityLayout(Modifier, CHAT_IMAGE_LAYOUT_ID) {
if (ci.meta.itemDeleted != null) {
if (ci.meta.itemDeleted is CIDeleted.Moderated) {
FramedItemHeader(String.format(stringResource(R.string.moderated_item_description), ci.meta.itemDeleted.byGroupMember.chatViewName), true, Icons.Outlined.Flag)
FramedItemHeader(String.format(stringResource(R.string.moderated_item_description), ci.meta.itemDeleted.byGroupMember.chatViewName), true, painterResource(R.drawable.ic_flag))
} else {
FramedItemHeader(stringResource(R.string.marked_deleted_description), true, Icons.Outlined.Delete)
FramedItemHeader(stringResource(R.string.marked_deleted_description), true, painterResource(R.drawable.ic_delete))
}
} else if (ci.meta.isLive) {
FramedItemHeader(stringResource(R.string.live), false)
@@ -4,8 +4,7 @@ import android.content.res.Configuration
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.PersonAdd
import androidx.compose.ui.res.painterResource
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -28,7 +27,6 @@ val bold = SpanStyle(fontWeight = FontWeight.Bold)
@Composable
fun ChatHelpView(addContact: (() -> Unit)? = null) {
Column(
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
val uriHandler = LocalUriHandler.current
@@ -44,7 +42,6 @@ fun ChatHelpView(addContact: (() -> Unit)? = null) {
Column(
Modifier.padding(top = 24.dp),
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
Text(
@@ -58,7 +55,7 @@ fun ChatHelpView(addContact: (() -> Unit)? = null) {
) {
Text(stringResource(R.string.chat_help_tap_button))
Icon(
Icons.Outlined.PersonAdd,
painterResource(R.drawable.ic_person_add),
stringResource(R.string.add_contact),
modifier = if (addContact != null) Modifier.clickable(onClick = addContact) else Modifier,
)
@@ -70,7 +67,6 @@ fun ChatHelpView(addContact: (() -> Unit)? = null) {
Column(
Modifier.padding(top = 24.dp),
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
Text(stringResource(R.string.to_connect_via_link_title), style = MaterialTheme.typography.h2)
@@ -81,7 +77,6 @@ fun ChatHelpView(addContact: (() -> Unit)? = null) {
Column(
Modifier.padding(vertical = 24.dp),
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
Text(stringResource(R.string.markdown_in_messages), style = MaterialTheme.typography.h2)
@@ -4,14 +4,12 @@ import android.content.res.Configuration
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
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
@@ -183,7 +181,7 @@ fun GroupMenuItems(chat: Chat, groupInfo: GroupInfo, chatModel: ChatModel, showM
fun MarkReadChatAction(chat: Chat, chatModel: ChatModel, showMenu: MutableState<Boolean>) {
ItemAction(
stringResource(R.string.mark_read),
Icons.Outlined.Check,
painterResource(R.drawable.ic_check),
onClick = {
markChatRead(chat, chatModel)
chatModel.controller.ntfManager.cancelNotificationsForChat(chat.id)
@@ -196,7 +194,7 @@ fun MarkReadChatAction(chat: Chat, chatModel: ChatModel, showMenu: MutableState<
fun MarkUnreadChatAction(chat: Chat, chatModel: ChatModel, showMenu: MutableState<Boolean>) {
ItemAction(
stringResource(R.string.mark_unread),
Icons.Outlined.MarkChatUnread,
painterResource(R.drawable.ic_mark_chat_unread),
onClick = {
markChatUnread(chat, chatModel)
showMenu.value = false
@@ -208,7 +206,7 @@ fun MarkUnreadChatAction(chat: Chat, chatModel: ChatModel, showMenu: MutableStat
fun ToggleNotificationsChatAction(chat: Chat, chatModel: ChatModel, ntfsEnabled: Boolean, showMenu: MutableState<Boolean>) {
ItemAction(
if (ntfsEnabled) stringResource(R.string.mute_chat) else stringResource(R.string.unmute_chat),
if (ntfsEnabled) Icons.Outlined.NotificationsOff else Icons.Outlined.Notifications,
if (ntfsEnabled) painterResource(R.drawable.ic_notifications_off) else painterResource(R.drawable.ic_notifications),
onClick = {
changeNtfsStatePerChat(!ntfsEnabled, mutableStateOf(ntfsEnabled), chat, chatModel)
showMenu.value = false
@@ -220,7 +218,7 @@ fun ToggleNotificationsChatAction(chat: Chat, chatModel: ChatModel, ntfsEnabled:
fun ClearChatAction(chat: Chat, chatModel: ChatModel, showMenu: MutableState<Boolean>) {
ItemAction(
stringResource(R.string.clear_chat_menu_action),
Icons.Outlined.Restore,
painterResource(R.drawable.ic_settings_backup_restore),
onClick = {
clearChatDialog(chat.chatInfo, chatModel)
showMenu.value = false
@@ -233,7 +231,7 @@ fun ClearChatAction(chat: Chat, chatModel: ChatModel, showMenu: MutableState<Boo
fun DeleteContactAction(chat: Chat, chatModel: ChatModel, showMenu: MutableState<Boolean>) {
ItemAction(
stringResource(R.string.delete_contact_menu_action),
Icons.Outlined.Delete,
painterResource(R.drawable.ic_delete),
onClick = {
deleteContactDialog(chat.chatInfo, chatModel)
showMenu.value = false
@@ -246,7 +244,7 @@ fun DeleteContactAction(chat: Chat, chatModel: ChatModel, showMenu: MutableState
fun DeleteGroupAction(chat: Chat, groupInfo: GroupInfo, chatModel: ChatModel, showMenu: MutableState<Boolean>) {
ItemAction(
stringResource(R.string.delete_group_menu_action),
Icons.Outlined.Delete,
painterResource(R.drawable.ic_delete),
onClick = {
deleteGroupDialog(chat.chatInfo, groupInfo, chatModel)
showMenu.value = false
@@ -260,7 +258,7 @@ fun JoinGroupAction(chat: Chat, groupInfo: GroupInfo, chatModel: ChatModel, show
val joinGroup: () -> Unit = { withApi { chatModel.controller.apiJoinGroup(groupInfo.groupId) } }
ItemAction(
if (chat.chatInfo.incognito) stringResource(R.string.join_group_incognito_button) else stringResource(R.string.join_group_button),
if (chat.chatInfo.incognito) Icons.Filled.TheaterComedy else Icons.Outlined.Login,
if (chat.chatInfo.incognito) painterResource(R.drawable.ic_theater_comedy_filled) else painterResource(R.drawable.ic_login),
color = if (chat.chatInfo.incognito) Indigo else MaterialTheme.colors.onBackground,
onClick = {
joinGroup()
@@ -273,7 +271,7 @@ fun JoinGroupAction(chat: Chat, groupInfo: GroupInfo, chatModel: ChatModel, show
fun LeaveGroupAction(groupInfo: GroupInfo, chatModel: ChatModel, showMenu: MutableState<Boolean>) {
ItemAction(
stringResource(R.string.leave_group_button),
Icons.Outlined.Logout,
painterResource(R.drawable.ic_logout),
onClick = {
leaveGroupDialog(groupInfo, chatModel)
showMenu.value = false
@@ -286,7 +284,7 @@ fun LeaveGroupAction(groupInfo: GroupInfo, chatModel: ChatModel, showMenu: Mutab
fun ContactRequestMenuItems(chatInfo: ChatInfo.ContactRequest, chatModel: ChatModel, showMenu: MutableState<Boolean>) {
ItemAction(
if (chatModel.incognito.value) stringResource(R.string.accept_contact_incognito_button) else stringResource(R.string.accept_contact_button),
if (chatModel.incognito.value) Icons.Filled.TheaterComedy else Icons.Outlined.Check,
if (chatModel.incognito.value) painterResource(R.drawable.ic_theater_comedy_filled) else painterResource(R.drawable.ic_check),
color = if (chatModel.incognito.value) Indigo else MaterialTheme.colors.onBackground,
onClick = {
acceptContactRequest(chatInfo.apiId, chatInfo, true, chatModel)
@@ -295,7 +293,7 @@ fun ContactRequestMenuItems(chatInfo: ChatInfo.ContactRequest, chatModel: ChatMo
)
ItemAction(
stringResource(R.string.reject_contact_button),
Icons.Outlined.Close,
painterResource(R.drawable.ic_close),
onClick = {
rejectContactRequest(chatInfo, chatModel)
showMenu.value = false
@@ -308,7 +306,7 @@ fun ContactRequestMenuItems(chatInfo: ChatInfo.ContactRequest, chatModel: ChatMo
fun ContactConnectionMenuItems(chatInfo: ChatInfo.ContactConnection, chatModel: ChatModel, showMenu: MutableState<Boolean>) {
ItemAction(
stringResource(R.string.set_contact_name),
Icons.Outlined.Edit,
painterResource(R.drawable.ic_edit),
onClick = {
ModalManager.shared.showModalCloseable(true) { close ->
ContactConnectionInfoView(chatModel, chatInfo.contactConnection.connReqInv, chatInfo.contactConnection, true, close)
@@ -318,7 +316,7 @@ fun ContactConnectionMenuItems(chatInfo: ChatInfo.ContactConnection, chatModel:
)
ItemAction(
stringResource(R.string.delete_verb),
Icons.Outlined.Delete,
painterResource(R.drawable.ic_delete),
onClick = {
deleteContactConnectionAlert(chatInfo.contactConnection, chatModel) {}
showMenu.value = false
@@ -330,7 +328,7 @@ fun ContactConnectionMenuItems(chatInfo: ChatInfo.ContactConnection, chatModel:
@Composable
private fun InvalidDataView() {
Row {
ProfileImage(72.dp, null, Icons.Filled.AccountCircle, HighOrLowlight)
ProfileImage(72.dp, null, R.drawable.ic_account_circle_filled, HighOrLowlight)
Column(
modifier = Modifier
.padding(horizontal = 8.dp)
@@ -7,15 +7,13 @@ import androidx.compose.foundation.lazy.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.*
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.*
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.capitalize
import androidx.compose.ui.text.font.FontWeight
@@ -67,10 +65,10 @@ fun ChatListView(chatModel: ChatModel, setPerformLA: (Boolean, FragmentActivity)
val scaffoldState = rememberScaffoldState()
val scope = rememberCoroutineScope()
val switchingUsers = rememberSaveable { mutableStateOf(false) }
Scaffold(
topBar = { ChatListToolbar(chatModel, scaffoldState.drawerState, userPickerState, stopped) { searchInList = it.trim() } },
Scaffold(topBar = { ChatListToolbar(chatModel, scaffoldState.drawerState, userPickerState, stopped) { searchInList = it.trim() } },
scaffoldState = scaffoldState,
drawerContent = { SettingsView(chatModel, setPerformLA) },
drawerScrimColor = MaterialTheme.colors.onSurface.copy(alpha = if (isInDarkTheme()) 0.16f else 0.32f),
floatingActionButton = {
if (searchInList.isEmpty()) {
FloatingActionButton(
@@ -79,6 +77,7 @@ fun ChatListView(chatModel: ChatModel, setPerformLA: (Boolean, FragmentActivity)
if (newChatSheetState.value.isVisible()) hideNewChatSheet(true) else showNewChatSheet()
}
},
Modifier.padding(end = DEFAULT_PADDING - 16.dp, bottom = DEFAULT_PADDING - 16.dp),
elevation = FloatingActionButtonDefaults.elevation(
defaultElevation = 0.dp,
pressedElevation = 0.dp,
@@ -88,7 +87,7 @@ fun ChatListView(chatModel: ChatModel, setPerformLA: (Boolean, FragmentActivity)
backgroundColor = if (!stopped) MaterialTheme.colors.primary else HighOrLowlight,
contentColor = Color.White
) {
Icon(if (!newChatSheetState.collectAsState().value.isVisible()) Icons.Default.Edit else Icons.Default.Close, stringResource(R.string.add_contact_or_create_group))
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))
}
}
}
@@ -181,7 +180,7 @@ private fun ChatListToolbar(chatModel: ChatModel, drawerState: DrawerState, user
if (chatModel.chats.size >= 8) {
barButtons.add {
IconButton({ showSearch = true }) {
Icon(Icons.Outlined.Search, stringResource(android.R.string.search_go).capitalize(Locale.current), tint = MaterialTheme.colors.primary)
Icon(painterResource(R.drawable.ic_search_500), stringResource(android.R.string.search_go).capitalize(Locale.current), tint = MaterialTheme.colors.primary)
}
}
}
@@ -194,7 +193,7 @@ private fun ChatListToolbar(chatModel: ChatModel, drawerState: DrawerState, user
)
}) {
Icon(
Icons.Filled.Report,
painterResource(R.drawable.ic_report_filled),
generalGetString(R.string.chat_is_stopped_indication),
tint = Color.Red,
)
@@ -231,7 +230,7 @@ private fun ChatListToolbar(chatModel: ChatModel, drawerState: DrawerState, user
)
if (chatModel.incognito.value) {
Icon(
Icons.Filled.TheaterComedy,
painterResource(R.drawable.ic_theater_comedy_filled),
stringResource(R.string.incognito),
tint = Indigo,
modifier = Modifier.padding(10.dp).size(26.dp)
@@ -7,15 +7,12 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.*
import androidx.compose.ui.text.font.FontWeight
@@ -46,7 +43,7 @@ fun ChatPreviewView(
@Composable
fun groupInactiveIcon() {
Icon(
Icons.Filled.Cancel,
painterResource(R.drawable.ic_cancel_filled),
stringResource(R.string.icon_descr_group_inactive),
Modifier.size(18.dp).background(MaterialTheme.colors.background, CircleShape),
tint = HighOrLowlight
@@ -79,15 +76,15 @@ fun ChatPreviewView(
@Composable
fun VerifiedIcon() {
Icon(Icons.Outlined.VerifiedUser, 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 = HighOrLowlight)
}
fun messageDraft(draft: ComposeState): Pair<AnnotatedString, Map<String, InlineTextContent>> {
fun attachment(): Pair<ImageVector, String?>? =
fun attachment(): Pair<Int, String?>? =
when (draft.preview) {
is ComposePreview.FilePreview -> Icons.Filled.InsertDriveFile to draft.preview.fileName
is ComposePreview.MediaPreview -> Icons.Outlined.Image to null
is ComposePreview.VoicePreview -> Icons.Filled.PlayArrow to durationText(draft.preview.durationMs / 1000)
is ComposePreview.FilePreview -> R.drawable.ic_draft_filled to draft.preview.fileName
is ComposePreview.MediaPreview -> R.drawable.ic_image to null
is ComposePreview.VoicePreview -> R.drawable.ic_play_arrow_filled to durationText(draft.preview.durationMs / 1000)
else -> null
}
@@ -108,12 +105,12 @@ fun ChatPreviewView(
"editIcon" to InlineTextContent(
Placeholder(20.sp, 20.sp, PlaceholderVerticalAlign.TextCenter)
) {
Icon(Icons.Outlined.EditNote, null, tint = MaterialTheme.colors.primary)
Icon(painterResource(R.drawable.ic_edit_note), null, tint = MaterialTheme.colors.primary)
},
"attachmentIcon" to InlineTextContent(
Placeholder(20.sp, 20.sp, PlaceholderVerticalAlign.TextCenter)
) {
Icon(attachment?.first ?: Icons.Outlined.EditNote, null, tint = HighOrLowlight)
Icon(if (attachment?.first != null) painterResource(attachment.first) else painterResource(R.drawable.ic_edit_note), null, tint = HighOrLowlight)
}
)
return text to inlineContent
@@ -239,7 +236,7 @@ fun ChatPreviewView(
contentAlignment = Alignment.Center
) {
Icon(
Icons.Filled.NotificationsOff,
painterResource(R.drawable.ic_notifications_off_filled),
contentDescription = generalGetString(R.string.notifications),
tint = HighOrLowlight,
modifier = Modifier
@@ -281,7 +278,7 @@ fun ChatStatusImage(s: NetworkStatus?) {
val descr = s?.statusString
if (s is NetworkStatus.Error) {
Icon(
Icons.Outlined.ErrorOutline,
painterResource(R.drawable.ic_error),
contentDescription = descr,
tint = HighOrLowlight,
modifier = Modifier
@@ -3,16 +3,16 @@ package chat.simplex.app.views.chatlist
import androidx.compose.foundation.layout.*
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
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.*
import chat.simplex.app.views.helpers.ProfileImage
@@ -21,7 +21,7 @@ import chat.simplex.app.views.helpers.ProfileImage
fun ContactConnectionView(contactConnection: PendingContactConnection) {
Row {
Box(Modifier.size(72.dp), contentAlignment = Alignment.Center) {
ProfileImage(size = 54.dp, null, if (contactConnection.initiated) Icons.Outlined.AddLink else Icons.Outlined.Link)
ProfileImage(size = 54.dp, null, if (contactConnection.initiated) R.drawable.ic_add_link else R.drawable.ic_link)
}
Column(
modifier = Modifier
@@ -42,7 +42,6 @@ fun ContactConnectionView(contactConnection: PendingContactConnection) {
val ts = getTimestampText(contactConnection.updatedAt)
Column(
Modifier.fillMaxHeight(),
verticalArrangement = Arrangement.Top
) {
Text(
ts,
@@ -39,7 +39,6 @@ fun ContactRequestView(chatModelIncognito: Boolean, contactRequest: ChatInfo.Con
val ts = getTimestampText(contactRequest.contactRequest.updatedAt)
Column(
Modifier.fillMaxHeight(),
verticalArrangement = Arrangement.Top
) {
Text(
ts,
@@ -6,14 +6,12 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.*
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.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.capitalize
import androidx.compose.ui.text.font.FontWeight
@@ -86,7 +84,7 @@ private fun ShareListToolbar(chatModel: ChatModel, userPickerState: MutableState
if (chatModel.chats.size >= 8) {
barButtons.add {
IconButton({ showSearch = true }) {
Icon(Icons.Outlined.Search, stringResource(android.R.string.search_go).capitalize(Locale.current), tint = MaterialTheme.colors.primary)
Icon(painterResource(R.drawable.ic_search_500), stringResource(android.R.string.search_go).capitalize(Locale.current), tint = MaterialTheme.colors.primary)
}
}
}
@@ -99,7 +97,7 @@ private fun ShareListToolbar(chatModel: ChatModel, userPickerState: MutableState
)
}) {
Icon(
Icons.Filled.Report,
painterResource(R.drawable.ic_report_filled),
generalGetString(R.string.chat_is_stopped_indication),
tint = Color.Red,
)
@@ -123,7 +121,7 @@ private fun ShareListToolbar(chatModel: ChatModel, userPickerState: MutableState
)
if (chatModel.incognito.value) {
Icon(
Icons.Filled.TheaterComedy,
painterResource(R.drawable.ic_theater_comedy_filled),
stringResource(R.string.incognito),
tint = Indigo,
modifier = Modifier.padding(10.dp).size(26.dp)
@@ -1,7 +1,6 @@
package chat.simplex.app.views.chatlist
import SectionItemView
import SectionItemViewSpaceBetween
import android.util.Log
import androidx.compose.animation.core.*
import androidx.compose.foundation.*
@@ -9,9 +8,6 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.ui.*
import androidx.compose.ui.draw.*
@@ -19,6 +15,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.capitalize
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.intl.Locale
@@ -110,12 +107,12 @@ fun UserPicker(
.width(IntrinsicSize.Min)
.height(IntrinsicSize.Min)
.shadow(8.dp, RoundedCornerShape(corner = CornerSize(25.dp)), clip = true)
.background(if (isInDarkTheme()) Color(0xff222222) else MaterialTheme.colors.background, RoundedCornerShape(corner = CornerSize(25.dp)))
.background(if (isInDarkTheme()) DarkGrayBackground else MaterialTheme.colors.background, RoundedCornerShape(corner = CornerSize(25.dp)))
.clip(RoundedCornerShape(corner = CornerSize(25.dp)))
) {
Column(Modifier.weight(1f).verticalScroll(rememberScrollState())) {
users.forEach { u ->
UserProfilePickerItem(u.user, u.unreadCount, PaddingValues(start = DEFAULT_PADDING, end = DEFAULT_PADDING * 2), openSettings = {
UserProfilePickerItem(u.user, u.unreadCount, PaddingValues(start = DEFAULT_PADDING_HALF, end = DEFAULT_PADDING), openSettings = {
settingsClicked()
userPickerState.value = AnimatedViewState.GONE
}) {
@@ -153,7 +150,7 @@ fun UserPicker(
}
@Composable
fun UserProfilePickerItem(u: User, unreadCount: Int = 0, padding: PaddingValues = PaddingValues(start = 8.dp, end = DEFAULT_PADDING), onLongClick: () -> Unit = {}, openSettings: () -> Unit = {}, onClick: () -> Unit) {
fun UserProfilePickerItem(u: User, unreadCount: Int = 0, padding: PaddingValues = PaddingValues(start = DEFAULT_PADDING_HALF, end = DEFAULT_PADDING), onLongClick: () -> Unit = {}, openSettings: () -> Unit = {}, onClick: () -> Unit) {
Row(
Modifier
.fillMaxWidth()
@@ -170,27 +167,25 @@ fun UserProfilePickerItem(u: User, unreadCount: Int = 0, padding: PaddingValues
) {
UserProfileRow(u)
if (u.activeUser) {
Icon(Icons.Filled.Done, null, Modifier.size(20.dp), tint = MaterialTheme.colors.onBackground)
Icon(painterResource(R.drawable.ic_done_filled), null, Modifier.size(20.dp), tint = MaterialTheme.colors.onBackground)
} else if (u.hidden) {
Icon(Icons.Outlined.Lock, null, Modifier.size(20.dp), tint = HighOrLowlight)
Icon(painterResource(R.drawable.ic_lock), null, Modifier.size(20.dp), tint = HighOrLowlight)
} else if (unreadCount > 0) {
Row {
Box(
contentAlignment = Alignment.Center
) {
Text(
unreadCountStr(unreadCount),
color = Color.White,
fontSize = 11.sp,
modifier = Modifier
.background(if (u.showNtfs) MaterialTheme.colors.primary else HighOrLowlight, shape = CircleShape)
.sizeIn(minWidth = 20.dp, minHeight = 20.dp)
.padding(horizontal = 3.dp)
.padding(vertical = 1.dp),
textAlign = TextAlign.Center,
maxLines = 1
.padding(2.dp)
.badgeLayout()
)
Spacer(Modifier.width(2.dp))
}
} else if (!u.showNtfs) {
Icon(Icons.Outlined.NotificationsOff, null, Modifier.size(20.dp), tint = HighOrLowlight)
Icon(painterResource(R.drawable.ic_notifications_off), null, Modifier.size(20.dp), tint = HighOrLowlight)
} else {
Box(Modifier.size(20.dp))
}
@@ -212,7 +207,7 @@ fun UserProfileRow(u: User) {
Text(
u.displayName,
modifier = Modifier
.padding(start = 8.dp, end = 8.dp),
.padding(start = 10.dp, end = 8.dp),
color = if (isInDarkTheme()) MenuTextColorDark else Color.Black,
fontWeight = if (u.activeUser) FontWeight.Medium else FontWeight.Normal
)
@@ -221,10 +216,10 @@ fun UserProfileRow(u: User) {
@Composable
private fun SettingsPickerItem(onClick: () -> Unit) {
SectionItemView(onClick, padding = PaddingValues(start = DEFAULT_PADDING * 2.2f, end = DEFAULT_PADDING * 2), minHeight = 68.dp) {
SectionItemView(onClick, padding = PaddingValues(start = DEFAULT_PADDING + 7.dp, end = DEFAULT_PADDING), minHeight = 68.dp) {
val text = generalGetString(R.string.settings_section_title_settings).lowercase().capitalize(Locale.current)
Icon(Icons.Outlined.Settings, text, Modifier.size(20.dp), tint = MaterialTheme.colors.onBackground)
Spacer(Modifier.width(DEFAULT_PADDING * 1.5f))
Icon(painterResource(R.drawable.ic_settings), text, Modifier.size(20.dp), tint = MaterialTheme.colors.onBackground)
Spacer(Modifier.width(DEFAULT_PADDING + 6.dp))
Text(
text,
color = if (isInDarkTheme()) MenuTextColorDark else Color.Black,
@@ -234,10 +229,10 @@ private fun SettingsPickerItem(onClick: () -> Unit) {
@Composable
private fun CancelPickerItem(onClick: () -> Unit) {
SectionItemViewSpaceBetween(onClick, padding = PaddingValues(start = DEFAULT_PADDING * 2.2f, end = DEFAULT_PADDING * 2), minHeight = 68.dp) {
SectionItemView(onClick, padding = PaddingValues(start = DEFAULT_PADDING + 7.dp, end = DEFAULT_PADDING), minHeight = 68.dp) {
val text = generalGetString(R.string.cancel_verb)
Icon(Icons.Outlined.Close, text, Modifier.size(20.dp), tint = MaterialTheme.colors.onBackground)
Spacer(Modifier.width(DEFAULT_PADDING * 1.5f))
Icon(painterResource(R.drawable.ic_close), text, Modifier.size(20.dp), tint = MaterialTheme.colors.onBackground)
Spacer(Modifier.width(DEFAULT_PADDING + 6.dp))
Text(
text,
color = if (isInDarkTheme()) MenuTextColorDark else Color.Black,
@@ -1,6 +1,6 @@
package chat.simplex.app.views.database
import SectionDivider
import SectionBottomSpacer
import SectionTextFooter
import SectionView
import android.content.Context
@@ -13,16 +13,14 @@ import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import chat.simplex.app.R
import chat.simplex.app.TAG
import chat.simplex.app.model.ChatModel
@@ -57,20 +55,18 @@ fun ChatArchiveLayout(
) {
Column(
Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.Start,
) {
AppBarTitle(title)
SectionView(stringResource(R.string.chat_archive_section)) {
SettingsActionItem(
Icons.Outlined.IosShare,
painterResource(R.drawable.ic_ios_share),
stringResource(R.string.save_archive),
saveArchive,
textColor = MaterialTheme.colors.primary,
iconColor = MaterialTheme.colors.primary,
)
SectionDivider()
SettingsActionItem(
Icons.Outlined.Delete,
painterResource(R.drawable.ic_delete),
stringResource(R.string.delete_archive),
deleteArchiveAlert,
textColor = Color.Red,
@@ -81,6 +77,7 @@ fun ChatArchiveLayout(
SectionTextFooter(
String.format(generalGetString(R.string.archive_created_on_ts), archiveTs)
)
SectionBottomSpacer()
}
}
@@ -1,6 +1,6 @@
package chat.simplex.app.views.database
import SectionDivider
import SectionBottomSpacer
import SectionItemView
import SectionItemViewSpaceBetween
import SectionTextFooter
@@ -12,15 +12,13 @@ import androidx.compose.foundation.shape.ZeroCornerSize
import androidx.compose.foundation.text.*
import androidx.compose.material.*
import androidx.compose.material.TextFieldDefaults.indicatorLine
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.*
import androidx.compose.ui.text.font.FontWeight
@@ -137,7 +135,6 @@ fun DatabaseEncryptionLayout(
) {
Column(
Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.Start,
) {
AppBarTitle(stringResource(R.string.database_passphrase))
SectionView(null) {
@@ -162,8 +159,6 @@ fun DatabaseEncryptionLayout(
}
if (!initialRandomDBPassphrase.value && chatDbEncrypted == true) {
SectionDivider()
PassphraseField(
currentKey,
generalGetString(R.string.current_passphrase),
@@ -173,8 +168,6 @@ fun DatabaseEncryptionLayout(
)
}
SectionDivider()
PassphraseField(
newKey,
generalGetString(R.string.new_passphrase),
@@ -206,8 +199,6 @@ fun DatabaseEncryptionLayout(
!validKey(newKey.value) ||
progressIndicator.value
SectionDivider()
PassphraseField(
confirmNewKey,
generalGetString(R.string.confirm_new_passphrase),
@@ -219,8 +210,6 @@ fun DatabaseEncryptionLayout(
}),
)
SectionDivider()
SectionItemViewSpaceBetween(onClickUpdate, disabled = disabled, minHeight = TextFieldDefaults.MinHeight) {
Text(generalGetString(R.string.update_database_passphrase), color = if (disabled) HighOrLowlight else MaterialTheme.colors.primary)
}
@@ -245,6 +234,7 @@ fun DatabaseEncryptionLayout(
SectionTextFooter(generalGetString(R.string.impossible_to_recover_passphrase))
}
}
SectionBottomSpacer()
}
}
@@ -300,7 +290,7 @@ fun SavePassphraseSetting(
SectionItemView(minHeight = minHeight) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
if (storedKey) Icons.Filled.VpnKey else Icons.Filled.VpnKeyOff,
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
)
@@ -311,13 +301,9 @@ fun SavePassphraseSetting(
color = Color.Unspecified
)
Spacer(Modifier.fillMaxWidth().weight(1f))
Switch(
DefaultSwitch(
checked = useKeychain,
onCheckedChange = onCheckedChange,
colors = SwitchDefaults.colors(
checkedThumbColor = MaterialTheme.colors.primary,
uncheckedThumbColor = HighOrLowlight
),
enabled = !initialRandomDBPassphrase && !progressIndicator
)
}
@@ -371,8 +357,8 @@ fun PassphraseField(
var valid by remember { mutableStateOf(validKey(key.value)) }
var showKey by remember { mutableStateOf(false) }
val icon = if (valid) {
if (showKey) Icons.Filled.VisibilityOff else Icons.Filled.Visibility
} else Icons.Outlined.Error
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
} else Color.Red
@@ -1,5 +1,6 @@
package chat.simplex.app.views.database
import SectionBottomSpacer
import SectionSpacer
import SectionView
import android.content.Context
@@ -61,7 +62,7 @@ fun DatabaseErrorView(
fun DatabaseErrorDetails(@StringRes title: Int, content: @Composable ColumnScope.() -> Unit) {
Text(
generalGetString(title),
Modifier.padding(start = 16.dp, top = 16.dp, bottom = 16.dp),
Modifier.padding(start = DEFAULT_PADDING, top = DEFAULT_PADDING, bottom = DEFAULT_PADDING),
style = MaterialTheme.typography.h1
)
SectionView(null, padding = PaddingValues(horizontal = DEFAULT_PADDING, vertical = DEFAULT_PADDING_HALF), content)
@@ -79,7 +80,6 @@ fun DatabaseErrorView(
Column(
Modifier.fillMaxSize().verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.Center,
) {
val buttonEnabled = validKey(dbKey.value) && !progressIndicator.value
@@ -158,7 +158,7 @@ fun DatabaseErrorView(
if (restoreDbFromBackup.value) {
SectionSpacer()
Text(generalGetString(R.string.database_backup_can_be_restored))
Spacer(Modifier.size(16.dp))
Spacer(Modifier.size(DEFAULT_PADDING))
RestoreDbButton {
AlertManager.shared.showAlertDialog(
title = generalGetString(R.string.restore_database_alert_title),
@@ -169,6 +169,7 @@ fun DatabaseErrorView(
)
}
}
SectionBottomSpacer()
}
if (progressIndicator.value) {
Box(
@@ -1,9 +1,9 @@
package chat.simplex.app.views.database
import SectionDivider
import SectionBottomSpacer
import SectionDividerSpaced
import SectionTextFooter
import SectionItemView
import SectionSpacer
import SectionView
import android.content.Context
import android.content.res.Configuration
@@ -17,14 +17,12 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.*
import androidx.compose.ui.text.font.FontWeight
@@ -154,13 +152,12 @@ fun DatabaseLayout(
val operationsDisabled = !stopped || progressIndicator
Column(
Modifier.fillMaxWidth().verticalScroll(rememberScrollState()).padding(bottom = DEFAULT_BOTTOM_PADDING),
horizontalAlignment = Alignment.Start,
Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
) {
AppBarTitle(stringResource(R.string.your_chat_database))
SectionView(stringResource(R.string.messages_section_title).uppercase()) {
SectionItemView { TtlOptions(chatItemTTL, enabled = rememberUpdatedState(!stopped && !progressIndicator), onChatItemTTLSelected) }
TtlOptions(chatItemTTL, enabled = rememberUpdatedState(!stopped && !progressIndicator), onChatItemTTLSelected)
}
SectionTextFooter(
remember(currentUser?.displayName) {
@@ -173,27 +170,28 @@ fun DatabaseLayout(
}
}
)
SectionSpacer()
SectionDividerSpaced(maxTopPadding = true)
SectionView(stringResource(R.string.run_chat_section)) {
RunChatSetting(runChat, stopped, chatDbDeleted, startChat, stopChatAlert)
}
SectionSpacer()
SectionDividerSpaced()
SectionView(stringResource(R.string.chat_database_section)) {
val unencrypted = chatDbEncrypted == false
SettingsActionItem(
if (unencrypted) Icons.Outlined.LockOpen else if (useKeyChain) Icons.Filled.VpnKey else Icons.Outlined.Lock,
if (unencrypted) painterResource(R.drawable.ic_lock_open) else if (useKeyChain) painterResource(R.drawable.ic_vpn_key_filled)
else painterResource(R
.drawable.ic_lock),
stringResource(R.string.database_passphrase),
click = showSettingsModal() { DatabaseEncryptionView(it) },
iconColor = if (unencrypted) WarningOrange else HighOrLowlight,
disabled = operationsDisabled
)
SectionDivider()
AppDataBackupPreference(privacyFullBackup, initialRandomDBPassphrase)
SectionDivider()
SectionDividerSpaced(maxBottomPadding = false)
SettingsActionItem(
Icons.Outlined.IosShare,
painterResource(R.drawable.ic_ios_share),
stringResource(R.string.export_database),
click = {
if (initialRandomDBPassphrase.get()) {
@@ -206,31 +204,28 @@ fun DatabaseLayout(
iconColor = MaterialTheme.colors.primary,
disabled = operationsDisabled
)
SectionDivider()
SettingsActionItem(
Icons.Outlined.FileDownload,
painterResource(R.drawable.ic_download),
stringResource(R.string.import_database),
{ importArchiveLauncher.launch("application/zip") },
textColor = Color.Red,
iconColor = Color.Red,
disabled = operationsDisabled
)
SectionDivider()
val chatArchiveNameVal = chatArchiveName.value
val chatArchiveTimeVal = chatArchiveTime.value
val chatLastStartVal = chatLastStart.value
if (chatArchiveNameVal != null && chatArchiveTimeVal != null && chatLastStartVal != null) {
val title = chatArchiveTitle(chatArchiveTimeVal, chatLastStartVal)
SettingsActionItem(
Icons.Outlined.Inventory2,
painterResource(R.drawable.ic_inventory_2),
title,
click = showSettingsModal { ChatArchiveView(it, title, chatArchiveNameVal, chatArchiveTimeVal) },
disabled = operationsDisabled
)
SectionDivider()
}
SettingsActionItem(
Icons.Outlined.DeleteForever,
painterResource(R.drawable.ic_delete_forever),
stringResource(R.string.delete_database),
deleteChatAlert,
textColor = Color.Red,
@@ -245,7 +240,7 @@ fun DatabaseLayout(
stringResource(R.string.stop_chat_to_enable_database_actions)
}
)
SectionSpacer()
SectionDividerSpaced(maxTopPadding = true)
SectionView(stringResource(R.string.files_and_media_section).uppercase()) {
val deleteFilesDisabled = operationsDisabled || appFilesCountAndSize.value.first == 0
@@ -267,35 +262,23 @@ fun DatabaseLayout(
String.format(stringResource(R.string.total_files_count_and_size), count, formatBytes(size))
}
)
SectionBottomSpacer()
}
}
@Composable
private fun AppDataBackupPreference(privacyFullBackup: SharedPreference<Boolean>, initialRandomDBPassphrase: SharedPreference<Boolean>) {
SectionItemView {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Outlined.Backup, stringResource(R.string.full_backup), tint = HighOrLowlight)
Spacer(Modifier.padding(horizontal = 4.dp))
val prefState = remember { mutableStateOf(privacyFullBackup.get()) }
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Text(stringResource(R.string.full_backup), Modifier.padding(end = 24.dp))
Spacer(Modifier.fillMaxWidth().weight(1f))
Switch(
checked = prefState.value,
onCheckedChange = {
if (initialRandomDBPassphrase.get()) {
exportProhibitedAlert()
} else {
privacyFullBackup.set(it)
prefState.value = it
}
},
colors = SwitchDefaults.colors(
checkedThumbColor = MaterialTheme.colors.primary,
uncheckedThumbColor = HighOrLowlight
)
)
}
SettingsPreferenceItem(
painterResource(R.drawable.ic_backup),
iconColor = HighOrLowlight,
pref = privacyFullBackup,
text = stringResource(R.string.full_backup)
) {
if (initialRandomDBPassphrase.get()) {
exportProhibitedAlert()
privacyFullBackup.set(false)
} else {
privacyFullBackup.set(it)
}
}
}
@@ -350,36 +333,23 @@ fun RunChatSetting(
startChat: () -> Unit,
stopChatAlert: () -> Unit
) {
SectionItemView() {
Row(verticalAlignment = Alignment.CenterVertically) {
val chatRunningText = if (stopped) stringResource(R.string.chat_is_stopped) else stringResource(R.string.chat_is_running)
Icon(
if (stopped) Icons.Filled.Report else Icons.Filled.PlayArrow,
chatRunningText,
tint = if (stopped) Color.Red else MaterialTheme.colors.primary
)
Spacer(Modifier.padding(horizontal = 4.dp))
Text(
chatRunningText,
Modifier.padding(end = 24.dp)
)
Spacer(Modifier.fillMaxWidth().weight(1f))
Switch(
enabled = !chatDbDeleted,
checked = runChat,
onCheckedChange = { runChatSwitch ->
if (runChatSwitch) {
startChat()
} else {
stopChatAlert()
}
},
colors = SwitchDefaults.colors(
checkedThumbColor = MaterialTheme.colors.primary,
uncheckedThumbColor = HighOrLowlight
),
)
}
val chatRunningText = if (stopped) stringResource(R.string.chat_is_stopped) else stringResource(R.string.chat_is_running)
SettingsActionItemWithContent(
icon = if (stopped) painterResource(R.drawable.ic_report_filled) else painterResource(R.drawable.ic_play_arrow_filled),
text = chatRunningText,
iconColor = if (stopped) Color.Red else MaterialTheme.colors.primary,
) {
DefaultSwitch(
enabled = !chatDbDeleted,
checked = runChat,
onCheckedChange = { runChatSwitch ->
if (runChatSwitch) {
startChat()
} else {
stopChatAlert()
}
},
)
}
}
@@ -37,7 +37,7 @@ class AlertManager {
) {
showAlert {
AlertDialog(
backgroundColor = if (isInDarkTheme()) Color(0xff222222) else MaterialTheme.colors.background,
backgroundColor = if (isInDarkTheme()) DarkGrayBackground else MaterialTheme.colors.background,
onDismissRequest = this::hideAlert,
title = alertTitle(title),
text = alertText(text),
@@ -56,7 +56,7 @@ class AlertManager {
Dialog(onDismissRequest = this::hideAlert) {
Column(
Modifier
.background(if (isInDarkTheme()) Color(0xff222222) else MaterialTheme.colors.background, RoundedCornerShape(corner = CornerSize(25.dp)))
.background(if (isInDarkTheme()) DarkGrayBackground else MaterialTheme.colors.background, RoundedCornerShape(corner = CornerSize(25.dp)))
.padding(bottom = DEFAULT_PADDING)
) {
Text(
@@ -106,7 +106,7 @@ class AlertManager {
}) { Text(confirmText, color = if (destructive) MaterialTheme.colors.error else Color.Unspecified) }
}
},
backgroundColor = if (isInDarkTheme()) Color(0xff222222) else MaterialTheme.colors.background,
backgroundColor = if (isInDarkTheme()) DarkGrayBackground else MaterialTheme.colors.background,
shape = RoundedCornerShape(corner = CornerSize(25.dp))
)
}
@@ -129,7 +129,7 @@ class AlertManager {
text = alertText(text),
buttons = {
Column(
Modifier.fillMaxWidth().padding(horizontal = 8.dp).padding(top = 16.dp, bottom = 2.dp),
Modifier.fillMaxWidth().padding(horizontal = DEFAULT_PADDING_HALF).padding(top = DEFAULT_PADDING, bottom = 2.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
TextButton(onClick = {
@@ -142,7 +142,7 @@ class AlertManager {
}) { Text(confirmText, color = if (destructive) Color.Red else Color.Unspecified, textAlign = TextAlign.End) }
}
},
backgroundColor = if (isInDarkTheme()) Color(0xff222222) else MaterialTheme.colors.background,
backgroundColor = if (isInDarkTheme()) DarkGrayBackground else MaterialTheme.colors.background,
shape = RoundedCornerShape(corner = CornerSize(25.dp))
)
}
@@ -168,7 +168,7 @@ class AlertManager {
}) { Text(confirmText, color = Color.Unspecified) }
}
},
backgroundColor = if (isInDarkTheme()) Color(0xff222222) else MaterialTheme.colors.background,
backgroundColor = if (isInDarkTheme()) DarkGrayBackground else MaterialTheme.colors.background,
shape = RoundedCornerShape(corner = CornerSize(25.dp))
)
}
@@ -5,15 +5,12 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.*
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
@@ -25,8 +22,8 @@ import chat.simplex.app.ui.theme.SimpleXTheme
@Composable
fun ChatInfoImage(chatInfo: ChatInfo, size: Dp, iconColor: Color = MaterialTheme.colors.secondary) {
val icon =
if (chatInfo is ChatInfo.Group) Icons.Filled.SupervisedUserCircle
else Icons.Filled.AccountCircle
if (chatInfo is ChatInfo.Group) R.drawable.ic_supervised_user_circle_filled
else R.drawable.ic_account_circle_filled
ProfileImage(size, chatInfo.image, icon, iconColor)
}
@@ -34,7 +31,7 @@ fun ChatInfoImage(chatInfo: ChatInfo, size: Dp, iconColor: Color = MaterialTheme
fun IncognitoImage(size: Dp, iconColor: Color = MaterialTheme.colors.secondary) {
Box(Modifier.size(size)) {
Icon(
Icons.Filled.TheaterComedy, stringResource(R.string.incognito),
painterResource(R.drawable.ic_theater_comedy_filled), stringResource(R.string.incognito),
modifier = Modifier.size(size).padding(size / 12),
iconColor
)
@@ -45,17 +42,31 @@ fun IncognitoImage(size: Dp, iconColor: Color = MaterialTheme.colors.secondary)
fun ProfileImage(
size: Dp,
image: String? = null,
icon: ImageVector = Icons.Filled.AccountCircle,
icon: Int = R.drawable.ic_account_circle_filled,
color: Color = MaterialTheme.colors.secondary
) {
Box(Modifier.size(size)) {
if (image == null) {
Icon(
icon,
contentDescription = stringResource(R.string.icon_descr_profile_image_placeholder),
tint = color,
modifier = Modifier.fillMaxSize()
)
val iconToReplace = when (icon) {
R.drawable.ic_account_circle_filled -> AccountCircleFilled
R.drawable.ic_supervised_user_circle_filled -> SupervisedUserCircleFilled
else -> null
}
if (iconToReplace != null) {
Icon(
iconToReplace,
contentDescription = stringResource(R.string.icon_descr_profile_image_placeholder),
tint = color,
modifier = Modifier.fillMaxSize()
)
} else {
Icon(
painterResource(icon),
contentDescription = stringResource(R.string.icon_descr_profile_image_placeholder),
tint = color,
modifier = Modifier.fillMaxSize()
)
}
} else {
val imageBitmap = base64ToBitmap(image).asImageBitmap()
Image(
@@ -68,6 +79,7 @@ fun ProfileImage(
}
}
@Preview
@Composable
fun PreviewChatInfoImage() {
@@ -2,8 +2,6 @@ package chat.simplex.app.views.helpers
import androidx.compose.foundation.layout.*
import androidx.compose.material.MaterialTheme
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.ui.Alignment
@@ -6,11 +6,10 @@ import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
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.R
import chat.simplex.app.ui.theme.*
@Composable
@@ -42,30 +41,15 @@ fun CloseSheetBar(close: (() -> Unit)?, endButtons: @Composable RowScope.() -> U
@Composable
fun AppBarTitle(title: String, withPadding: Boolean = true) {
val padding = if (withPadding)
PaddingValues(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING)
else
PaddingValues(bottom = DEFAULT_PADDING)
Text(
title,
Modifier
.fillMaxWidth()
.padding(padding),
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.h1
)
}
@Composable
fun ColumnScope.AppBarTitleCentered(title: String, withPadding: Boolean = true) {
Text(
title,
Modifier
.padding(bottom = if (withPadding) DEFAULT_PADDING * 1.5f else 0.dp)
.align(Alignment.CenterHorizontally),
.padding(bottom = DEFAULT_PADDING * 1.5f, start = if (withPadding) DEFAULT_PADDING else 0.dp, end = if (withPadding) DEFAULT_PADDING else 0.dp,),
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.h1,
color = MaterialTheme.colors.primary
color = MaterialTheme.colors.primary,
textAlign = TextAlign.Center
)
}
@@ -0,0 +1,143 @@
package chat.simplex.app.views.helpers
import androidx.compose.material.icons.materialIcon
import androidx.compose.material.icons.materialPath
import androidx.compose.ui.graphics.vector.*
val AccountCircleFilled: ImageVector
get() {
if (_accountCircleFilled != null) {
return _accountCircleFilled!!
}
_accountCircleFilled = materialIcon(name = "Filled.AccountCircle") {
materialPath {
moveTo(12.0f, 2.0f)
curveTo(6.48f, 2.0f, 2.0f, 6.48f, 2.0f, 12.0f)
reflectiveCurveToRelative(4.48f, 10.0f, 10.0f, 10.0f)
reflectiveCurveToRelative(10.0f, -4.48f, 10.0f, -10.0f)
reflectiveCurveTo(17.52f, 2.0f, 12.0f, 2.0f)
close()
moveTo(12.0f, 5.0f)
curveToRelative(1.66f, 0.0f, 3.0f, 1.34f, 3.0f, 3.0f)
reflectiveCurveToRelative(-1.34f, 3.0f, -3.0f, 3.0f)
reflectiveCurveToRelative(-3.0f, -1.34f, -3.0f, -3.0f)
reflectiveCurveToRelative(1.34f, -3.0f, 3.0f, -3.0f)
close()
moveTo(12.0f, 19.2f)
curveToRelative(-2.5f, 0.0f, -4.71f, -1.28f, -6.0f, -3.22f)
curveToRelative(0.03f, -1.99f, 4.0f, -3.08f, 6.0f, -3.08f)
curveToRelative(1.99f, 0.0f, 5.97f, 1.09f, 6.0f, 3.08f)
curveToRelative(-1.29f, 1.94f, -3.5f, 3.22f, -6.0f, 3.22f)
close()
}
}
return _accountCircleFilled!!
}
private var _accountCircleFilled: ImageVector? = null
val SupervisedUserCircleFilled: ImageVector
get() {
if (_supervisedUserCircleFilled != null) {
return _supervisedUserCircleFilled!!
}
_supervisedUserCircleFilled = materialIcon(name = "Filled.SupervisedUserCircle") {
materialPath {
moveTo(11.99f, 2.0f)
curveToRelative(-5.52f, 0.0f, -10.0f, 4.48f, -10.0f, 10.0f)
reflectiveCurveToRelative(4.48f, 10.0f, 10.0f, 10.0f)
reflectiveCurveToRelative(10.0f, -4.48f, 10.0f, -10.0f)
reflectiveCurveToRelative(-4.48f, -10.0f, -10.0f, -10.0f)
close()
moveTo(15.6f, 8.34f)
curveToRelative(1.07f, 0.0f, 1.93f, 0.86f, 1.93f, 1.93f)
curveToRelative(0.0f, 1.07f, -0.86f, 1.93f, -1.93f, 1.93f)
curveToRelative(-1.07f, 0.0f, -1.93f, -0.86f, -1.93f, -1.93f)
curveToRelative(-0.01f, -1.07f, 0.86f, -1.93f, 1.93f, -1.93f)
close()
moveTo(9.6f, 6.76f)
curveToRelative(1.3f, 0.0f, 2.36f, 1.06f, 2.36f, 2.36f)
curveToRelative(0.0f, 1.3f, -1.06f, 2.36f, -2.36f, 2.36f)
reflectiveCurveToRelative(-2.36f, -1.06f, -2.36f, -2.36f)
curveToRelative(0.0f, -1.31f, 1.05f, -2.36f, 2.36f, -2.36f)
close()
moveTo(9.6f, 15.89f)
verticalLineToRelative(3.75f)
curveToRelative(-2.4f, -0.75f, -4.3f, -2.6f, -5.14f, -4.96f)
curveToRelative(1.05f, -1.12f, 3.67f, -1.69f, 5.14f, -1.69f)
curveToRelative(0.53f, 0.0f, 1.2f, 0.08f, 1.9f, 0.22f)
curveToRelative(-1.64f, 0.87f, -1.9f, 2.02f, -1.9f, 2.68f)
close()
moveTo(11.99f, 20.0f)
curveToRelative(-0.27f, 0.0f, -0.53f, -0.01f, -0.79f, -0.04f)
verticalLineToRelative(-4.07f)
curveToRelative(0.0f, -1.42f, 2.94f, -2.13f, 4.4f, -2.13f)
curveToRelative(1.07f, 0.0f, 2.92f, 0.39f, 3.84f, 1.15f)
curveToRelative(-1.17f, 2.97f, -4.06f, 5.09f, -7.45f, 5.09f)
close()
}
}
return _supervisedUserCircleFilled!!
}
private var _supervisedUserCircleFilled: ImageVector? = null
val BoltFilled: ImageVector
get() {
if (_boltFilled != null) {
return _boltFilled!!
}
_boltFilled = materialIcon(name = "Filled.Bolt") {
materialPath {
moveTo(11.0f, 21.0f)
horizontalLineToRelative(-1.0f)
lineToRelative(1.0f, -7.0f)
horizontalLineTo(7.5f)
curveToRelative(-0.58f, 0.0f, -0.57f, -0.32f, -0.38f, -0.66f)
curveToRelative(0.19f, -0.34f, 0.05f, -0.08f, 0.07f, -0.12f)
curveTo(8.48f, 10.94f, 10.42f, 7.54f, 13.0f, 3.0f)
horizontalLineToRelative(1.0f)
lineToRelative(-1.0f, 7.0f)
horizontalLineToRelative(3.5f)
curveToRelative(0.49f, 0.0f, 0.56f, 0.33f, 0.47f, 0.51f)
lineToRelative(-0.07f, 0.15f)
curveTo(12.96f, 17.55f, 11.0f, 21.0f, 11.0f, 21.0f)
close()
}
}
return _boltFilled!!
}
private var _boltFilled: ImageVector? = null
val MoreVertFilled: ImageVector
get() {
if (_moreVertFilled != null) {
return _moreVertFilled!!
}
_moreVertFilled = materialIcon(name = "Filled.MoreVert") {
materialPath {
moveTo(12.0f, 8.0f)
curveToRelative(1.1f, 0.0f, 2.0f, -0.9f, 2.0f, -2.0f)
reflectiveCurveToRelative(-0.9f, -2.0f, -2.0f, -2.0f)
reflectiveCurveToRelative(-2.0f, 0.9f, -2.0f, 2.0f)
reflectiveCurveToRelative(0.9f, 2.0f, 2.0f, 2.0f)
close()
moveTo(12.0f, 10.0f)
curveToRelative(-1.1f, 0.0f, -2.0f, 0.9f, -2.0f, 2.0f)
reflectiveCurveToRelative(0.9f, 2.0f, 2.0f, 2.0f)
reflectiveCurveToRelative(2.0f, -0.9f, 2.0f, -2.0f)
reflectiveCurveToRelative(-0.9f, -2.0f, -2.0f, -2.0f)
close()
moveTo(12.0f, 16.0f)
curveToRelative(-1.1f, 0.0f, -2.0f, 0.9f, -2.0f, 2.0f)
reflectiveCurveToRelative(0.9f, 2.0f, 2.0f, 2.0f)
reflectiveCurveToRelative(2.0f, -0.9f, 2.0f, -2.0f)
reflectiveCurveToRelative(-0.9f, -2.0f, -2.0f, -2.0f)
close()
}
}
return _moreVertFilled!!
}
private var _moreVertFilled: ImageVector? = null
@@ -8,10 +8,6 @@ import androidx.compose.foundation.shape.ZeroCornerSize
import androidx.compose.foundation.text.*
import androidx.compose.material.*
import androidx.compose.material.TextFieldDefaults.indicatorLine
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Visibility
import androidx.compose.material.icons.filled.VisibilityOff
import androidx.compose.material.icons.outlined.Error
import androidx.compose.runtime.*
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
@@ -19,11 +15,13 @@ import androidx.compose.ui.focus.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.*
import androidx.compose.ui.text.font.FontWeight
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
@@ -136,8 +134,8 @@ fun DefaultConfigurableTextField(
var valid by remember { mutableStateOf(validKey(state.value.text)) }
var showKey by remember { mutableStateOf(false) }
val icon = if (valid) {
if (showKey) Icons.Filled.VisibilityOff else Icons.Filled.Visibility
} else Icons.Outlined.Error
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
} else Color.Red
@@ -19,7 +19,7 @@ fun DefaultDropdownMenu(
dropdownMenuItems: (@Composable () -> Unit)?
) {
MaterialTheme(
colors = MaterialTheme.colors.copy(surface = if (isInDarkTheme()) Color(0xFF080808) else MaterialTheme.colors.background),
colors = MaterialTheme.colors.copy(surface = if (isInDarkTheme()) Color(0xFF0B0B0B) else MaterialTheme.colors.background),
shapes = MaterialTheme.shapes.copy(medium = RoundedCornerShape(corner = CornerSize(25.dp)))
) {
DropdownMenu(
@@ -0,0 +1,40 @@
package chat.simplex.app.views.helpers
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
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(
checked: Boolean,
onCheckedChange: ((Boolean) -> Unit)?,
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
colors: SwitchColors = SwitchDefaults.colors(
checkedThumbColor = MaterialTheme.colors.primary,
uncheckedThumbColor = HighOrLowlight,
checkedTrackAlpha = 0.0f,
uncheckedTrackAlpha = 0.0f,
)
) {
val color = if (checked) MaterialTheme.colors.primary.copy(alpha = 0.3f) else HighOrLowlight.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() }
Switch(
checked = checked,
onCheckedChange = onCheckedChange,
modifier.drawBehind { drawRoundRect(color, size = size, topLeft = offset, cornerRadius = CornerRadius(radius, radius)) },
colors = colors,
enabled = enabled,
interactionSource = interactionSource,
)
}
@@ -4,12 +4,11 @@ import chat.simplex.app.R
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.*
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import chat.simplex.app.ui.theme.*
@@ -48,7 +47,7 @@ fun DefaultTopAppBar(
fun NavigationButtonBack(onButtonClicked: (() -> Unit)?) {
IconButton(onButtonClicked ?: {}, enabled = onButtonClicked != null) {
Icon(
Icons.Outlined.ArrowBackIos, 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 HighOrLowlight
)
}
}
@@ -57,7 +56,7 @@ fun NavigationButtonBack(onButtonClicked: (() -> Unit)?) {
fun ShareButton(onButtonClicked: () -> Unit) {
IconButton(onButtonClicked) {
Icon(
Icons.Outlined.Share, stringResource(R.string.share_verb), tint = MaterialTheme.colors.primary
painterResource(R.drawable.ic_share), stringResource(R.string.share_verb), tint = MaterialTheme.colors.primary
)
}
}
@@ -66,7 +65,7 @@ fun ShareButton(onButtonClicked: () -> Unit) {
fun NavigationButtonMenu(onButtonClicked: () -> Unit) {
IconButton(onClick = onButtonClicked) {
Icon(
Icons.Outlined.Menu,
painterResource(R.drawable.ic_menu),
stringResource(R.string.icon_descr_settings),
tint = MaterialTheme.colors.primary,
)
@@ -2,20 +2,19 @@ package chat.simplex.app.views.helpers
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ExpandLess
import androidx.compose.material.icons.outlined.ExpandMore
import androidx.compose.ui.res.painterResource
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.app.R
import chat.simplex.app.ui.theme.*
import chat.simplex.app.views.usersettings.SettingsActionItemWithContent
@Composable
fun <T> ExposedDropDownSettingRow(
@@ -23,27 +22,13 @@ fun <T> ExposedDropDownSettingRow(
values: List<Pair<T, String>>,
selection: State<T>,
label: String? = null,
icon: ImageVector? = null,
icon: Painter? = null,
iconTint: Color = HighOrLowlight,
enabled: State<Boolean> = mutableStateOf(true),
onSelected: (T) -> Unit
) {
Row(
Modifier.fillMaxWidth().padding(vertical = 10.dp),
verticalAlignment = Alignment.CenterVertically,
) {
SettingsActionItemWithContent(icon, title, iconColor = iconTint, disabled = !enabled.value) {
val expanded = remember { mutableStateOf(false) }
if (icon != null) {
Icon(
icon,
"",
Modifier.padding(end = 8.dp),
tint = iconTint
)
}
Text(title, Modifier.weight(1f), color = if (enabled.value) Color.Unspecified else HighOrLowlight)
ExposedDropdownMenuBox(
expanded = expanded.value,
onExpandedChange = {
@@ -55,7 +40,7 @@ fun <T> ExposedDropDownSettingRow(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
) {
val maxWidth = with(LocalDensity.current){ 180.sp.toDp() }
val maxWidth = with(LocalDensity.current) { 180.sp.toDp() }
Text(
values.first { it.first == selection.value }.second + (if (label != null) " $label" else ""),
Modifier.widthIn(max = maxWidth),
@@ -65,7 +50,7 @@ fun <T> ExposedDropDownSettingRow(
)
Spacer(Modifier.size(12.dp))
Icon(
if (!expanded.value) Icons.Outlined.ExpandMore else Icons.Outlined.ExpandLess,
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
)
@@ -17,14 +17,13 @@ import androidx.activity.result.contract.ActivityResultContract
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.CallSuper
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
@@ -247,7 +246,7 @@ fun GetImageBottomSheet(
.padding(horizontal = 8.dp, vertical = 30.dp),
horizontalArrangement = Arrangement.SpaceEvenly
) {
ActionButton(null, stringResource(R.string.use_camera_button), icon = Icons.Outlined.PhotoCamera) {
ActionButton(null, stringResource(R.string.use_camera_button), icon = painterResource(R.drawable.ic_photo_camera)) {
when (PackageManager.PERMISSION_GRANTED) {
ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) -> {
cameraLauncher.launchWithFallback()
@@ -258,7 +257,7 @@ fun GetImageBottomSheet(
}
}
}
ActionButton(null, stringResource(R.string.from_gallery_button), icon = Icons.Outlined.Image) {
ActionButton(null, stringResource(R.string.from_gallery_button), icon = painterResource(R.drawable.ic_image)) {
try {
galleryLauncher.launch(0)
} catch (e: ActivityNotFoundException) {
@@ -6,13 +6,12 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Close
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
@@ -112,7 +111,7 @@ fun ComposeLinkView(linkPreview: LinkPreview?, cancelPreview: () -> Unit) {
}
IconButton(onClick = cancelPreview, modifier = Modifier.padding(0.dp)) {
Icon(
Icons.Outlined.Close,
painterResource(R.drawable.ic_close),
contentDescription = stringResource(R.string.icon_descr_cancel_link_preview),
tint = MaterialTheme.colors.primary,
modifier = Modifier.padding(10.dp)
@@ -1,6 +1,7 @@
package chat.simplex.app.views.helpers
import android.os.Build.VERSION.SDK_INT
import androidx.activity.compose.BackHandler
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricManager.Authenticators.*
import androidx.biometric.BiometricPrompt
@@ -53,6 +54,10 @@ fun authenticate(
LAMode.PASSCODE -> {
val password = ksAppPassword.get() ?: return completed(LAResult.Unavailable(generalGetString(R.string.la_no_app_password)))
ModalManager.shared.showCustomModal(animated = false) { close ->
BackHandler {
close()
completed(LAResult.Error(generalGetString(R.string.authentication_cancelled)))
}
Surface(Modifier.fillMaxSize(), color = MaterialTheme.colors.background) {
LocalAuthView(SimplexApp.context.chatModel, LocalAuthRequest(promptTitle, promptSubtitle, password) {
close()
@@ -11,7 +11,6 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import chat.simplex.app.TAG
import chat.simplex.app.ui.theme.SettingsBackgroundLight
import chat.simplex.app.ui.theme.isInDarkTheme
import java.util.concurrent.atomic.AtomicBoolean
@@ -40,13 +39,13 @@ class ModalManager {
fun showModal(settings: Boolean = false, endButtons: @Composable RowScope.() -> Unit = {}, content: @Composable () -> Unit) {
showCustomModal { close ->
ModalView(close, if (!settings || isInDarkTheme()) MaterialTheme.colors.background else SettingsBackgroundLight, endButtons = endButtons, content = content)
ModalView(close, endButtons = endButtons, content = content)
}
}
fun showModalCloseable(settings: Boolean = false, content: @Composable (close: () -> Unit) -> Unit) {
showCustomModal { close ->
ModalView(close, if (!settings || isInDarkTheme()) MaterialTheme.colors.background else SettingsBackgroundLight, content = { content(close) })
ModalView(close, content = { content(close) })
}
}
@@ -8,8 +8,6 @@ import androidx.compose.foundation.text.*
import androidx.compose.material.*
import androidx.compose.material.TextFieldDefaults.indicatorLine
import androidx.compose.material.TextFieldDefaults.textFieldWithLabelPadding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.ExperimentalComposeUiApi
@@ -19,6 +17,7 @@ import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.*
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
@@ -99,7 +98,7 @@ fun SearchTextField(modifier: Modifier, placeholder: String, alwaysVisible: Bool
searchText = TextFieldValue("");
onValueChange("")
}) {
Icon(Icons.Default.Close, stringResource(R.string.icon_descr_close_button), tint = MaterialTheme.colors.primary,)
Icon(painterResource(R.drawable.ic_close), stringResource(R.string.icon_descr_close_button), tint = MaterialTheme.colors.primary,)
}
}} else null,
singleLine = true,
@@ -3,21 +3,22 @@ import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Check
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.*
import chat.simplex.app.R
import chat.simplex.app.ui.theme.*
import chat.simplex.app.views.helpers.ValueTitleDesc
import chat.simplex.app.views.helpers.ValueTitle
import chat.simplex.app.views.usersettings.SettingsActionItemWithContent
@Composable
fun SectionView(title: String? = null, padding: PaddingValues = PaddingValues(), content: (@Composable ColumnScope.() -> Unit)) {
@@ -28,16 +29,14 @@ fun SectionView(title: String? = null, padding: PaddingValues = PaddingValues(),
modifier = Modifier.padding(start = DEFAULT_PADDING, bottom = 5.dp), fontSize = 12.sp
)
}
Surface(color = if (isInDarkTheme()) GroupDark else MaterialTheme.colors.background) {
Column(Modifier.padding(padding).fillMaxWidth()) { content() }
}
Column(Modifier.padding(padding).fillMaxWidth()) { content() }
}
}
@Composable
fun SectionView(
title: String,
icon: ImageVector,
icon: Painter,
iconTint: Color = HighOrLowlight,
leadingIcon: Boolean = false,
padding: PaddingValues = PaddingValues(),
@@ -46,13 +45,11 @@ fun SectionView(
Column {
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 = 4.dp).size(iconSize), tint = iconTint)
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)
if (!leadingIcon) Icon(icon, null, Modifier.padding(start = 4.dp).size(iconSize), tint = iconTint)
}
Surface(color = if (isInDarkTheme()) GroupDark else MaterialTheme.colors.background) {
Column(Modifier.padding(padding).fillMaxWidth()) { content() }
if (!leadingIcon) Icon(icon, null, Modifier.padding(start = DEFAULT_PADDING_HALF).size(iconSize), tint = iconTint)
}
Column(Modifier.padding(padding).fillMaxWidth()) { content() }
}
}
@@ -70,7 +67,7 @@ fun <T> SectionViewSelectable(
SectionItemViewSpaceBetween({ onSelected(item.value) }) {
Text(item.title)
if (currentValue.value == item.value) {
Icon(Icons.Outlined.Check, item.title, tint = MaterialTheme.colors.primary)
Icon(painterResource(R.drawable.ic_check), item.title, tint = MaterialTheme.colors.primary)
}
}
Spacer(Modifier.padding(horizontal = 4.dp))
@@ -85,7 +82,30 @@ fun SectionItemView(
click: (() -> Unit)? = null,
minHeight: Dp = 46.dp,
disabled: Boolean = false,
padding: PaddingValues = PaddingValues(horizontal = DEFAULT_PADDING),
extraPadding: Boolean = false,
padding: PaddingValues = if (extraPadding)
PaddingValues(start = DEFAULT_PADDING * 1.7f, end = DEFAULT_PADDING)
else
PaddingValues(horizontal = DEFAULT_PADDING),
content: (@Composable RowScope.() -> Unit)
) {
val modifier = Modifier
.fillMaxWidth()
.sizeIn(minHeight = minHeight)
Row(
if (click == null || disabled) modifier.padding(padding) else modifier.clickable(onClick = click).padding(padding),
verticalAlignment = Alignment.CenterVertically
) {
content()
}
}
@Composable
fun SectionItemViewWithIcon(
click: (() -> Unit)? = null,
minHeight: Dp = 46.dp,
disabled: Boolean = false,
padding: PaddingValues = PaddingValues(start = DEFAULT_PADDING * 1.7f, end = DEFAULT_PADDING),
content: (@Composable RowScope.() -> Unit)
) {
val modifier = Modifier
@@ -126,24 +146,12 @@ fun <T> SectionItemWithValue(
currentValue: State<T>,
values: List<ValueTitle<T>>,
label: String? = null,
icon: ImageVector? = null,
icon: Painter? = null,
iconTint: Color = HighOrLowlight,
enabled: State<Boolean> = mutableStateOf(true),
onSelected: () -> Unit
) {
SectionItemView(click = if (enabled.value) onSelected else null) {
if (icon != null) {
Icon(
icon,
title,
Modifier.padding(end = 8.dp),
tint = iconTint
)
}
Text(title, color = if (enabled.value) Color.Unspecified else HighOrLowlight)
Spacer(Modifier.fillMaxWidth().weight(1f))
SettingsActionItemWithContent(icon = icon, text = title, iconColor = iconTint, click = if (enabled.value) onSelected else null, disabled = !enabled.value) {
Row(
Modifier.padding(start = 10.dp),
verticalAlignment = Alignment.CenterVertically,
@@ -189,13 +197,34 @@ fun SectionDivider() {
Divider(Modifier.padding(horizontal = 8.dp))
}
@Composable
fun SectionDividerSpaced(maxTopPadding: Boolean = false, maxBottomPadding: Boolean = true) {
Divider(
Modifier.padding(
start = DEFAULT_PADDING_HALF,
top = if (maxTopPadding) 40.dp else 30.dp,
end = DEFAULT_PADDING_HALF,
bottom = if (maxBottomPadding) 40.dp else 30.dp)
)
}
@Composable
fun SectionSpacer() {
Spacer(Modifier.height(30.dp))
}
@Composable
fun InfoRow(title: String, value: String, icon: ImageVector? = null, iconTint: Color? = null) {
fun SectionBottomSpacer() {
Spacer(Modifier.height(DEFAULT_BOTTOM_PADDING))
}
@Composable
fun TextIconSpaced(extraPadding: Boolean = false) {
Spacer(Modifier.padding(horizontal = if (extraPadding) 17.dp else DEFAULT_PADDING_HALF))
}
@Composable
fun InfoRow(title: String, value: String, icon: Painter? = null, iconTint: Color? = null) {
SectionItemViewSpaceBetween {
Row {
val iconSize = with(LocalDensity.current) { 21.sp.toDp() }
@@ -5,20 +5,20 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Share
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.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import chat.simplex.app.R
@Composable
fun SimpleButton(text: String, icon: ImageVector,
fun SimpleButton(text: String, icon: Painter,
color: Color = MaterialTheme.colors.primary,
click: () -> Unit) {
SimpleButtonFrame(click) {
@@ -31,7 +31,7 @@ fun SimpleButton(text: String, icon: ImageVector,
}
@Composable
fun SimpleButtonDecorated(text: String, icon: ImageVector,
fun SimpleButtonDecorated(text: String, icon: Painter,
color: Color = MaterialTheme.colors.primary,
textDecoration: TextDecoration = TextDecoration.Underline,
fontWeight: FontWeight = FontWeight.Normal,
@@ -47,7 +47,7 @@ fun SimpleButtonDecorated(text: String, icon: ImageVector,
@Composable
fun SimpleButton(
text: String, icon: ImageVector,
text: String, icon: Painter,
color: Color = MaterialTheme.colors.primary,
disabled: Boolean,
click: () -> Unit
@@ -64,7 +64,7 @@ fun SimpleButton(
@Composable
fun SimpleButtonIconEnded(
text: String,
icon: ImageVector,
icon: Painter,
color: Color = MaterialTheme.colors.primary,
click: () -> Unit
) {
@@ -92,6 +92,6 @@ fun SimpleButtonFrame(click: () -> Unit, modifier: Modifier = Modifier, disabled
@Composable
fun PreviewCloseSheetBar() {
SimpleXTheme {
SimpleButton(text = "Share", icon = Icons.Outlined.Share, click = {})
SimpleButton(text = "Share", icon = painterResource(R.drawable.ic_share), click = {})
}
}
@@ -557,7 +557,7 @@ fun getBitmapFromVideo(uri: Uri, timestamp: Long? = null, random: Boolean = true
val image = when {
timestamp != null -> mmr.getFrameAtTime(timestamp * 1000, MediaMetadataRetriever.OPTION_CLOSEST)
random -> mmr.frameAtTime
else -> mmr.getFrameAtIndex(0)
else -> mmr.getFrameAtTime(0)
}
mmr.release()
return VideoPlayer.PreviewAndDuration(image, durationMs, timestamp ?: 0)
@@ -86,7 +86,7 @@ class VideoPlayer private constructor(
.build()
.apply {
// Repeat the same track endlessly
repeatMode = 1
repeatMode = Player.REPEAT_MODE_ONE
currentVolume = volume
if (!soundEnabled) {
volume = 0f
@@ -4,13 +4,11 @@ import android.content.res.Configuration
import androidx.compose.foundation.layout.*
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Done
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import chat.simplex.app.R
import chat.simplex.app.ui.theme.DEFAULT_PADDING
@@ -41,9 +39,9 @@ fun PasscodeView(
}
PasscodeEntry(passcode, true)
Row {
SimpleButton(generalGetString(R.string.cancel_verb), icon = Icons.Default.Close, click = cancel)
SimpleButton(generalGetString(R.string.cancel_verb), icon = painterResource(R.drawable.ic_close), click = cancel)
Spacer(Modifier.size(20.dp))
SimpleButton(submitLabel, icon = Icons.Default.Done, disabled = submitEnabled?.invoke(passcode.value) == false || passcode.value.length < 4, click = submit)
SimpleButton(submitLabel, icon = painterResource(R.drawable.ic_done_filled), disabled = submitEnabled?.invoke(passcode.value) == false || passcode.value.length < 4, click = submit)
}
}
}
@@ -84,8 +82,8 @@ fun PasscodeView(
Modifier.padding(start = 30.dp).height(s * 3),
verticalArrangement = Arrangement.SpaceEvenly
) {
SimpleButton(generalGetString(R.string.cancel_verb), icon = Icons.Default.Close, click = cancel)
SimpleButton(submitLabel, icon = Icons.Default.Done, disabled = submitEnabled?.invoke(passcode.value) == false || passcode.value.length < 4, click = submit)
SimpleButton(generalGetString(R.string.cancel_verb), icon = painterResource(R.drawable.ic_close), click = cancel)
SimpleButton(submitLabel, icon = painterResource(R.drawable.ic_done_filled), disabled = submitEnabled?.invoke(passcode.value) == false || passcode.value.length < 4, click = submit)
}
}
}
@@ -5,20 +5,18 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.outlined.Backspace
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import chat.simplex.app.R
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.vector.ImageVector
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.model.ChatModel
import chat.simplex.app.ui.theme.HighOrLowlight
@Composable
@@ -59,13 +57,13 @@ private fun BoxWithConstraintsScope.VerticalPasswordGrid(password: MutableState<
DigitsRow(s, 7, 8, 9, password)
Divider()
Row(Modifier.requiredHeight(s)) {
PasswordEdit(s, Icons.Default.Close) {
PasswordEdit(s, painterResource(R.drawable.ic_close)) {
password.value = ""
}
VerticalDivider()
PasswordDigit(s, 0, password)
VerticalDivider()
PasswordEdit(s, Icons.Outlined.Backspace) {
PasswordEdit(s, painterResource(R.drawable.ic_backspace)) {
password.value = password.value.dropLast(1)
}
}
@@ -79,7 +77,7 @@ private fun BoxWithConstraintsScope.HorizontalPasswordGrid(password: MutableStat
Row(Modifier.height(IntrinsicSize.Min)) {
DigitsRow(s, 1, 2, 3, password);
VerticalDivider()
PasswordEdit(s, Icons.Default.Close) {
PasswordEdit(s, painterResource(R.drawable.ic_close)) {
password.value = ""
}
}
@@ -93,7 +91,7 @@ private fun BoxWithConstraintsScope.HorizontalPasswordGrid(password: MutableStat
Row(Modifier.height(IntrinsicSize.Min)) {
DigitsRow(s, 7, 8, 9, password)
VerticalDivider()
PasswordEdit(s, Icons.Outlined.Backspace) {
PasswordEdit(s, painterResource(R.drawable.ic_backspace)) {
password.value = password.value.dropLast(1)
}
}
@@ -137,7 +135,7 @@ private fun PasswordDigit(size: Dp, d: Int, password: MutableState<String>) {
}
@Composable
private fun PasswordEdit(size: Dp, image: ImageVector, action: () -> Unit) {
private fun PasswordEdit(size: Dp, image: Painter, action: () -> Unit) {
PasswordButton(size, action) {
Icon(image, null, tint = HighOrLowlight)
}
@@ -1,5 +1,6 @@
package chat.simplex.app.views.localauth
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import chat.simplex.app.R
@@ -18,6 +19,10 @@ fun SetAppPasscodeView(
@Composable
fun SetPasswordView(title: String, submitLabel: String, submitEnabled: (((String) -> Boolean))? = null, submit: () -> Unit) {
BackHandler {
close()
cancel()
}
PasscodeView(passcode, title = title, submitLabel = submitLabel, submitEnabled = submitEnabled, submit = submit) {
close()
cancel()
@@ -1,14 +1,12 @@
package chat.simplex.app.views.newchat
import SectionBottomSpacer
import android.content.res.Configuration
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.TheaterComedy
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material.icons.outlined.Share
import androidx.compose.ui.res.painterResource
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -19,7 +17,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.app.R
import chat.simplex.app.model.ChatModel
import chat.simplex.app.ui.theme.*
import chat.simplex.app.views.helpers.*
@@ -39,8 +36,7 @@ fun AddContactLayout(connReq: String, connIncognito: Boolean, share: () -> Unit)
val screenHeight = maxHeight
Column(
Modifier
.verticalScroll(rememberScrollState())
.padding(bottom = 16.dp),
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.SpaceBetween,
) {
AppBarTitle(stringResource(R.string.add_contact), false)
@@ -75,14 +71,15 @@ fun AddContactLayout(connReq: String, connIncognito: Boolean, share: () -> Unit)
annotatedStringResource(R.string.if_you_cannot_meet_in_person_show_QR_in_video_call_or_via_another_channel),
lineHeight = 22.sp,
modifier = Modifier
.padding(top = 16.dp, bottom = if (screenHeight > 600.dp) 16.dp else 0.dp)
.padding(top = DEFAULT_PADDING, bottom = if (screenHeight > 600.dp) DEFAULT_PADDING else 0.dp)
)
Row(
Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
SimpleButton(stringResource(R.string.share_invitation_link), icon = Icons.Outlined.Share, click = share)
SimpleButton(stringResource(R.string.share_invitation_link), icon = painterResource(R.drawable.ic_share), click = share)
}
SectionBottomSpacer()
}
}
}
@@ -98,7 +95,7 @@ fun InfoAboutIncognito(chatModelIncognito: Boolean, supportedIncognito: Boolean
horizontalArrangement = if (centered) Arrangement.Center else Arrangement.Start
) {
Icon(
if (supportedIncognito) Icons.Filled.TheaterComedy else Icons.Outlined.Info,
if (supportedIncognito) painterResource(R.drawable.ic_theater_comedy_filled) else painterResource(R.drawable.ic_info),
stringResource(R.string.incognito),
tint = if (supportedIncognito) Indigo else WarningOrange,
modifier = Modifier.padding(end = 10.dp).size(20.dp)
@@ -114,7 +111,7 @@ fun InfoAboutIncognito(chatModelIncognito: Boolean, supportedIncognito: Boolean
horizontalArrangement = if (centered) Arrangement.Center else Arrangement.Start
) {
Icon(
Icons.Outlined.Info,
painterResource(R.drawable.ic_info),
stringResource(R.string.incognito),
tint = HighOrLowlight,
modifier = Modifier.padding(end = 10.dp).size(20.dp)
@@ -5,14 +5,13 @@ import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowForwardIos
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.graphics.Color
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.TextAlign
@@ -89,7 +88,7 @@ fun AddGroupLayout(createGroup: (GroupProfile) -> Unit, close: () -> Unit) {
.verticalScroll(rememberScrollState())
.padding(horizontal = DEFAULT_PADDING)
) {
AppBarTitleCentered(stringResource(R.string.create_secret_group_title))
AppBarTitle(stringResource(R.string.create_secret_group_title))
ReadableText(R.string.group_is_decentralized, TextAlign.Center)
Box(
Modifier
@@ -163,7 +162,7 @@ fun CreateGroupButton(color: Color, modifier: Modifier) {
Surface(shape = RoundedCornerShape(20.dp)) {
Row(modifier, verticalAlignment = Alignment.CenterVertically) {
Text(stringResource(R.string.create_profile_button), style = MaterialTheme.typography.caption, color = color, fontWeight = FontWeight.Bold)
Icon(Icons.Outlined.ArrowForwardIos, stringResource(R.string.create_profile_button), tint = color)
Icon(painterResource(R.drawable.ic_arrow_forward_ios), stringResource(R.string.create_profile_button), tint = color)
}
}
}
@@ -2,10 +2,9 @@ package chat.simplex.app.views.newchat
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.sp
import chat.simplex.app.R
@@ -58,7 +57,7 @@ fun ConnectViaLinkView(m: ChatModel, close: () -> Unit) {
text = { Text(it, fontSize = 13.sp) },
icon = {
Icon(
if (ConnectViaLinkTab.SCAN.ordinal == index) Icons.Outlined.QrCode else Icons.Outlined.Article,
if (ConnectViaLinkTab.SCAN.ordinal == index) painterResource(R.drawable.ic_qr_code) else painterResource(R.drawable.ic_article),
it
)
},
@@ -1,17 +1,16 @@
package chat.simplex.app.views.newchat
import SectionDivider
import SectionBottomSpacer
import SectionView
import android.content.res.Configuration
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
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.tooling.preview.Preview
import chat.simplex.app.R
@@ -101,17 +100,17 @@ private fun ContactConnectionInfoLayout(
SectionView {
if (!connReq.isNullOrEmpty() && contactConnection.initiated) {
ShowQrButton(contactConnection.incognito, showQr)
SectionDivider()
}
DeleteButton(deleteConnection)
}
SectionBottomSpacer()
}
}
@Composable
fun ShowQrButton(incognito: Boolean, onClick: () -> Unit) {
SettingsActionItem(
Icons.Outlined.QrCode,
painterResource(R.drawable.ic_qr_code),
stringResource(R.string.show_QR_code),
click = onClick,
textColor = if (incognito) Indigo else MaterialTheme.colors.primary,
@@ -122,7 +121,7 @@ fun ShowQrButton(incognito: Boolean, onClick: () -> Unit) {
@Composable
fun DeleteButton(onClick: () -> Unit) {
SettingsActionItem(
Icons.Outlined.Delete,
painterResource(R.drawable.ic_delete),
stringResource(R.string.delete_verb),
click = onClick,
textColor = Color.Red,
@@ -2,11 +2,10 @@ package chat.simplex.app.views.newchat
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.sp
import chat.simplex.app.R
@@ -80,7 +79,7 @@ fun CreateLinkView(m: ChatModel, initialSelection: CreateLinkTab) {
text = { Text(it, fontSize = 13.sp) },
icon = {
Icon(
if (CreateLinkTab.ONE_TIME.ordinal == index) Icons.Outlined.RepeatOne else Icons.Outlined.AllInclusive,
if (CreateLinkTab.ONE_TIME.ordinal == index) painterResource(R.drawable.ic_repeat_one) else painterResource(R.drawable.ic_all_inclusive),
it
)
},
@@ -9,18 +9,14 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.*
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.TextAlign
@@ -60,7 +56,7 @@ fun NewChatSheet(chatModel: ChatModel, newChatSheetState: StateFlow<AnimatedView
}
private val titles = listOf(R.string.share_one_time_link, R.string.connect_via_link_or_qr, R.string.create_group)
private val icons = listOf(Icons.Outlined.AddLink, Icons.Outlined.QrCode, Icons.Outlined.Group)
private val icons = listOf(R.drawable.ic_add_link, R.drawable.ic_qr_code, R.drawable.ic_group)
@Composable
private fun NewChatSheetLayout(
@@ -133,7 +129,7 @@ private fun NewChatSheetLayout(
fontWeight = FontWeight.Medium,
)
Icon(
icons[index],
painterResource(icons[index]),
stringResource(titles[index]),
Modifier.size(42.dp),
tint = if (isInDarkTheme()) MaterialTheme.colors.primary else MaterialTheme.colors.primary
@@ -147,7 +143,7 @@ private fun NewChatSheetLayout(
}
FloatingActionButton(
onClick = { if (!stopped) closeNewChatSheet(true) },
Modifier.padding(end = 16.dp, bottom = 16.dp),
Modifier.padding(end = DEFAULT_PADDING, bottom = DEFAULT_PADDING),
elevation = FloatingActionButtonDefaults.elevation(
defaultElevation = 0.dp,
pressedElevation = 0.dp,
@@ -158,11 +154,11 @@ private fun NewChatSheetLayout(
contentColor = Color.White
) {
Icon(
Icons.Default.Edit, stringResource(R.string.add_contact_or_create_group),
painterResource(R.drawable.ic_edit_filled), stringResource(R.string.add_contact_or_create_group),
Modifier.graphicsLayer { alpha = 1 - animatedFloat.value }
)
Icon(
Icons.Default.Close, stringResource(R.string.add_contact_or_create_group),
painterResource(R.drawable.ic_close), stringResource(R.string.add_contact_or_create_group),
Modifier.graphicsLayer { alpha = animatedFloat.value }
)
}
@@ -173,7 +169,7 @@ private fun NewChatSheetLayout(
fun ActionButton(
text: String?,
comment: String?,
icon: ImageVector,
icon: Painter,
disabled: Boolean = false,
click: () -> Unit = {}
) {
@@ -1,5 +1,6 @@
package chat.simplex.app.views.newchat
import SectionBottomSpacer
import android.content.ClipboardManager
import android.content.res.Configuration
import android.net.Uri
@@ -8,11 +9,10 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -84,7 +84,7 @@ fun PasteToConnectLayout(
generalGetString(R.string.profile_will_be_sent_to_contact_sending_link)
)
Box(Modifier.padding(top = 16.dp, bottom = 6.dp)) {
Box(Modifier.padding(top = DEFAULT_PADDING, bottom = 6.dp)) {
TextEditor(Modifier.height(180.dp), text = connectionLink)
}
@@ -93,21 +93,22 @@ fun PasteToConnectLayout(
horizontalArrangement = Arrangement.Start,
) {
if (connectionLink.value == "") {
SimpleButton(text = stringResource(R.string.paste_button), icon = Icons.Outlined.ContentPaste) {
SimpleButton(text = stringResource(R.string.paste_button), icon = painterResource(R.drawable.ic_content_paste)) {
pasteFromClipboard()
}
} else {
SimpleButton(text = stringResource(R.string.clear_verb), icon = Icons.Outlined.Clear) {
SimpleButton(text = stringResource(R.string.clear_verb), icon = painterResource(R.drawable.ic_close)) {
connectionLink.value = ""
}
}
Spacer(Modifier.weight(1f).fillMaxWidth())
SimpleButton(text = stringResource(R.string.connect_button), icon = Icons.Outlined.Link) {
SimpleButton(text = stringResource(R.string.connect_button), icon = painterResource(R.drawable.ic_link)) {
connectViaLink(connectionLink.value)
}
}
Text(annotatedStringResource(R.string.you_can_also_connect_by_clicking_the_link))
SectionBottomSpacer()
}
}
@@ -1,5 +1,6 @@
package chat.simplex.app.views.newchat
import SectionBottomSpacer
import android.Manifest
import android.content.res.Configuration
import android.net.Uri
@@ -141,6 +142,7 @@ fun ConnectContactLayout(chatModelIncognito: Boolean, qrCodeScanner: @Composable
annotatedStringResource(R.string.if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link),
lineHeight = 22.sp
)
SectionBottomSpacer()
}
}
@@ -26,7 +26,6 @@ fun HowItWorks(user: User?, onboardingStage: MutableState<OnboardingStage?>? = n
Column(Modifier
.fillMaxWidth()
.padding(horizontal = DEFAULT_PADDING),
horizontalAlignment = Alignment.Start
) {
AppBarTitle(stringResource(R.string.how_simplex_works), false)
ReadableText(R.string.many_people_asked_how_can_it_deliver)
@@ -47,7 +46,7 @@ fun HowItWorks(user: User?, onboardingStage: MutableState<OnboardingStage?>? = n
Spacer(Modifier.fillMaxHeight().weight(1f))
if (onboardingStage != null) {
Box(Modifier.fillMaxWidth().padding(bottom = 16.dp), contentAlignment = Alignment.Center) {
Box(Modifier.fillMaxWidth().padding(bottom = DEFAULT_PADDING), contentAlignment = Alignment.Center) {
OnboardingActionButton(user, onboardingStage, onclick = { ModalManager.shared.closeModal() })
}
Spacer(Modifier.fillMaxHeight().weight(1f))
@@ -10,10 +10,8 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.app.R
@@ -32,7 +30,7 @@ fun SetNotificationsMode(m: ChatModel) {
.padding(vertical = 14.dp)
) {
//CloseSheetBar(null)
AppBarTitleCentered(stringResource(R.string.onboarding_notifications_mode_title))
AppBarTitle(stringResource(R.string.onboarding_notifications_mode_title))
val currentMode = rememberSaveable { mutableStateOf(NotificationsMode.default) }
Column(Modifier.padding(horizontal = DEFAULT_PADDING * 1f)) {
Text(stringResource(R.string.onboarding_notifications_mode_subtitle), Modifier.fillMaxWidth(), textAlign = TextAlign.Center)
@@ -42,7 +40,7 @@ fun SetNotificationsMode(m: ChatModel) {
NotificationButton(currentMode, NotificationsMode.SERVICE, R.string.onboarding_notifications_mode_service, R.string.onboarding_notifications_mode_service_desc)
}
Spacer(Modifier.fillMaxHeight().weight(1f))
Box(Modifier.fillMaxWidth().padding(bottom = 16.dp), contentAlignment = Alignment.Center) {
Box(Modifier.fillMaxWidth().padding(bottom = DEFAULT_PADDING), contentAlignment = Alignment.Center) {
OnboardingActionButton(R.string.use_chat, OnboardingStage.OnboardingComplete, m.onboardingStage, false) {
changeNotificationsMode(currentMode.value, m)
}
@@ -6,9 +6,6 @@ import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowForwardIos
import androidx.compose.material.icons.outlined.Info
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.ui.Alignment
@@ -71,7 +68,7 @@ fun SimpleXInfoLayout(
.fillMaxWidth()
.padding(bottom = DEFAULT_PADDING, top = DEFAULT_PADDING), contentAlignment = Alignment.Center
) {
SimpleButtonDecorated(text = stringResource(R.string.how_it_works), icon = Icons.Outlined.Info,
SimpleButtonDecorated(text = stringResource(R.string.how_it_works), icon = painterResource(R.drawable.ic_info),
click = showModal { HowItWorks(user, onboardingStage) })
}
Spacer(Modifier.weight(1f))
@@ -95,7 +92,7 @@ private fun InfoRow(icon: Painter, @StringRes titleId: Int, @StringRes textId: I
Image(icon, contentDescription = null, modifier = Modifier
.width(width)
.padding(top = 8.dp, start = 8.dp, end = 24.dp))
Column(horizontalAlignment = Alignment.Start) {
Column {
Text(stringResource(titleId), fontWeight = FontWeight.Bold, style = MaterialTheme.typography.h3, lineHeight = 24.sp)
Text(stringResource(textId), lineHeight = 24.sp, style = MaterialTheme.typography.body1)
}
@@ -136,9 +133,9 @@ fun OnboardingActionButton(
}, modifier) {
Text(stringResource(labelId), style = MaterialTheme.typography.h2, color = MaterialTheme.colors.primary, fontSize = 20.sp)
Icon(
Icons.Outlined.ArrowForwardIos, "next stage", tint = MaterialTheme.colors.primary,
painterResource(R.drawable.ic_arrow_forward_ios), "next stage", tint = MaterialTheme.colors.primary,
modifier = Modifier
.padding(start = 16.dp, top = 5.dp)
.padding(start = DEFAULT_PADDING, top = 5.dp)
.size(15.dp)
)
}
@@ -1,17 +1,17 @@
package chat.simplex.app.views.onboarding
import android.content.res.Configuration
import androidx.annotation.IntegerRes
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalUriHandler
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.TextAlign
@@ -28,19 +28,18 @@ fun WhatsNewView(viaSettings: Boolean = false, close: () -> Unit) {
val currentVersion = remember { mutableStateOf(versionDescriptions.lastIndex) }
@Composable
fun featureDescription(icon: ImageVector, titleId: Int, descrId: Int, link: String?) {
fun featureDescription(icon: Painter, titleId: Int, descrId: Int, link: String?) {
@Composable
fun linkButton(link: String) {
val uriHandler = LocalUriHandler.current
Icon(
Icons.Outlined.OpenInNew, stringResource(titleId), tint = MaterialTheme.colors.primary,
painterResource(R.drawable.ic_open_in_new), stringResource(titleId), tint = MaterialTheme.colors.primary,
modifier = Modifier
.clickable { uriHandler.openUriCatching(link) }
)
}
Column(
horizontalAlignment = Alignment.Start,
modifier = Modifier.padding(bottom = 12.dp)
) {
Row(
@@ -68,7 +67,7 @@ fun WhatsNewView(viaSettings: Boolean = false, close: () -> Unit) {
fun pagination() {
Row(
Modifier
.padding(bottom = 16.dp)
.padding(bottom = DEFAULT_PADDING)
) {
if (currentVersion.value > 0) {
val prev = currentVersion.value - 1
@@ -80,7 +79,7 @@ fun WhatsNewView(viaSettings: Boolean = false, close: () -> Unit) {
.clickable { currentVersion.value = prev }
.padding(8.dp)
) {
Icon(Icons.Outlined.ArrowBackIosNew, "previous", tint = MaterialTheme.colors.primary)
Icon(painterResource(R.drawable.ic_arrow_back_ios_new), "previous", tint = MaterialTheme.colors.primary)
Text(versionDescriptions[prev].version, color = MaterialTheme.colors.primary)
}
}
@@ -97,7 +96,7 @@ fun WhatsNewView(viaSettings: Boolean = false, close: () -> Unit) {
.padding(8.dp)
) {
Text(versionDescriptions[next].version, color = MaterialTheme.colors.primary)
Icon(Icons.Outlined.ArrowForwardIos, "next", tint = MaterialTheme.colors.primary)
Icon(painterResource(R.drawable.ic_arrow_forward_ios), "next", tint = MaterialTheme.colors.primary)
}
}
}
@@ -112,8 +111,7 @@ fun WhatsNewView(viaSettings: Boolean = false, close: () -> Unit) {
.fillMaxSize()
.padding(horizontal = DEFAULT_PADDING)
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.spacedBy(16.dp)
verticalArrangement = Arrangement.spacedBy(DEFAULT_PADDING)
) {
Text(
String.format(generalGetString(R.string.new_in_version), v.version),
@@ -128,7 +126,7 @@ fun WhatsNewView(viaSettings: Boolean = false, close: () -> Unit) {
)
v.features.forEach { feature ->
featureDescription(feature.icon, feature.titleId, feature.descrId, feature.link)
featureDescription(painterResource(feature.icon), feature.titleId, feature.descrId, feature.link)
}
if (!viaSettings) {
@@ -154,7 +152,7 @@ fun WhatsNewView(viaSettings: Boolean = false, close: () -> Unit) {
}
private data class FeatureDescription(
val icon: ImageVector,
val icon: Int,
val titleId: Int,
val descrId: Int,
val link: String? = null
@@ -170,18 +168,18 @@ private val versionDescriptions: List<VersionDescription> = listOf(
version = "v4.2",
features = listOf(
FeatureDescription(
icon = Icons.Outlined.VerifiedUser,
icon = R.drawable.ic_verified_user,
titleId = R.string.v4_2_security_assessment,
descrId = R.string.v4_2_security_assessment_desc,
link = "https://simplex.chat/blog/20221108-simplex-chat-v4.2-security-audit-new-website.html"
),
FeatureDescription(
icon = Icons.Outlined.Group,
icon = R.drawable.ic_group,
titleId = R.string.v4_2_group_links,
descrId = R.string.v4_2_group_links_desc
),
FeatureDescription(
icon = Icons.Outlined.Check,
icon = R.drawable.ic_check,
titleId = R.string.v4_2_auto_accept_contact_requests,
descrId = R.string.v4_2_auto_accept_contact_requests_desc
),
@@ -191,22 +189,22 @@ private val versionDescriptions: List<VersionDescription> = listOf(
version = "v4.3",
features = listOf(
FeatureDescription(
icon = Icons.Outlined.Mic,
icon = R.drawable.ic_mic,
titleId = R.string.v4_3_voice_messages,
descrId = R.string.v4_3_voice_messages_desc
),
FeatureDescription(
icon = Icons.Outlined.DeleteForever,
icon = R.drawable.ic_delete_forever,
titleId = R.string.v4_3_irreversible_message_deletion,
descrId = R.string.v4_3_irreversible_message_deletion_desc
),
FeatureDescription(
icon = Icons.Outlined.WifiTethering,
icon = R.drawable.ic_wifi_tethering,
titleId = R.string.v4_3_improved_server_configuration,
descrId = R.string.v4_3_improved_server_configuration_desc
),
FeatureDescription(
icon = Icons.Outlined.VisibilityOff,
icon = R.drawable.ic_visibility_off,
titleId = R.string.v4_3_improved_privacy_and_security,
descrId = R.string.v4_3_improved_privacy_and_security_desc
),
@@ -216,22 +214,22 @@ private val versionDescriptions: List<VersionDescription> = listOf(
version = "v4.4",
features = listOf(
FeatureDescription(
icon = Icons.Outlined.Timer,
icon = R.drawable.ic_timer,
titleId = R.string.v4_4_disappearing_messages,
descrId = R.string.v4_4_disappearing_messages_desc
),
FeatureDescription(
icon = Icons.Outlined.Pending,
icon = R.drawable.ic_pending,
titleId = R.string.v4_4_live_messages,
descrId = R.string.v4_4_live_messages_desc
),
FeatureDescription(
icon = Icons.Outlined.VerifiedUser,
icon = R.drawable.ic_verified_user,
titleId = R.string.v4_4_verify_connection_security,
descrId = R.string.v4_4_verify_connection_security_desc
),
FeatureDescription(
icon = Icons.Outlined.Translate,
icon = R.drawable.ic_translate,
titleId = R.string.v4_4_french_interface,
descrId = R.string.v4_4_french_interface_descr
)
@@ -241,33 +239,33 @@ private val versionDescriptions: List<VersionDescription> = listOf(
version = "v4.5",
features = listOf(
FeatureDescription(
icon = Icons.Outlined.ManageAccounts,
icon = R.drawable.ic_manage_accounts,
titleId = R.string.v4_5_multiple_chat_profiles,
descrId = R.string.v4_5_multiple_chat_profiles_descr
),
FeatureDescription(
icon = Icons.Outlined.EditNote,
icon = R.drawable.ic_edit_note,
titleId = R.string.v4_5_message_draft,
descrId = R.string.v4_5_message_draft_descr
),
FeatureDescription(
icon = Icons.Outlined.SafetyDivider,
icon = R.drawable.ic_safety_divider,
titleId = R.string.v4_5_transport_isolation,
descrId = R.string.v4_5_transport_isolation_descr,
link = "https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation"
),
FeatureDescription(
icon = Icons.Outlined.Task,
icon = R.drawable.ic_task,
titleId = R.string.v4_5_private_filenames,
descrId = R.string.v4_5_private_filenames_descr
),
FeatureDescription(
icon = Icons.Outlined.Battery2Bar,
icon = R.drawable.ic_battery_2_bar,
titleId = R.string.v4_5_reduced_battery_usage,
descrId = R.string.v4_5_reduced_battery_usage_descr
),
FeatureDescription(
icon = Icons.Outlined.Translate,
icon = R.drawable.ic_translate,
titleId = R.string.v4_5_italian_interface,
descrId = R.string.v4_5_italian_interface_descr,
link = "https://github.com/simplex-chat/simplex-chat/tree/stable#translate-the-apps"
@@ -278,32 +276,32 @@ private val versionDescriptions: List<VersionDescription> = listOf(
version = "v4.6",
features = listOf(
FeatureDescription(
icon = Icons.Outlined.Lock,
icon = R.drawable.ic_lock,
titleId = R.string.v4_6_hidden_chat_profiles,
descrId = R.string.v4_6_hidden_chat_profiles_descr
),
FeatureDescription(
icon = Icons.Outlined.Flag,
icon = R.drawable.ic_flag,
titleId = R.string.v4_6_group_moderation,
descrId = R.string.v4_6_group_moderation_descr
),
FeatureDescription(
icon = Icons.Outlined.MapsUgc,
icon = R.drawable.ic_maps_ugc,
titleId = R.string.v4_6_group_welcome_message,
descrId = R.string.v4_6_group_welcome_message_descr
),
FeatureDescription(
icon = Icons.Outlined.Call,
icon = R.drawable.ic_call,
titleId = R.string.v4_6_audio_video_calls,
descrId = R.string.v4_6_audio_video_calls_descr
),
FeatureDescription(
icon = Icons.Outlined.Battery3Bar,
icon = R.drawable.ic_battery_3_bar,
titleId = R.string.v4_6_reduced_battery_usage,
descrId = R.string.v4_6_reduced_battery_usage_descr
),
FeatureDescription(
icon = Icons.Outlined.Translate,
icon = R.drawable.ic_translate,
titleId = R.string.v4_6_chinese_spanish_interface,
descrId = R.string.v4_6_chinese_spanish_interface_descr,
link = "https://github.com/simplex-chat/simplex-chat/tree/stable#translate-the-apps"
@@ -314,17 +312,17 @@ private val versionDescriptions: List<VersionDescription> = listOf(
version = "v5.0",
features = listOf(
FeatureDescription(
icon = Icons.Outlined.UploadFile,
icon = R.drawable.ic_upload_file,
titleId = R.string.v5_0_large_files_support,
descrId = R.string.v5_0_large_files_support_descr
),
FeatureDescription(
icon = Icons.Outlined.Lock,
icon = R.drawable.ic_lock,
titleId = R.string.v5_0_app_passcode,
descrId = R.string.v5_0_app_passcode_descr
),
FeatureDescription(
icon = Icons.Outlined.Translate,
icon = R.drawable.ic_translate,
titleId = R.string.v5_0_polish_interface,
descrId = R.string.v5_0_polish_interface_descr,
link = "https://github.com/simplex-chat/simplex-chat/tree/stable#translate-the-apps"
@@ -1,17 +1,14 @@
package chat.simplex.app.views.usersettings
import SectionBottomSpacer
import SectionCustomFooter
import SectionDivider
import SectionItemView
import SectionView
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.TheaterComedy
import androidx.compose.material.icons.outlined.*
import androidx.compose.ui.res.painterResource
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -53,25 +50,20 @@ private fun AcceptRequestsLayout(
val autoAcceptState = remember { mutableStateOf(AutoAcceptState(contactLink)) }
val autoAcceptStateSaved = remember { mutableStateOf(autoAcceptState.value) }
SectionView(stringResource(R.string.accept_requests).uppercase()) {
SectionItemView {
PreferenceToggleWithIcon(stringResource(R.string.accept_automatically), Icons.Outlined.Check, checked = autoAcceptState.value.enable) {
autoAcceptState.value = if (!it)
AutoAcceptState()
else
AutoAcceptState(it, autoAcceptState.value.incognito, autoAcceptState.value.welcomeText)
}
PreferenceToggleWithIcon(stringResource(R.string.accept_automatically), painterResource(R.drawable.ic_check), checked = autoAcceptState.value.enable) {
autoAcceptState.value = if (!it)
AutoAcceptState()
else
AutoAcceptState(it, autoAcceptState.value.incognito, autoAcceptState.value.welcomeText)
}
if (autoAcceptState.value.enable) {
SectionDivider()
SectionItemView {
PreferenceToggleWithIcon(
stringResource(R.string.incognito),
if (autoAcceptState.value.incognito) Icons.Filled.TheaterComedy else Icons.Outlined.TheaterComedy,
if (autoAcceptState.value.incognito) Indigo else HighOrLowlight,
autoAcceptState.value.incognito,
) {
autoAcceptState.value = AutoAcceptState(autoAcceptState.value.enable, it, autoAcceptState.value.welcomeText)
}
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,
autoAcceptState.value.incognito,
) {
autoAcceptState.value = AutoAcceptState(autoAcceptState.value.enable, it, autoAcceptState.value.welcomeText)
}
}
}
@@ -99,6 +91,7 @@ private fun AcceptRequestsLayout(
}
}
}
SectionBottomSpacer()
}
}
@@ -109,8 +102,8 @@ private fun ButtonsFooter(cancel: () -> Unit, save: () -> Unit, disabled: Boolea
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
FooterButton(Icons.Outlined.Replay, stringResource(R.string.cancel_verb), cancel, disabled)
FooterButton(Icons.Outlined.Check, stringResource(R.string.save_verb), save, disabled)
FooterButton(painterResource(R.drawable.ic_replay), stringResource(R.string.cancel_verb), cancel, disabled)
FooterButton(painterResource(R.drawable.ic_check), stringResource(R.string.save_verb), save, disabled)
}
}
@@ -1,22 +1,20 @@
package chat.simplex.app.views.usersettings
import SectionBottomSpacer
import SectionCustomFooter
import SectionDivider
import SectionItemView
import SectionSpacer
import SectionView
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
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.vector.ImageVector
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
@@ -141,55 +139,46 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.Start,
) {
AppBarTitle(stringResource(R.string.network_settings_title))
SectionView {
SectionItemView {
ResetToDefaultsButton(reset, disabled = resetDisabled)
}
SectionDivider()
SectionItemView {
TimeoutSettingRow(
stringResource(R.string.network_option_tcp_connection_timeout), networkTCPConnectTimeout,
listOf(2_500000, 5_000000, 7_500000, 10_000000, 15_000000, 20_000000), secondsLabel
)
}
SectionDivider()
SectionItemView {
TimeoutSettingRow(
stringResource(R.string.network_option_protocol_timeout), networkTCPTimeout,
listOf(1_500000, 3_000000, 5_000000, 7_000000, 10_000000, 15_000000), secondsLabel
)
}
SectionDivider()
SectionItemView {
TimeoutSettingRow(
stringResource(R.string.network_option_ping_interval), networkSMPPingInterval,
listOf(120_000000, 300_000000, 600_000000, 1200_000000, 2400_000000, 3600_000000), secondsLabel
)
}
SectionDivider()
SectionItemView {
IntSettingRow(
stringResource(R.string.network_option_ping_count), networkSMPPingCount,
listOf(1, 2, 3, 5, 8), ""
)
}
SectionDivider()
SectionItemView {
EnableKeepAliveSwitch(networkEnableKeepAlive)
}
SectionDivider()
if (networkEnableKeepAlive.value) {
SectionItemView {
IntSettingRow("TCP_KEEPIDLE", networkTCPKeepIdle, listOf(15, 30, 60, 120, 180), secondsLabel)
}
SectionDivider()
SectionItemView {
IntSettingRow("TCP_KEEPINTVL", networkTCPKeepIntvl, listOf(5, 10, 15, 30, 60), secondsLabel)
}
SectionDivider()
SectionItemView {
IntSettingRow("TCP_KEEPCNT", networkTCPKeepCnt, listOf(1, 2, 4, 6, 8), "")
}
@@ -197,11 +186,9 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
SectionItemView {
Text("TCP_KEEPIDLE", color = HighOrLowlight)
}
SectionDivider()
SectionItemView {
Text("TCP_KEEPINTVL", color = HighOrLowlight)
}
SectionDivider()
SectionItemView {
Text("TCP_KEEPCNT", color = HighOrLowlight)
}
@@ -210,7 +197,7 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
SectionCustomFooter {
SettingsSectionFooter(revert, save, footerDisabled)
}
SectionSpacer()
SectionBottomSpacer()
}
}
@@ -236,13 +223,9 @@ fun EnableKeepAliveSwitch(
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(stringResource(R.string.network_option_enable_tcp_keep_alive))
Switch(
DefaultSwitch(
checked = networkEnableKeepAlive.value,
onCheckedChange = { networkEnableKeepAlive.value = it },
colors = SwitchDefaults.colors(
checkedThumbColor = MaterialTheme.colors.primary,
uncheckedThumbColor = HighOrLowlight
),
)
}
}
@@ -277,7 +260,7 @@ fun IntSettingRow(title: String, selection: MutableState<Int>, values: List<Int>
)
Spacer(Modifier.size(4.dp))
Icon(
if (!expanded.value) Icons.Outlined.ExpandMore else Icons.Outlined.ExpandLess,
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
@@ -338,7 +321,7 @@ fun TimeoutSettingRow(title: String, selection: MutableState<Long>, values: List
)
Spacer(Modifier.size(4.dp))
Icon(
if (!expanded.value) Icons.Outlined.ExpandMore else Icons.Outlined.ExpandLess,
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
@@ -374,13 +357,13 @@ fun SettingsSectionFooter(revert: () -> Unit, save: () -> Unit, disabled: Boolea
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
FooterButton(Icons.Outlined.Replay, stringResource(R.string.network_options_revert), revert, disabled)
FooterButton(Icons.Outlined.Check, stringResource(R.string.network_options_save), save, disabled)
FooterButton(painterResource(R.drawable.ic_replay), stringResource(R.string.network_options_revert), revert, disabled)
FooterButton(painterResource(R.drawable.ic_check), stringResource(R.string.network_options_save), save, disabled)
}
}
@Composable
fun FooterButton(icon: ImageVector, title: String, action: () -> Unit, disabled: Boolean) {
fun FooterButton(icon: Painter, title: String, action: () -> Unit, disabled: Boolean) {
Surface(
shape = RoundedCornerShape(20.dp),
color = Color.Black.copy(alpha = 0f)
@@ -1,28 +1,21 @@
package chat.simplex.app.views.usersettings
import SectionBottomSpacer
import SectionCustomFooter
import SectionDivider
import SectionItemView
import SectionDividerSpaced
import SectionItemViewSpaceBetween
import SectionItemWithValue
import SectionSpacer
import SectionView
import android.app.Activity
import android.content.ComponentName
import android.content.Intent
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.os.Build
import android.provider.Settings
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.material.*
import androidx.compose.material.MaterialTheme.colors
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Circle
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
@@ -31,6 +24,7 @@ import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.*
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -91,8 +85,7 @@ fun AppearanceView(m: ChatModel) {
editPrimaryColor: (Color) -> Unit,
) {
Column(
Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.Start,
Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
) {
AppBarTitle(stringResource(R.string.appearance_settings))
SectionView(stringResource(R.string.settings_section_title_language), padding = PaddingValues()) {
@@ -105,26 +98,24 @@ fun AppearanceView(m: ChatModel) {
// onSelected = { openSystemLangPicker(context as? Activity ?: return@SectionItemWithValue) }
// )
// } else {
val state = rememberSaveable { mutableStateOf(languagePref.get() ?: "system") }
SectionItemView {
LangSelector(state) {
state.value = it
withApi {
delay(200)
val activity = context as? Activity
if (activity != null) {
if (it == "system") {
saveAppLocale(languagePref, activity)
} else {
saveAppLocale(languagePref, activity, it)
}
}
val state = rememberSaveable { mutableStateOf(languagePref.get() ?: "system") }
LangSelector(state) {
state.value = it
withApi {
delay(200)
val activity = context as? Activity
if (activity != null) {
if (it == "system") {
saveAppLocale(languagePref, activity)
} else {
saveAppLocale(languagePref, activity, it)
}
}
}
}
// }
}
SectionSpacer()
SectionDividerSpaced()
SectionView(stringResource(R.string.settings_section_title_icon), padding = PaddingValues(horizontal = DEFAULT_PADDING_HALF)) {
LazyRow {
@@ -149,21 +140,18 @@ fun AppearanceView(m: ChatModel) {
}
}
SectionSpacer()
SectionDividerSpaced(maxTopPadding = true)
val currentTheme by CurrentColors.collectAsState()
SectionView(stringResource(R.string.settings_section_title_themes)) {
SectionItemViewSpaceBetween {
val darkTheme = isSystemInDarkTheme()
val state = remember { derivedStateOf { currentTheme.second } }
ThemeSelector(state) {
ThemeManager.applyTheme(it.name, darkTheme)
}
val darkTheme = isSystemInDarkTheme()
val state = remember { derivedStateOf { currentTheme.second } }
ThemeSelector(state) {
ThemeManager.applyTheme(it.name, darkTheme)
}
SectionDivider()
SectionItemViewSpaceBetween({ editPrimaryColor(currentTheme.first.primary) }) {
val title = generalGetString(R.string.color_primary)
Text(title)
Icon(Icons.Filled.Circle, title, tint = colors.primary)
Icon(painterResource(R.drawable.ic_circle_filled), title, tint = colors.primary)
}
}
if (currentTheme.first.primary != LightColorPalette.primary) {
@@ -177,6 +165,7 @@ fun AppearanceView(m: ChatModel) {
}
}
}
SectionBottomSpacer()
}
}
@@ -1,17 +1,17 @@
package chat.simplex.app.views.usersettings
import SectionDivider
import SectionBottomSpacer
import SectionItemView
import SectionTextFooter
import SectionView
import androidx.compose.foundation.clickable
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import chat.simplex.app.R
@@ -37,22 +37,17 @@ fun CallSettingsLayout(
editIceServers: () -> Unit,
) {
Column(
Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.Start,
Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
AppBarTitle(stringResource(R.string.your_calls))
val lockCallState = remember { mutableStateOf(callOnLockScreen.get()) }
SectionView(stringResource(R.string.settings_section_title_settings)) {
SectionItemView(editIceServers) { Text(stringResource(R.string.webrtc_ice_servers)) }
SectionDivider()
val enabled = remember { mutableStateOf(true) }
SectionItemView { LockscreenOpts(lockCallState, enabled, onSelected = { callOnLockScreen.set(it); lockCallState.value = it }) }
SectionDivider()
SectionItemView() {
SharedPreferenceToggle(stringResource(R.string.always_use_relay), webrtcPolicyRelay)
}
LockscreenOpts(lockCallState, enabled, onSelected = { callOnLockScreen.set(it); lockCallState.value = it })
SettingsPreferenceItem(null, stringResource(R.string.always_use_relay), webrtcPolicyRelay)
}
SectionTextFooter(
if (remember { webrtcPolicyRelay.state }.value) {
@@ -61,6 +56,7 @@ fun CallSettingsLayout(
generalGetString(R.string.relay_server_if_necessary)
}
)
SectionBottomSpacer()
}
}
@@ -87,34 +83,24 @@ private fun LockscreenOpts(lockscreenOpts: State<CallOnLockScreen>, enabled: Sta
@Composable
fun SharedPreferenceToggle(
text: String,
preference: SharedPreference<Boolean>,
preferenceState: MutableState<Boolean>? = null,
enabled: Boolean = true,
onChange: ((Boolean) -> Unit)? = null,
) {
val prefState = preferenceState ?: remember { mutableStateOf(preference.get()) }
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Text(text, Modifier.padding(end = 24.dp))
Spacer(Modifier.fillMaxWidth().weight(1f))
Switch(
checked = prefState.value,
onCheckedChange = {
preference.set(it)
prefState.value = it
onChange?.invoke(it)
},
colors = SwitchDefaults.colors(
checkedThumbColor = MaterialTheme.colors.primary,
uncheckedThumbColor = HighOrLowlight
)
)
}
) {
DefaultSwitch(
enabled = enabled,
checked = remember { preference.state }.value,
onCheckedChange = {
preference.set(it)
onChange?.invoke(it)
},
)
}
@Composable
fun SharedPreferenceToggleWithIcon(
text: String,
icon: ImageVector,
icon: Painter,
stopped: Boolean = false,
onClickInfo: () -> Unit,
preference: SharedPreference<Boolean>,
@@ -130,16 +116,12 @@ fun SharedPreferenceToggleWithIcon(
tint = MaterialTheme.colors.primary
)
Spacer(Modifier.fillMaxWidth().weight(1f))
Switch(
DefaultSwitch(
checked = prefState.value,
onCheckedChange = {
preference.set(it)
prefState.value = it
},
colors = SwitchDefaults.colors(
checkedThumbColor = MaterialTheme.colors.primary,
uncheckedThumbColor = HighOrLowlight
),
enabled = !stopped
)
}
@@ -1,17 +1,16 @@
package chat.simplex.app.views.usersettings
import SectionDivider
import SectionSpacer
import SectionBottomSpacer
import SectionTextFooter
import SectionView
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import chat.simplex.app.R
import chat.simplex.app.model.ChatModel
@@ -22,26 +21,23 @@ import chat.simplex.app.views.helpers.*
fun DeveloperView(
m: ChatModel,
showCustomModal: (@Composable (ChatModel, () -> Unit) -> Unit) -> (() -> Unit),
withAuth: (block: () -> Unit) -> Unit
withAuth: (title: String, desc: String, block: () -> Unit) -> Unit
) {
Column(Modifier.fillMaxWidth()) {
Column(Modifier.fillMaxWidth().verticalScroll(rememberScrollState())) {
val uriHandler = LocalUriHandler.current
AppBarTitle(stringResource(R.string.settings_developer_tools))
val developerTools = m.controller.appPrefs.developerTools
val devTools = remember { mutableStateOf(developerTools.get()) }
val devTools = remember { developerTools.state }
SectionView() {
InstallTerminalAppItem(uriHandler)
SectionDivider()
ChatConsoleItem { withAuth(showCustomModal { it, close -> TerminalView(it, close) }) }
SectionDivider()
SettingsPreferenceItem(Icons.Outlined.DriveFolderUpload, stringResource(R.string.confirm_database_upgrades), m.controller.appPrefs.confirmDBUpgrades)
SectionDivider()
SettingsPreferenceItem(Icons.Outlined.Code, stringResource(R.string.show_developer_options), developerTools, devTools)
ChatConsoleItem { withAuth(generalGetString(R.string.auth_open_chat_console), generalGetString(R.string.auth_log_in_using_credential), showCustomModal { it, close -> TerminalView(it, close) })}
SettingsPreferenceItem(painterResource(R.drawable.ic_drive_folder_upload), stringResource(R.string.confirm_database_upgrades), m.controller.appPrefs.confirmDBUpgrades)
SettingsPreferenceItem(painterResource(R.drawable.ic_code), stringResource(R.string.show_developer_options), developerTools)
}
SectionTextFooter(
generalGetString(if (devTools.value) R.string.show_dev_options else R.string.hide_dev_options) + " " +
generalGetString(R.string.developer_options)
)
SectionSpacer()
SectionBottomSpacer()
}
}
@@ -27,7 +27,6 @@ fun HelpLayout(userDisplayName: String) {
.fillMaxWidth()
.verticalScroll(rememberScrollState())
.padding(horizontal = DEFAULT_PADDING),
horizontalAlignment = Alignment.Start
){
AppBarTitle(String.format(stringResource(R.string.personal_welcome), userDisplayName), false)
ChatHelpView()
@@ -1,6 +1,6 @@
package chat.simplex.app.views.usersettings
import SectionDivider
import SectionBottomSpacer
import SectionItemView
import SectionItemViewSpaceBetween
import SectionSpacer
@@ -12,9 +12,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import chat.simplex.app.R
@@ -58,8 +56,7 @@ private fun HiddenProfileLayout(
Column(
Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState())
.padding(bottom = DEFAULT_BOTTOM_PADDING),
.verticalScroll(rememberScrollState()),
) {
AppBarTitle(stringResource(R.string.hide_profile))
SectionView(padding = PaddingValues(start = 8.dp, end = DEFAULT_PADDING)) {
@@ -76,15 +73,14 @@ private fun HiddenProfileLayout(
SectionItemView {
PassphraseField(hidePassword, generalGetString(R.string.password_to_show), isValid = { passwordValid }, showStrength = true)
}
SectionDivider()
SectionItemView {
PassphraseField(confirmHidePassword, stringResource(R.string.confirm_password), isValid = { confirmValid }, dependsOn = hidePassword)
}
SectionDivider()
SectionItemViewSpaceBetween({ saveProfilePassword(hidePassword.value) }, disabled = saveDisabled, minHeight = TextFieldDefaults.MinHeight) {
Text(generalGetString(R.string.save_profile_password), color = if (saveDisabled) HighOrLowlight else MaterialTheme.colors.primary)
}
}
SectionTextFooter(stringResource(R.string.to_reveal_profile_enter_password))
SectionBottomSpacer()
}
}
@@ -1,5 +1,6 @@
package chat.simplex.app.views.usersettings
import SectionBottomSpacer
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
@@ -31,6 +32,7 @@ fun IncognitoLayout() {
Text(generalGetString(R.string.incognito_info_allows))
Text(generalGetString(R.string.incognito_info_share))
Text(generalGetString(R.string.incognito_info_find))
SectionBottomSpacer()
}
}
}
@@ -1,5 +1,6 @@
package chat.simplex.app.views.usersettings
import SectionBottomSpacer
import android.content.res.Configuration
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
@@ -58,6 +59,7 @@ fun MarkdownHelpView() {
})
}
}
SectionBottomSpacer()
}
}
@@ -1,31 +1,33 @@
package chat.simplex.app.views.usersettings
import SectionBottomSpacer
import SectionCustomFooter
import SectionDivider
import SectionItemView
import SectionItemWithValue
import SectionSpacer
import SectionTextFooter
import SectionView
import SectionViewSelectable
import TextIconSpaced
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
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.TextRange
import androidx.compose.ui.text.*
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.input.*
import androidx.compose.ui.tooling.preview.Preview
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.*
import chat.simplex.app.views.chat.item.ClickableText
import chat.simplex.app.views.helpers.*
@Composable
@@ -156,37 +158,33 @@ fun NetworkAndServersView(
updateSessionMode: (TransportSessionMode) -> Unit,
) {
Column(
Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.Start,
Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
AppBarTitle(stringResource(R.string.network_and_servers))
SectionView(generalGetString(R.string.settings_section_title_messages)) {
SettingsActionItem(Icons.Outlined.Dns, stringResource(R.string.smp_servers), showCustomModal { m, close -> ProtocolServersView(m, ServerProtocol.SMP, close) })
SectionDivider()
SettingsActionItem(painterResource(R.drawable.ic_dns), stringResource(R.string.smp_servers), showCustomModal { m, close -> ProtocolServersView(m, ServerProtocol.SMP, close) })
SettingsActionItem(Icons.Outlined.Dns, stringResource(R.string.xftp_servers), showCustomModal { m, close -> ProtocolServersView(m, ServerProtocol.XFTP, close) })
SectionDivider()
SettingsActionItem(painterResource(R.drawable.ic_dns), stringResource(R.string.xftp_servers), showCustomModal { m, close -> ProtocolServersView(m, ServerProtocol.XFTP, close) })
SectionItemView {
UseSocksProxySwitch(networkUseSocksProxy, proxyPort, toggleSocksProxy, showSettingsModal)
}
SectionDivider()
UseSocksProxySwitch(networkUseSocksProxy, proxyPort, toggleSocksProxy, showSettingsModal)
UseOnionHosts(onionHosts, networkUseSocksProxy, showSettingsModal, useOnion)
SectionDivider()
if (developerTools) {
SessionModePicker(sessionMode, showSettingsModal, updateSessionMode)
SectionDivider()
}
SettingsActionItem(Icons.Outlined.Cable, stringResource(R.string.network_settings), showSettingsModal { AdvancedNetworkSettingsView(it) })
SettingsActionItem(painterResource(R.drawable.ic_cable), stringResource(R.string.network_settings), showSettingsModal { AdvancedNetworkSettingsView(it) })
}
if (networkUseSocksProxy.value) {
SectionCustomFooter { Text(annotatedStringResource(R.string.disable_onion_hosts_when_not_supported)) }
Divider(Modifier.padding(start = DEFAULT_PADDING_HALF, top = 32.dp, end = DEFAULT_PADDING_HALF, bottom = 30.dp))
} else {
Divider(Modifier.padding(start = DEFAULT_PADDING_HALF, top = 24.dp, end = DEFAULT_PADDING_HALF, bottom = 30.dp))
}
Spacer(Modifier.height(16.dp))
SectionView(generalGetString(R.string.settings_section_title_calls)) {
SettingsActionItem(Icons.Outlined.ElectricalServices, stringResource(R.string.webrtc_ice_servers), showModal { RTCServersView(it) })
SettingsActionItem(painterResource(R.drawable.ic_electrical_services), stringResource(R.string.webrtc_ice_servers), showModal { RTCServersView(it) })
}
SectionBottomSpacer()
}
}
@@ -198,41 +196,49 @@ fun UseSocksProxySwitch(
showSettingsModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit)
) {
Row(
Modifier.fillMaxWidth(),
Modifier.fillMaxWidth().padding(end = DEFAULT_PADDING),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Row(
Modifier.weight(1f),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
Modifier.weight(1f).padding(horizontal = DEFAULT_PADDING),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Outlined.SettingsEthernet,
painterResource(R.drawable.ic_settings_ethernet),
stringResource(R.string.network_socks_toggle),
tint = HighOrLowlight
)
TextIconSpaced(false)
if (networkUseSocksProxy.value) {
Row {
Text(generalGetString(R.string.network_socks_toggle_use_socks_proxy) + " (")
Text(
generalGetString(R.string.network_proxy_port).format(proxyPort.value),
Modifier.clickable { showSettingsModal { SockProxySettings(it) }() },
color = MaterialTheme.colors.primary
val text = buildAnnotatedString {
append(generalGetString(R.string.network_socks_toggle_use_socks_proxy) + " (")
val style = SpanStyle(color = MaterialTheme.colors.primary)
withAnnotation(tag = "PORT", annotation = generalGetString(R.string.network_proxy_port).format(proxyPort.value)) {
withStyle(style) { append(generalGetString(R.string.network_proxy_port).format(proxyPort.value)) }
}
append(")")
}
ClickableText(
text,
style = TextStyle(color = MaterialTheme.colors.onBackground, fontSize = 16.sp, fontFamily = FontFamily(Font(R.font.inter_regular))),
onClick = { offset ->
text.getStringAnnotations(tag = "PORT", start = offset, end = offset)
.firstOrNull()?.let { _ ->
showSettingsModal { SockProxySettings(it) }()
}
},
shouldConsumeEvent = { offset ->
text.getStringAnnotations(tag = "PORT", start = offset, end = offset).any()
}
)
Text(")")
}
} else {
Text(stringResource(R.string.network_socks_toggle))
}
}
Switch(
DefaultSwitch(
checked = networkUseSocksProxy.value,
onCheckedChange = toggleSocksProxy,
colors = SwitchDefaults.colors(
checkedThumbColor = MaterialTheme.colors.primary,
uncheckedThumbColor = HighOrLowlight
),
)
}
}
@@ -243,7 +249,6 @@ fun SockProxySettings(m: ChatModel) {
Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState())
.padding(bottom = DEFAULT_BOTTOM_PADDING),
) {
val defaultHostPort = remember { "localhost:9050" }
AppBarTitle(generalGetString(R.string.network_socks_proxy_settings))
@@ -273,7 +278,6 @@ fun SockProxySettings(m: ChatModel) {
}
}, disabled = hostPort == defaultHostPort)
}
SectionDivider()
SectionItemView {
DefaultConfigurableTextField(
hostUnsaved,
@@ -284,7 +288,6 @@ fun SockProxySettings(m: ChatModel) {
keyboardType = KeyboardType.Text,
)
}
SectionDivider()
SectionItemView {
DefaultConfigurableTextField(
portUnsaved,
@@ -311,6 +314,7 @@ fun SockProxySettings(m: ChatModel) {
remember { derivedStateOf { !validPort(portUnsaved.value.text) } }.value
)
}
SectionBottomSpacer()
}
}
@@ -333,7 +337,6 @@ private fun UseOnionHosts(
val onSelected = showModal {
Column(
Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.Start,
) {
AppBarTitle(stringResource(R.string.network_use_onion_hosts))
SectionViewSelectable(null, onionHosts, values, useOnion)
@@ -344,7 +347,7 @@ private fun UseOnionHosts(
generalGetString(R.string.network_use_onion_hosts),
onionHosts,
values,
icon = Icons.Outlined.Security,
icon = painterResource(R.drawable.ic_security),
enabled = enabled,
onSelected = onSelected
)
@@ -369,11 +372,10 @@ private fun SessionModePicker(
generalGetString(R.string.network_session_mode_transport_isolation),
sessionMode,
values,
icon = Icons.Outlined.SafetyDivider,
icon = painterResource(R.drawable.ic_safety_divider),
onSelected = showModal {
Column(
Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.Start,
) {
AppBarTitle(stringResource(R.string.network_session_mode_transport_isolation))
SectionViewSelectable(null, sessionMode, values, updateSessionMode)
@@ -389,8 +391,8 @@ private fun NetworkSectionFooter(revert: () -> Unit, save: () -> Unit, revertDis
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
FooterButton(Icons.Outlined.Replay, stringResource(R.string.network_options_revert), revert, revertDisabled)
FooterButton(Icons.Outlined.Check, stringResource(R.string.network_options_save), save, saveDisabled)
FooterButton(painterResource(R.drawable.ic_replay), stringResource(R.string.network_options_revert), revert, revertDisabled)
FooterButton(painterResource(R.drawable.ic_check), stringResource(R.string.network_options_save), save, saveDisabled)
}
}
@@ -1,11 +1,12 @@
package chat.simplex.app.views.usersettings
import SectionDivider
import SectionItemViewSpaceBetween
import SectionBottomSpacer
import SectionView
import SectionViewSelectable
import android.os.Build
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
@@ -14,7 +15,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.capitalize
import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import chat.simplex.app.*
import chat.simplex.app.R
import chat.simplex.app.model.ChatModel
@@ -79,14 +79,11 @@ fun NotificationsSettingsLayout(
val previewModes = remember { notificationPreviewModes() }
Column(
Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.Start,
Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
) {
AppBarTitle(stringResource(R.string.notifications))
SectionView(null) {
SectionItemViewSpaceBetween({ showPage(CurrentPage.NOTIFICATIONS_MODE) }) {
Text(stringResource(R.string.settings_notifications_mode_title))
Spacer(Modifier.padding(horizontal = 10.dp))
SettingsActionItemWithContent(null, stringResource(R.string.settings_notifications_mode_title), { showPage(CurrentPage.NOTIFICATIONS_MODE) }) {
Text(
modes.first { it.value == notificationsMode.value }.title,
maxLines = 1,
@@ -94,10 +91,7 @@ fun NotificationsSettingsLayout(
color = HighOrLowlight
)
}
SectionDivider()
SectionItemViewSpaceBetween({ showPage(CurrentPage.NOTIFICATION_PREVIEW_MODE) }) {
Text(stringResource(R.string.settings_notification_preview_mode_title))
Spacer(Modifier.padding(horizontal = 10.dp))
SettingsActionItemWithContent(null, stringResource(R.string.settings_notification_preview_mode_title), { showPage(CurrentPage.NOTIFICATION_PREVIEW_MODE) }) {
Text(
previewModes.first { it.value == notificationPreviewMode.value }.title,
maxLines = 1,
@@ -106,6 +100,7 @@ fun NotificationsSettingsLayout(
)
}
}
SectionBottomSpacer()
}
}
@@ -117,7 +112,6 @@ fun NotificationsModeView(
val modes = remember { notificationModes() }
Column(
Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.Start,
) {
AppBarTitle(stringResource(R.string.settings_notifications_mode_title).lowercase().capitalize(Locale.current))
SectionViewSelectable(null, notificationsMode, modes, onNotificationsModeSelected)
@@ -132,7 +126,6 @@ fun NotificationPreviewView(
val previewModes = remember { notificationPreviewModes() }
Column(
Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.Start,
) {
AppBarTitle(stringResource(R.string.settings_notification_preview_title))
SectionViewSelectable(null, notificationPreviewMode, previewModes, onNotificationPreviewModeSelected)
@@ -1,8 +1,8 @@
package chat.simplex.app.views.usersettings
import SectionDivider
import SectionBottomSpacer
import SectionDividerSpaced
import SectionItemView
import SectionSpacer
import SectionTextFooter
import SectionView
import androidx.compose.foundation.*
@@ -11,7 +11,6 @@ import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import chat.simplex.app.R
@@ -40,7 +39,6 @@ fun PreferencesView(m: ChatModel, user: User, close: () -> Unit,) {
if (preferences == currentPreferences) close()
else showUnsavedChangesAlert({ savePrefs(close) }, close)
},
background = if (isInDarkTheme()) MaterialTheme.colors.background else SettingsBackgroundLight
) {
PreferencesLayout(
preferences,
@@ -62,50 +60,48 @@ private fun PreferencesLayout(
) {
Column(
Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.Start,
) {
AppBarTitle(stringResource(R.string.your_preferences))
val timedMessages = remember(preferences) { mutableStateOf(preferences.timedMessages.allow) }
TimedMessagesFeatureSection(timedMessages) {
applyPrefs(preferences.copy(timedMessages = TimedMessagesPreference(allow = if (it) FeatureAllowed.YES else FeatureAllowed.NO)))
}
SectionSpacer()
SectionDividerSpaced(true, maxBottomPadding = false)
val allowFullDeletion = remember(preferences) { mutableStateOf(preferences.fullDelete.allow) }
FeatureSection(ChatFeature.FullDelete, allowFullDeletion) {
applyPrefs(preferences.copy(fullDelete = SimpleChatPreference(allow = it)))
}
SectionSpacer()
SectionDividerSpaced(true, maxBottomPadding = false)
val allowVoice = remember(preferences) { mutableStateOf(preferences.voice.allow) }
FeatureSection(ChatFeature.Voice, allowVoice) {
applyPrefs(preferences.copy(voice = SimpleChatPreference(allow = it)))
}
SectionSpacer()
SectionDividerSpaced(true, maxBottomPadding = false)
val allowCalls = remember(preferences) { mutableStateOf(preferences.calls.allow) }
FeatureSection(ChatFeature.Calls, allowCalls) {
applyPrefs(preferences.copy(calls = SimpleChatPreference(allow = it)))
}
SectionSpacer()
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
ResetSaveButtons(
reset = reset,
save = savePrefs,
disabled = preferences == currentPreferences
)
SectionBottomSpacer()
}
}
@Composable
private fun FeatureSection(feature: ChatFeature, allowFeature: State<FeatureAllowed>, onSelected: (FeatureAllowed) -> Unit) {
SectionView {
SectionItemView {
ExposedDropDownSettingRow(
feature.text,
FeatureAllowed.values().map { it to it.text },
allowFeature,
icon = feature.icon,
enabled = remember { mutableStateOf(feature != ChatFeature.Calls) },
onSelected = onSelected,
)
}
ExposedDropDownSettingRow(
feature.text,
FeatureAllowed.values().map { it to it.text },
allowFeature,
icon = feature.icon,
enabled = remember { mutableStateOf(feature != ChatFeature.Calls) },
onSelected = onSelected,
)
}
SectionTextFooter(feature.allowDescription(allowFeature.value) + (if (feature == ChatFeature.Calls) generalGetString(R.string.available_in_v51) else ""))
}
@@ -113,15 +109,14 @@ private fun FeatureSection(feature: ChatFeature, allowFeature: State<FeatureAllo
@Composable
private fun TimedMessagesFeatureSection(allowFeature: State<FeatureAllowed>, onSelected: (Boolean) -> Unit) {
SectionView {
SectionItemView {
PreferenceToggleWithIcon(
ChatFeature.TimedMessages.text,
ChatFeature.TimedMessages.icon,
HighOrLowlight,
allowFeature.value == FeatureAllowed.ALWAYS || allowFeature.value == FeatureAllowed.YES,
onSelected
)
}
PreferenceToggleWithIcon(
ChatFeature.TimedMessages.text,
ChatFeature.TimedMessages.icon,
HighOrLowlight,
allowFeature.value == FeatureAllowed.ALWAYS || allowFeature.value == FeatureAllowed.YES,
extraPadding = false,
onSelected
)
}
SectionTextFooter(ChatFeature.TimedMessages.allowDescription(allowFeature.value))
}
@@ -132,7 +127,6 @@ private fun ResetSaveButtons(reset: () -> Unit, save: () -> Unit, disabled: Bool
SectionItemView(reset, disabled = disabled) {
Text(stringResource(R.string.reset_verb), color = if (disabled) HighOrLowlight else MaterialTheme.colors.primary)
}
SectionDivider()
SectionItemView(save, disabled = disabled) {
Text(stringResource(R.string.save_and_notify_contacts), color = if (disabled) HighOrLowlight else MaterialTheme.colors.primary)
}
@@ -1,27 +1,25 @@
package chat.simplex.app.views.usersettings
import SectionDivider
import SectionBottomSpacer
import SectionDividerSpaced
import SectionItemView
import SectionSpacer
import SectionTextFooter
import SectionView
import android.view.WindowManager
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Lock
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity
import chat.simplex.app.R
import chat.simplex.app.model.*
import chat.simplex.app.ui.theme.HighOrLowlight
import chat.simplex.app.ui.theme.SimplexGreen
import chat.simplex.app.views.helpers.*
import chat.simplex.app.views.helpers.DatabaseUtils.ksAppPassword
import chat.simplex.app.views.localauth.SetAppPasscodeView
@@ -44,16 +42,14 @@ fun PrivacySettingsView(
setPerformLA: (Boolean, FragmentActivity) -> Unit
) {
Column(
Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.Start
Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
) {
val simplexLinkMode = chatModel.controller.appPrefs.simplexLinkMode
AppBarTitle(stringResource(R.string.your_privacy))
SectionView(stringResource(R.string.settings_section_title_device)) {
ChatLockItem(chatModel, showSettingsModal, setPerformLA)
SectionDivider()
val context = LocalContext.current
SettingsPreferenceItem(Icons.Outlined.VisibilityOff, stringResource(R.string.protect_app_screen), chatModel.controller.appPrefs.privacyProtectScreen) { on ->
SettingsPreferenceItem(painterResource(R.drawable.ic_visibility_off), stringResource(R.string.protect_app_screen), chatModel.controller.appPrefs.privacyProtectScreen) { on ->
if (on) {
(context as? FragmentActivity)?.window?.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
@@ -64,23 +60,20 @@ fun PrivacySettingsView(
}
}
}
SectionSpacer()
SectionDividerSpaced()
SectionView(stringResource(R.string.settings_section_title_chats)) {
SettingsPreferenceItem(Icons.Outlined.Image, stringResource(R.string.auto_accept_images), chatModel.controller.appPrefs.privacyAcceptImages)
SectionDivider()
SettingsPreferenceItem(Icons.Outlined.TravelExplore, stringResource(R.string.send_link_previews), chatModel.controller.appPrefs.privacyLinkPreviews)
SectionDivider()
SectionItemView {
SimpleXLinkOptions(chatModel.simplexLinkMode, onSelected = {
simplexLinkMode.set(it)
chatModel.simplexLinkMode.value = it
})
}
SettingsPreferenceItem(painterResource(R.drawable.ic_image), stringResource(R.string.auto_accept_images), chatModel.controller.appPrefs.privacyAcceptImages)
SettingsPreferenceItem(painterResource(R.drawable.ic_travel_explore), stringResource(R.string.send_link_previews), chatModel.controller.appPrefs.privacyLinkPreviews)
SimpleXLinkOptions(chatModel.simplexLinkMode, onSelected = {
simplexLinkMode.set(it)
chatModel.simplexLinkMode.value = it
})
}
if (chatModel.simplexLinkMode.value == SimplexLinkMode.BROWSER) {
SectionTextFooter(stringResource(R.string.simplex_link_mode_browser_warning))
}
SectionBottomSpacer()
}
}
@@ -207,8 +200,7 @@ fun SimplexLockView(
}
Column(
Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.Start
Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
) {
AppBarTitle(stringResource(R.string.chat_lock))
SectionView {
@@ -241,31 +233,25 @@ fun SimplexLockView(
setPerformLA(false, activity)
}
}
SectionDivider()
SectionItemView {
LockModeSelector(laMode) { newLAMode ->
if (laMode.value == newLAMode) return@LockModeSelector
if (chatModel.controller.appPrefs.performLA.get()) {
toggleLAMode(newLAMode)
} else {
currentLAMode.set(newLAMode)
}
LockModeSelector(laMode) { newLAMode ->
if (laMode.value == newLAMode) return@LockModeSelector
if (chatModel.controller.appPrefs.performLA.get()) {
toggleLAMode(newLAMode)
} else {
currentLAMode.set(newLAMode)
}
}
if (performLA.value) {
SectionDivider()
SectionItemView {
LockDelaySelector(remember { laLockDelay.state }) { laLockDelay.set(it) }
}
LockDelaySelector(remember { laLockDelay.state }) { laLockDelay.set(it) }
if (showChangePasscode.value && laMode.value == LAMode.PASSCODE) {
SectionDivider()
SectionItemView({ changeLAPassword() }) {
Text(generalGetString(R.string.la_change_app_passcode))
}
}
}
}
SectionBottomSpacer()
}
}
@@ -279,13 +265,9 @@ private fun EnableLock(performLA: MutableState<Boolean>, onCheckedChange: (Boole
.fillMaxWidth()
.weight(1F)
)
Switch(
DefaultSwitch(
checked = performLA.value,
onCheckedChange = onCheckedChange,
colors = SwitchDefaults.colors(
checkedThumbColor = MaterialTheme.colors.primary,
uncheckedThumbColor = HighOrLowlight
)
)
}
}
@@ -1,21 +1,20 @@
package chat.simplex.app.views.usersettings
import SectionDivider
import SectionBottomSpacer
import SectionDividerSpaced
import SectionItemView
import SectionItemViewSpaceBetween
import SectionSpacer
import SectionView
import android.util.Log
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
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.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
@@ -81,7 +80,6 @@ private fun ProtocolServerLayout(
Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState())
.padding(bottom = DEFAULT_PADDING)
) {
AppBarTitle(stringResource(if (server.preset) R.string.smp_servers_preset_server else R.string.smp_servers_your_server))
@@ -90,6 +88,7 @@ private fun ProtocolServerLayout(
} else {
CustomServer(testing, server, serverProtocol, testServer, onUpdate, onDelete)
}
SectionBottomSpacer()
}
}
@@ -113,7 +112,7 @@ private fun PresetServer(
)
}
}
SectionSpacer()
SectionDividerSpaced(maxTopPadding = true)
UseServerSection(true, testing, server, testServer, onUpdate, onDelete)
}
@@ -136,7 +135,7 @@ private fun CustomServer(
}
SectionView(
stringResource(R.string.smp_servers_your_server_address).uppercase(),
icon = Icons.Outlined.ErrorOutline,
icon = painterResource(R.drawable.ic_error),
iconTint = if (!valid.value) MaterialTheme.colors.error else Color.Transparent,
) {
val testedPreviously = remember { mutableMapOf<String, Boolean?>() }
@@ -151,13 +150,13 @@ private fun CustomServer(
onUpdate(server.copy(server = it, tested = testedPreviously[serverAddress.value]))
}
}
SectionSpacer()
SectionDividerSpaced()
UseServerSection(valid.value, testing, server, testServer, onUpdate, onDelete)
SectionSpacer()
if (valid.value) {
SectionDividerSpaced()
SectionView(stringResource(R.string.smp_servers_add_to_another_device).uppercase()) {
QRCode(serverAddress.value, Modifier.aspectRatio(1f))
QRCode(serverAddress.value, Modifier.aspectRatio(1f).padding(horizontal = DEFAULT_PADDING))
}
}
}
@@ -176,12 +175,8 @@ private fun UseServerSection(
Text(stringResource(R.string.smp_servers_test_server), color = if (valid && !testing) MaterialTheme.colors.onBackground else HighOrLowlight)
ShowTestStatus(server)
}
SectionDivider()
SectionItemView {
val enabled = rememberUpdatedState(server.enabled)
PreferenceToggle(stringResource(R.string.smp_servers_use_server_for_new_conn), enabled.value) { onUpdate(server.copy(enabled = it)) }
}
SectionDivider()
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)
}
@@ -191,9 +186,9 @@ private fun UseServerSection(
@Composable
fun ShowTestStatus(server: ServerCfg, modifier: Modifier = Modifier) =
when (server.tested) {
true -> Icon(Icons.Outlined.Check, null, modifier, tint = SimplexGreen)
false -> Icon(Icons.Outlined.Clear, null, modifier, tint = MaterialTheme.colors.error)
else -> Icon(Icons.Outlined.Check, null, modifier, tint = Color.Transparent)
true -> Icon(painterResource(R.drawable.ic_check), null, modifier, tint = SimplexGreen)
false -> Icon(painterResource(R.drawable.ic_close), null, modifier, tint = MaterialTheme.colors.error)
else -> Icon(painterResource(R.drawable.ic_check), null, modifier, tint = Color.Transparent)
}
suspend fun testServerConnection(server: ServerCfg, m: ChatModel): Pair<ServerCfg, ProtocolTestFailure?> =
@@ -1,20 +1,19 @@
package chat.simplex.app.views.usersettings
import SectionDivider
import SectionBottomSpacer
import SectionDividerSpaced
import SectionItemView
import SectionSpacer
import SectionTextFooter
import SectionView
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.*
import androidx.compose.ui.text.font.FontWeight
@@ -92,7 +91,6 @@ fun ProtocolServersView(m: ChatModel, serverProtocol: ServerProtocol, close: ()
if (saveDisabled.value) close()
else showUnsavedChangesAlert({ saveServers(serverProtocol, currServers, servers, m, close) }, close)
},
background = if (isInDarkTheme()) MaterialTheme.colors.background else SettingsBackgroundLight
) {
ProtocolServersLayout(
serverProtocol,
@@ -195,7 +193,6 @@ private fun ProtocolServersLayout(
Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState())
.padding(bottom = DEFAULT_PADDING),
) {
AppBarTitle(stringResource(if (serverProtocol == ServerProtocol.SMP) R.string.your_SMP_servers else R.string.your_XFTP_servers))
@@ -204,10 +201,9 @@ private fun ProtocolServersLayout(
SectionItemView({ showServer(srv) }, disabled = testing) {
ProtocolServerView(serverProtocol, srv, servers, testing)
}
SectionDivider()
}
SettingsActionItem(
Icons.Outlined.Add,
painterResource(R.drawable.ic_add),
stringResource(R.string.smp_servers_add),
addServer,
disabled = testing,
@@ -226,25 +222,24 @@ private fun ProtocolServersLayout(
}
}
)
SectionSpacer()
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
SectionView {
SectionItemView(resetServers, disabled = serversUnchanged) {
Text(stringResource(R.string.reset_verb), color = if (!serversUnchanged) MaterialTheme.colors.onBackground else HighOrLowlight)
}
SectionDivider()
val testServersDisabled = testing || allServersDisabled
SectionItemView(testServers, disabled = testServersDisabled) {
Text(stringResource(R.string.smp_servers_test_servers), color = if (!testServersDisabled) MaterialTheme.colors.onBackground else HighOrLowlight)
}
SectionDivider()
SectionItemView(saveSMPServers, disabled = saveDisabled) {
Text(stringResource(R.string.smp_servers_save), color = if (!saveDisabled) MaterialTheme.colors.onBackground else HighOrLowlight)
}
}
SectionSpacer()
SectionDividerSpaced(maxBottomPadding = false)
SectionView {
HowToButton()
}
SectionBottomSpacer()
}
}
@@ -253,7 +248,7 @@ 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(Icons.Outlined.DoNotDisturb, null, tint = HighOrLowlight)
!srv.enabled -> Icon(painterResource(R.drawable.ic_do_not_disturb_on), null, tint = HighOrLowlight)
else -> ShowTestStatus(srv)
}
Spacer(Modifier.padding(horizontal = 4.dp))
@@ -269,7 +264,7 @@ private fun ProtocolServerView(serverProtocol: ServerProtocol, srv: ServerCfg, s
private fun HowToButton() {
val uriHandler = LocalUriHandler.current
SettingsActionItem(
Icons.Outlined.OpenInNew,
painterResource(R.drawable.ic_open_in_new),
stringResource(R.string.how_to_use_your_servers),
{ uriHandler.openUriCatching("https://github.com/simplex-chat/simplex-chat/blob/stable/docs/SERVER.md") },
textColor = MaterialTheme.colors.primary,
@@ -279,7 +274,7 @@ private fun HowToButton() {
@Composable
fun InvalidServer() {
Icon(Icons.Outlined.ErrorOutline, null, tint = MaterialTheme.colors.error)
Icon(painterResource(R.drawable.ic_error), null, tint = MaterialTheme.colors.error)
}
private fun uniqueAddress(s: ServerCfg, address: ServerAddress, servers: List<ServerCfg>): Boolean = servers.all { srv ->

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