Merge branch 'dev' into just-prefs

This commit is contained in:
bemusementpark 2024-07-18 23:07:30 +09:30
commit a196a98f00
97 changed files with 1913 additions and 1714 deletions

View File

@ -381,7 +381,7 @@ dependencies {
implementation "androidx.compose.ui:ui-tooling:$composeVersion" implementation "androidx.compose.ui:ui-tooling:$composeVersion"
implementation "androidx.compose.runtime:runtime-livedata:$composeVersion" implementation "androidx.compose.runtime:runtime-livedata:$composeVersion"
implementation "androidx.compose.foundation:foundation-layout:$composeVersion" implementation "androidx.compose.foundation:foundation-layout:$composeVersion"
implementation "androidx.compose.material:material:$composeVersion" implementation "androidx.compose.material3:material3:1.2.1"
androidTestImplementation "androidx.compose.ui:ui-test-junit4-android:$composeVersion" androidTestImplementation "androidx.compose.ui:ui-test-junit4-android:$composeVersion"
debugImplementation "androidx.compose.ui:ui-test-manifest:$composeVersion" debugImplementation "androidx.compose.ui:ui-test-manifest:$composeVersion"

View File

@ -114,14 +114,14 @@ class SessionDialogBuilder(val context: Context) {
options, options,
) { dialog, it -> onSelect(it); dialog.dismiss() } ) { dialog, it -> onSelect(it); dialog.dismiss() }
fun destructiveButton( fun dangerButton(
@StringRes text: Int, @StringRes text: Int,
@StringRes contentDescription: Int = text, @StringRes contentDescription: Int = text,
listener: () -> Unit = {} listener: () -> Unit = {}
) = button( ) = button(
text, text,
contentDescription, contentDescription,
R.style.Widget_Session_Button_Dialog_DestructiveText, R.style.Widget_Session_Button_Dialog_DangerText,
) { listener() } ) { listener() }
fun okButton(listener: (() -> Unit) = {}) = button(android.R.string.ok) { listener() } fun okButton(listener: (() -> Unit) = {}) = button(android.R.string.ok) { listener() }

View File

@ -17,6 +17,7 @@ import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.utilities.Address import org.session.libsession.utilities.Address
import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.GroupUtil
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.prefs
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.dependencies.DatabaseComponent import org.thoughtcrime.securesms.dependencies.DatabaseComponent
@ -30,7 +31,7 @@ class ProfilePictureView @JvmOverloads constructor(
private val binding = ViewProfilePictureBinding.inflate(LayoutInflater.from(context), this) private val binding = ViewProfilePictureBinding.inflate(LayoutInflater.from(context), this)
private val glide: GlideRequests = GlideApp.with(this) private val glide: GlideRequests = GlideApp.with(this)
private val prefs = TextSecurePreferences(context) private val prefs = context.prefs
private val userPublicKey = prefs.getLocalNumber() private val userPublicKey = prefs.getLocalNumber()
var publicKey: String? = null var publicKey: String? = null
var displayName: String? = null var displayName: String? = null

View File

@ -1,64 +0,0 @@
package org.thoughtcrime.securesms.components.emoji;
import android.net.Uri;
import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.conversation.v2.Util;
import java.util.LinkedList;
import java.util.List;
public class CompositeEmojiPageModel implements EmojiPageModel {
@AttrRes private final int iconAttr;
@NonNull private final List<EmojiPageModel> models;
public CompositeEmojiPageModel(@AttrRes int iconAttr, @NonNull List<EmojiPageModel> models) {
this.iconAttr = iconAttr;
this.models = models;
}
@Override
public String getKey() {
return Util.hasItems(models) ? models.get(0).getKey() : "";
}
public int getIconAttr() {
return iconAttr;
}
@Override
public @NonNull List<String> getEmoji() {
List<String> emojis = new LinkedList<>();
for (EmojiPageModel model : models) {
emojis.addAll(model.getEmoji());
}
return emojis;
}
@Override
public @NonNull List<Emoji> getDisplayEmoji() {
List<Emoji> emojis = new LinkedList<>();
for (EmojiPageModel model : models) {
emojis.addAll(model.getDisplayEmoji());
}
return emojis;
}
@Override
public boolean hasSpriteMap() {
return false;
}
@Override
public @Nullable Uri getSpriteUri() {
return null;
}
@Override
public boolean isDynamic() {
return false;
}
}

View File

@ -0,0 +1,39 @@
package org.thoughtcrime.securesms.components.emoji
import android.net.Uri
import androidx.annotation.AttrRes
import java.util.LinkedList
class CompositeEmojiPageModel(
@field:AttrRes @param:AttrRes private val iconAttr: Int,
private val models: List<EmojiPageModel>
) : EmojiPageModel {
override fun getKey(): String {
return if (models.isEmpty()) "" else models[0].key
}
override fun getIconAttr(): Int { return iconAttr }
override fun getEmoji(): List<String> {
val emojis: MutableList<String> = LinkedList()
for (model in models) {
emojis.addAll(model.emoji)
}
return emojis
}
override fun getDisplayEmoji(): List<Emoji> {
val emojis: MutableList<Emoji> = LinkedList()
for (model in models) {
emojis.addAll(model.displayEmoji)
}
return emojis
}
override fun hasSpriteMap(): Boolean { return false }
override fun getSpriteUri(): Uri? { return null }
override fun isDynamic(): Boolean { return false }
}

View File

@ -2,7 +2,7 @@ package org.thoughtcrime.securesms.components.menu
import android.content.Context import android.content.Context
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.annotation.ColorRes import androidx.annotation.ColorInt
/** /**
* Represents an action to be rendered * Represents an action to be rendered
@ -13,5 +13,5 @@ data class ActionItem(
val action: Runnable, val action: Runnable,
val contentDescription: Int? = null, val contentDescription: Int? = null,
val subtitle: ((Context) -> CharSequence?)? = null, val subtitle: ((Context) -> CharSequence?)? = null,
@ColorRes val color: Int? = null, @ColorInt val color: Int? = null,
) )

View File

@ -78,7 +78,7 @@ class ContextMenuList(recyclerView: RecyclerView, onItemClick: () -> Unit) {
override fun bind(model: DisplayItem) { override fun bind(model: DisplayItem) {
val item = model.item val item = model.item
val color = item.color?.let { ContextCompat.getColor(context, it) } val color = item.color
if (item.iconRes > 0) { if (item.iconRes > 0) {
val typedValue = TypedValue() val typedValue = TypedValue()

View File

@ -14,7 +14,6 @@ import org.session.libsession.utilities.ExpirationUtil
import org.session.libsession.utilities.SSKEnvironment.MessageExpirationManagerProtocol import org.session.libsession.utilities.SSKEnvironment.MessageExpirationManagerProtocol
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.getExpirationTypeDisplayValue import org.session.libsession.utilities.getExpirationTypeDisplayValue
import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.showSessionDialog import org.thoughtcrime.securesms.showSessionDialog
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
@ -57,7 +56,7 @@ class DisappearingMessages @Inject constructor(
context.getExpirationTypeDisplayValue(message.isNotDisappearAfterRead) context.getExpirationTypeDisplayValue(message.isNotDisappearAfterRead)
) )
}) })
destructiveButton( dangerButton(
text = if (message.expiresIn == 0L) R.string.dialog_disappearing_messages_follow_setting_confirm else R.string.dialog_disappearing_messages_follow_setting_set, text = if (message.expiresIn == 0L) R.string.dialog_disappearing_messages_follow_setting_confirm else R.string.dialog_disappearing_messages_follow_setting_set,
contentDescription = if (message.expiresIn == 0L) R.string.AccessibilityId_confirm else R.string.AccessibilityId_set_button contentDescription = if (message.expiresIn == 0L) R.string.AccessibilityId_confirm else R.string.AccessibilityId_set_button
) { ) {

View File

@ -13,8 +13,8 @@ import kotlin.time.Duration.Companion.seconds
fun State.toUiState() = UiState( fun State.toUiState() = UiState(
cards = listOfNotNull( cards = listOfNotNull(
typeOptions()?.let { ExpiryOptionsCard(GetString(R.string.activity_disappearing_messages_delete_type), it) }, typeOptions()?.let { ExpiryOptionsCardData(GetString(R.string.activity_disappearing_messages_delete_type), it) },
timeOptions()?.let { ExpiryOptionsCard(GetString(R.string.activity_disappearing_messages_timer), it) } timeOptions()?.let { ExpiryOptionsCardData(GetString(R.string.activity_disappearing_messages_timer), it) }
), ),
showGroupFooter = isGroup && isNewConfigEnabled, showGroupFooter = isGroup && isNewConfigEnabled,
showSetButton = isSelfAdmin showSetButton = isSelfAdmin

View File

@ -3,11 +3,13 @@ package org.thoughtcrime.securesms.conversation.disappearingmessages.ui
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -18,15 +20,15 @@ import androidx.compose.ui.unit.dp
import network.loki.messenger.R import network.loki.messenger.R
import network.loki.messenger.libsession_util.util.ExpiryMode import network.loki.messenger.libsession_util.util.ExpiryMode
import org.thoughtcrime.securesms.ui.Callbacks import org.thoughtcrime.securesms.ui.Callbacks
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.NoOpCallbacks import org.thoughtcrime.securesms.ui.NoOpCallbacks
import org.thoughtcrime.securesms.ui.OptionsCard import org.thoughtcrime.securesms.ui.OptionsCard
import org.thoughtcrime.securesms.ui.RadioOption import org.thoughtcrime.securesms.ui.RadioOption
import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.components.SlimOutlineButton import org.thoughtcrime.securesms.ui.components.SlimOutlineButton
import org.thoughtcrime.securesms.ui.contentDescription import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.extraSmall
import org.thoughtcrime.securesms.ui.fadingEdges import org.thoughtcrime.securesms.ui.fadingEdges
import org.thoughtcrime.securesms.ui.theme.LocalType
typealias ExpiryCallbacks = Callbacks<ExpiryMode> typealias ExpiryCallbacks = Callbacks<ExpiryMode>
typealias ExpiryRadioOption = RadioOption<ExpiryMode> typealias ExpiryRadioOption = RadioOption<ExpiryMode>
@ -39,26 +41,32 @@ fun DisappearingMessages(
) { ) {
val scrollState = rememberScrollState() val scrollState = rememberScrollState()
Column(modifier = modifier.padding(horizontal = LocalDimensions.current.margin)) { Column(modifier = modifier.padding(horizontal = LocalDimensions.current.spacing)) {
Box(modifier = Modifier.weight(1f)) { Box(modifier = Modifier.weight(1f)) {
Column( Column(
modifier = Modifier modifier = Modifier
.padding(bottom = 20.dp) .padding(vertical = LocalDimensions.current.spacing)
.verticalScroll(scrollState) .verticalScroll(scrollState)
.fadingEdges(scrollState), .fadingEdges(scrollState),
verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing)
) { ) {
state.cards.forEach { state.cards.forEachIndexed { index, option ->
OptionsCard(it, callbacks) OptionsCard(option, callbacks)
// add spacing if not the last item
if(index != state.cards.lastIndex){
Spacer(modifier = Modifier.height(LocalDimensions.current.spacing))
}
} }
if (state.showGroupFooter) Text( if (state.showGroupFooter) Text(
text = stringResource(R.string.activity_disappearing_messages_group_footer), text = stringResource(R.string.activity_disappearing_messages_group_footer),
style = extraSmall, style = LocalType.current.extraSmall,
fontWeight = FontWeight(400), fontWeight = FontWeight(400),
color = LocalColors.current.textSecondary, color = LocalColors.current.textSecondary,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth() modifier = Modifier
.fillMaxWidth()
.padding(top = LocalDimensions.current.xsSpacing)
) )
} }
} }
@ -68,7 +76,7 @@ fun DisappearingMessages(
modifier = Modifier modifier = Modifier
.contentDescription(R.string.AccessibilityId_set_button) .contentDescription(R.string.AccessibilityId_set_button)
.align(Alignment.CenterHorizontally) .align(Alignment.CenterHorizontally)
.padding(bottom = 20.dp), .padding(bottom = LocalDimensions.current.spacing),
onClick = callbacks::onSetClick onClick = callbacks::onSetClick
) )
} }

View File

@ -10,9 +10,9 @@ import androidx.compose.ui.unit.dp
import network.loki.messenger.libsession_util.util.ExpiryMode import network.loki.messenger.libsession_util.util.ExpiryMode
import org.thoughtcrime.securesms.conversation.disappearingmessages.ExpiryType import org.thoughtcrime.securesms.conversation.disappearingmessages.ExpiryType
import org.thoughtcrime.securesms.conversation.disappearingmessages.State import org.thoughtcrime.securesms.conversation.disappearingmessages.State
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.color.Colors import org.thoughtcrime.securesms.ui.theme.ThemeColors
import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
@Preview(widthDp = 450, heightDp = 700) @Preview(widthDp = 450, heightDp = 700)
@Composable @Composable
@ -51,7 +51,7 @@ class StatePreviewParameterProvider : PreviewParameterProvider<State> {
@Preview @Preview
@Composable @Composable
fun PreviewThemes( fun PreviewThemes(
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
) { ) {
PreviewTheme(colors) { PreviewTheme(colors) {
DisappearingMessages( DisappearingMessages(

View File

@ -5,15 +5,15 @@ import network.loki.messenger.libsession_util.util.ExpiryMode
import org.thoughtcrime.securesms.ui.GetString import org.thoughtcrime.securesms.ui.GetString
import org.thoughtcrime.securesms.ui.RadioOption import org.thoughtcrime.securesms.ui.RadioOption
typealias ExpiryOptionsCard = OptionsCard<ExpiryMode> typealias ExpiryOptionsCardData = OptionsCardData<ExpiryMode>
data class UiState( data class UiState(
val cards: List<ExpiryOptionsCard> = emptyList(), val cards: List<ExpiryOptionsCardData> = emptyList(),
val showGroupFooter: Boolean = false, val showGroupFooter: Boolean = false,
val showSetButton: Boolean = true val showSetButton: Boolean = true
) { ) {
constructor( constructor(
vararg cards: ExpiryOptionsCard, vararg cards: ExpiryOptionsCardData,
showGroupFooter: Boolean = false, showGroupFooter: Boolean = false,
showSetButton: Boolean = true, showSetButton: Boolean = true,
): this( ): this(
@ -23,7 +23,7 @@ data class UiState(
) )
} }
data class OptionsCard<T>( data class OptionsCardData<T>(
val title: GetString, val title: GetString,
val options: List<RadioOption<T>> val options: List<RadioOption<T>>
) { ) {

View File

@ -7,8 +7,9 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Surface import androidx.compose.material3.MaterialTheme
import androidx.compose.material.Text import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
@ -21,23 +22,25 @@ import org.thoughtcrime.securesms.conversation.start.NullStartConversationDelega
import org.thoughtcrime.securesms.conversation.start.StartConversationDelegate import org.thoughtcrime.securesms.conversation.start.StartConversationDelegate
import org.thoughtcrime.securesms.ui.Divider import org.thoughtcrime.securesms.ui.Divider
import org.thoughtcrime.securesms.ui.ItemButton import org.thoughtcrime.securesms.ui.ItemButton
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
import org.thoughtcrime.securesms.ui.color.Colors import org.thoughtcrime.securesms.ui.theme.ThemeColors
import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.components.AppBar import org.thoughtcrime.securesms.ui.components.AppBar
import org.thoughtcrime.securesms.ui.components.QrImage import org.thoughtcrime.securesms.ui.components.QrImage
import org.thoughtcrime.securesms.ui.contentDescription import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.small import org.thoughtcrime.securesms.ui.theme.LocalType
import org.thoughtcrime.securesms.ui.xl
@Composable @Composable
internal fun StartConversationScreen( internal fun StartConversationScreen(
accountId: String, accountId: String,
delegate: StartConversationDelegate delegate: StartConversationDelegate
) { ) {
Column(modifier = Modifier.background(LocalColors.current.backgroundSecondary)) { Column(modifier = Modifier.background(
LocalColors.current.backgroundSecondary,
shape = MaterialTheme.shapes.small
)) {
AppBar(stringResource(R.string.dialog_start_conversation_title), onClose = delegate::onDialogClosePressed) AppBar(stringResource(R.string.dialog_start_conversation_title), onClose = delegate::onDialogClosePressed)
Surface( Surface(
modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection()), modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection()),
@ -74,18 +77,18 @@ internal fun StartConversationScreen(
) )
Column( Column(
modifier = Modifier modifier = Modifier
.padding(horizontal = LocalDimensions.current.margin) .padding(horizontal = LocalDimensions.current.spacing)
.padding(top = LocalDimensions.current.itemSpacing) .padding(top = LocalDimensions.current.spacing)
.padding(bottom = LocalDimensions.current.margin) .padding(bottom = LocalDimensions.current.spacing)
) { ) {
Text(stringResource(R.string.accountIdYours), style = xl) Text(stringResource(R.string.accountIdYours), style = LocalType.current.xl)
Spacer(modifier = Modifier.height(LocalDimensions.current.xxxsItemSpacing)) Spacer(modifier = Modifier.height(LocalDimensions.current.xxsSpacing))
Text( Text(
text = stringResource(R.string.qrYoursDescription), text = stringResource(R.string.qrYoursDescription),
color = LocalColors.current.textSecondary, color = LocalColors.current.textSecondary,
style = small style = LocalType.current.small
) )
Spacer(modifier = Modifier.height(LocalDimensions.current.smallItemSpacing)) Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing))
QrImage( QrImage(
string = accountId, string = accountId,
Modifier.contentDescription(R.string.AccessibilityId_qr_code), Modifier.contentDescription(R.string.AccessibilityId_qr_code),
@ -100,7 +103,7 @@ internal fun StartConversationScreen(
@Preview @Preview
@Composable @Composable
private fun PreviewStartConversationScreen( private fun PreviewStartConversationScreen(
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
) { ) {
PreviewTheme(colors) { PreviewTheme(colors) {
StartConversationScreen( StartConversationScreen(

View File

@ -8,23 +8,23 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.base import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.color.LocalColors
import org.thoughtcrime.securesms.ui.components.AppBar import org.thoughtcrime.securesms.ui.components.AppBar
import org.thoughtcrime.securesms.ui.components.SlimOutlineButton import org.thoughtcrime.securesms.ui.components.SlimOutlineButton
import org.thoughtcrime.securesms.ui.components.SlimOutlineCopyButton import org.thoughtcrime.securesms.ui.components.SlimOutlineCopyButton
import org.thoughtcrime.securesms.ui.components.border import org.thoughtcrime.securesms.ui.components.border
import org.thoughtcrime.securesms.ui.contentDescription import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.small import org.thoughtcrime.securesms.ui.theme.LocalType
@Composable @Composable
internal fun InviteFriend( internal fun InviteFriend(
@ -34,10 +34,14 @@ internal fun InviteFriend(
copyPublicKey: () -> Unit = {}, copyPublicKey: () -> Unit = {},
sendInvitation: () -> Unit = {}, sendInvitation: () -> Unit = {},
) { ) {
Column(modifier = Modifier.background(LocalColors.current.backgroundSecondary)) { Column(modifier = Modifier.background(
LocalColors.current.backgroundSecondary,
shape = MaterialTheme.shapes.small
)) {
AppBar(stringResource(R.string.invite_a_friend), onBack = onBack, onClose = onClose) AppBar(stringResource(R.string.invite_a_friend), onBack = onBack, onClose = onClose)
Column( Column(
modifier = Modifier.padding(horizontal = LocalDimensions.current.itemSpacing), modifier = Modifier.padding(horizontal = LocalDimensions.current.spacing)
.padding(top = LocalDimensions.current.spacing),
) { ) {
Text( Text(
accountId, accountId,
@ -45,24 +49,24 @@ internal fun InviteFriend(
.contentDescription(R.string.AccessibilityId_account_id) .contentDescription(R.string.AccessibilityId_account_id)
.fillMaxWidth() .fillMaxWidth()
.border() .border()
.padding(LocalDimensions.current.smallMargin), .padding(LocalDimensions.current.spacing),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
style = base style = LocalType.current.base
) )
Spacer(modifier = Modifier.height(LocalDimensions.current.xsItemSpacing)) Spacer(modifier = Modifier.height(LocalDimensions.current.xsSpacing))
Text( Text(
stringResource(R.string.invite_your_friend_to_chat_with_you_on_session_by_sharing_your_account_id_with_them), stringResource(R.string.invite_your_friend_to_chat_with_you_on_session_by_sharing_your_account_id_with_them),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
style = small, style = LocalType.current.small,
color = LocalColors.current.textSecondary, color = LocalColors.current.textSecondary,
modifier = Modifier.padding(horizontal = LocalDimensions.current.smallItemSpacing) modifier = Modifier.padding(horizontal = LocalDimensions.current.smallSpacing)
) )
Spacer(modifier = Modifier.height(LocalDimensions.current.smallItemSpacing)) Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing))
Row(horizontalArrangement = spacedBy(LocalDimensions.current.smallItemSpacing)) { Row(horizontalArrangement = spacedBy(LocalDimensions.current.smallSpacing)) {
SlimOutlineButton( SlimOutlineButton(
stringResource(R.string.share), stringResource(R.string.share),
modifier = Modifier modifier = Modifier

View File

@ -3,17 +3,19 @@ package org.thoughtcrime.securesms.conversation.start.newmessage
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Text import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -23,12 +25,13 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.emptyFlow
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.onboarding.ui.ContinuePrimaryOutlineButton
import org.thoughtcrime.securesms.ui.LoadingArcOr import org.thoughtcrime.securesms.ui.LoadingArcOr
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
import org.thoughtcrime.securesms.ui.color.Colors import org.thoughtcrime.securesms.ui.theme.ThemeColors
import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.components.AppBar import org.thoughtcrime.securesms.ui.components.AppBar
import org.thoughtcrime.securesms.ui.components.BorderlessButtonWithIcon import org.thoughtcrime.securesms.ui.components.BorderlessButtonWithIcon
import org.thoughtcrime.securesms.ui.components.MaybeScanQrCode import org.thoughtcrime.securesms.ui.components.MaybeScanQrCode
@ -36,7 +39,7 @@ import org.thoughtcrime.securesms.ui.components.PrimaryOutlineButton
import org.thoughtcrime.securesms.ui.components.SessionOutlinedTextField import org.thoughtcrime.securesms.ui.components.SessionOutlinedTextField
import org.thoughtcrime.securesms.ui.components.SessionTabRow import org.thoughtcrime.securesms.ui.components.SessionTabRow
import org.thoughtcrime.securesms.ui.contentDescription import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.small import org.thoughtcrime.securesms.ui.theme.LocalType
private val TITLES = listOf(R.string.enter_account_id, R.string.qrScan) private val TITLES = listOf(R.string.enter_account_id, R.string.qrScan)
@ -52,7 +55,10 @@ internal fun NewMessage(
) { ) {
val pagerState = rememberPagerState { TITLES.size } val pagerState = rememberPagerState { TITLES.size }
Column(modifier = Modifier.background(LocalColors.current.backgroundSecondary)) { Column(modifier = Modifier.background(
LocalColors.current.backgroundSecondary,
shape = MaterialTheme.shapes.small
)) {
AppBar(stringResource(R.string.messageNew), onClose = onClose, onBack = onBack) AppBar(stringResource(R.string.messageNew), onClose = onClose, onBack = onBack)
SessionTabRow(pagerState, TITLES) SessionTabRow(pagerState, TITLES)
HorizontalPager(pagerState) { HorizontalPager(pagerState) {
@ -70,7 +76,6 @@ private fun EnterAccountId(
callbacks: Callbacks, callbacks: Callbacks,
onHelp: () -> Unit = {} onHelp: () -> Unit = {}
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@ -78,14 +83,13 @@ private fun EnterAccountId(
.imePadding() .imePadding()
) { ) {
Column( Column(
modifier = Modifier.padding(horizontal = LocalDimensions.current.xxsMargin, vertical = LocalDimensions.current.xsMargin), modifier = Modifier.padding(vertical = LocalDimensions.current.spacing),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.xsMargin)
) { ) {
SessionOutlinedTextField( SessionOutlinedTextField(
text = state.newMessageIdOrOns, text = state.newMessageIdOrOns,
modifier = Modifier modifier = Modifier
.padding(horizontal = LocalDimensions.current.smallMargin), .padding(horizontal = LocalDimensions.current.spacing),
contentDescription = "Session id input box", contentDescription = "Session id input box",
placeholder = stringResource(R.string.accountIdOrOnsEnter), placeholder = stringResource(R.string.accountIdOrOnsEnter),
onChange = callbacks::onChange, onChange = callbacks::onChange,
@ -94,26 +98,32 @@ private fun EnterAccountId(
isTextErrorColor = state.isTextErrorColor isTextErrorColor = state.isTextErrorColor
) )
Spacer(modifier = Modifier.height(LocalDimensions.current.xxxsSpacing))
BorderlessButtonWithIcon( BorderlessButtonWithIcon(
text = stringResource(R.string.messageNewDescription), text = stringResource(R.string.messageNewDescription),
modifier = Modifier modifier = Modifier
.contentDescription(R.string.AccessibilityId_help_desk_link) .contentDescription(R.string.AccessibilityId_help_desk_link)
.padding(horizontal = LocalDimensions.current.margin) .padding(horizontal = LocalDimensions.current.mediumSpacing)
.fillMaxWidth(), .fillMaxWidth(),
style = small, style = LocalType.current.small,
color = LocalColors.current.textSecondary, color = LocalColors.current.textSecondary,
iconRes = R.drawable.ic_circle_question_mark, iconRes = R.drawable.ic_circle_question_mark,
onClick = onHelp onClick = onHelp
) )
} }
AnimatedVisibility(state.isNextButtonVisible) { Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing))
Spacer(Modifier.weight(2f))
PrimaryOutlineButton( PrimaryOutlineButton(
modifier = Modifier modifier = Modifier
.align(Alignment.CenterHorizontally) .align(Alignment.CenterHorizontally)
.padding(horizontal = LocalDimensions.current.largeMargin) .padding(horizontal = LocalDimensions.current.xlargeSpacing)
.padding(bottom = LocalDimensions.current.smallSpacing)
.fillMaxWidth() .fillMaxWidth()
.contentDescription(R.string.next), .contentDescription(R.string.next),
enabled = state.isNextButtonEnabled,
onClick = callbacks::onContinue onClick = callbacks::onContinue
) { ) {
LoadingArcOr(state.loading) { LoadingArcOr(state.loading) {
@ -122,12 +132,11 @@ private fun EnterAccountId(
} }
} }
} }
}
@Preview @Preview
@Composable @Composable
private fun PreviewNewMessage( private fun PreviewNewMessage(
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
) { ) {
PreviewTheme(colors) { PreviewTheme(colors) {
NewMessage(State("z")) NewMessage(State("z"))

View File

@ -6,7 +6,6 @@ import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -22,8 +21,6 @@ import org.session.libsignal.utilities.timeout
import org.thoughtcrime.securesms.ui.GetString import org.thoughtcrime.securesms.ui.GetString
import java.util.concurrent.TimeoutException import java.util.concurrent.TimeoutException
import javax.inject.Inject import javax.inject.Inject
import kotlin.coroutines.cancellation.CancellationException
import kotlin.time.Duration.Companion.seconds
@HiltViewModel @HiltViewModel
internal class NewMessageViewModel @Inject constructor( internal class NewMessageViewModel @Inject constructor(
@ -112,7 +109,7 @@ internal data class State(
val error: GetString? = null, val error: GetString? = null,
val loading: Boolean = false val loading: Boolean = false
) { ) {
val isNextButtonVisible: Boolean get() = newMessageIdOrOns.isNotBlank() val isNextButtonEnabled: Boolean get() = newMessageIdOrOns.isNotBlank()
} }
internal data class Success(val publicKey: String) internal data class Success(val publicKey: String)

View File

@ -1120,7 +1120,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
showSessionDialog { showSessionDialog {
title(R.string.RecipientPreferenceActivity_block_this_contact_question) title(R.string.RecipientPreferenceActivity_block_this_contact_question)
text(R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact) text(R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact)
destructiveButton(R.string.RecipientPreferenceActivity_block, R.string.AccessibilityId_block_confirm) { dangerButton(R.string.RecipientPreferenceActivity_block, R.string.AccessibilityId_block_confirm) {
viewModel.block() viewModel.block()
if (deleteThread) { if (deleteThread) {
viewModel.deleteThread() viewModel.deleteThread()
@ -1163,7 +1163,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
showSessionDialog { showSessionDialog {
title(R.string.ConversationActivity_unblock_this_contact_question) title(R.string.ConversationActivity_unblock_this_contact_question)
text(R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact) text(R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact)
destructiveButton( dangerButton(
R.string.ConversationActivity_unblock, R.string.ConversationActivity_unblock,
R.string.AccessibilityId_block_confirm R.string.AccessibilityId_block_confirm
) { viewModel.unblock() } ) { viewModel.unblock() }

View File

@ -546,7 +546,8 @@ class ConversationReactionOverlay : FrameLayout {
} }
// Delete message // Delete message
if (userCanDeleteSelectedItems(context, message, openGroup, userPublicKey, blindedPublicKey)) { if (userCanDeleteSelectedItems(context, message, openGroup, userPublicKey, blindedPublicKey)) {
items += ActionItem(R.attr.menu_trash_icon, R.string.delete, { handleActionItemClicked(Action.DELETE) }, R.string.AccessibilityId_delete_message, message.subtitle, R.color.destructive) items += ActionItem(R.attr.menu_trash_icon, R.string.delete, { handleActionItemClicked(Action.DELETE) },
R.string.AccessibilityId_delete_message, message.subtitle, ThemeUtil.getThemedColor(context, R.attr.danger))
} }
// Ban user // Ban user
if (userCanBanSelectedUsers(context, message, openGroup, userPublicKey, blindedPublicKey)) { if (userCanBanSelectedUsers(context, message, openGroup, userPublicKey, blindedPublicKey)) {

View File

@ -27,9 +27,9 @@ import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Icon import androidx.compose.material3.Icon
import androidx.compose.material.Surface import androidx.compose.material3.Surface
import androidx.compose.material.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -63,20 +63,19 @@ import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin
import org.thoughtcrime.securesms.ui.Divider import org.thoughtcrime.securesms.ui.Divider
import org.thoughtcrime.securesms.ui.GetString import org.thoughtcrime.securesms.ui.GetString
import org.thoughtcrime.securesms.ui.HorizontalPagerIndicator import org.thoughtcrime.securesms.ui.HorizontalPagerIndicator
import org.thoughtcrime.securesms.ui.ItemButton
import org.thoughtcrime.securesms.ui.LargeItemButton import org.thoughtcrime.securesms.ui.LargeItemButton
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
import org.thoughtcrime.securesms.ui.TitledText import org.thoughtcrime.securesms.ui.TitledText
import org.thoughtcrime.securesms.ui.base import org.thoughtcrime.securesms.ui.theme.ThemeColors
import org.thoughtcrime.securesms.ui.baseBold import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.baseMonospace import org.thoughtcrime.securesms.ui.theme.blackAlpha40
import org.thoughtcrime.securesms.ui.color.Colors import org.thoughtcrime.securesms.ui.theme.dangerButtonColors
import org.thoughtcrime.securesms.ui.color.LocalColors
import org.thoughtcrime.securesms.ui.color.blackAlpha40
import org.thoughtcrime.securesms.ui.color.destructiveButtonColors
import org.thoughtcrime.securesms.ui.setComposeContent import org.thoughtcrime.securesms.ui.setComposeContent
import org.thoughtcrime.securesms.ui.theme.LocalType
import org.thoughtcrime.securesms.ui.theme.bold
import org.thoughtcrime.securesms.ui.theme.monospace
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
@ -152,12 +151,12 @@ fun MessageDetails(
Column( Column(
modifier = Modifier modifier = Modifier
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(vertical = LocalDimensions.current.smallItemSpacing), .padding(vertical = LocalDimensions.current.smallSpacing),
verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing) verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing)
) { ) {
state.record?.let { message -> state.record?.let { message ->
AndroidView( AndroidView(
modifier = Modifier.padding(horizontal = LocalDimensions.current.margin), modifier = Modifier.padding(horizontal = LocalDimensions.current.spacing),
factory = { factory = {
ViewVisibleMessageContentBinding.inflate(LayoutInflater.from(it)).mainContainerConstraint.apply { ViewVisibleMessageContentBinding.inflate(LayoutInflater.from(it)).mainContainerConstraint.apply {
bind( bind(
@ -193,7 +192,7 @@ fun CellMetadata(
state.apply { state.apply {
if (listOfNotNull(sent, received, error, senderInfo).isEmpty()) return if (listOfNotNull(sent, received, error, senderInfo).isEmpty()) return
CellWithPaddingAndMargin { CellWithPaddingAndMargin {
Column(verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing)) { Column(verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing)) {
TitledText(sent) TitledText(sent)
TitledText(received) TitledText(received)
TitledErrorText(error) TitledErrorText(error)
@ -237,7 +236,7 @@ fun CellButtons(
LargeItemButton( LargeItemButton(
R.string.delete, R.string.delete,
R.drawable.ic_message_details__trash, R.drawable.ic_message_details__trash,
colors = destructiveButtonColors(), colors = dangerButtonColors(),
onClick = onDelete onClick = onDelete
) )
} }
@ -251,7 +250,7 @@ fun Carousel(attachments: List<Attachment>, onClick: (Int) -> Unit) {
val pagerState = rememberPagerState { attachments.size } val pagerState = rememberPagerState { attachments.size }
Column(verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing)) { Column(verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing)) {
Row { Row {
CarouselPrevButton(pagerState) CarouselPrevButton(pagerState)
Box(modifier = Modifier.weight(1f)) { Box(modifier = Modifier.weight(1f)) {
@ -260,7 +259,7 @@ fun Carousel(attachments: List<Attachment>, onClick: (Int) -> Unit) {
ExpandButton( ExpandButton(
modifier = Modifier modifier = Modifier
.align(Alignment.BottomEnd) .align(Alignment.BottomEnd)
.padding(LocalDimensions.current.xxsItemSpacing) .padding(LocalDimensions.current.xxsSpacing)
) { onClick(pagerState.currentPage) } ) { onClick(pagerState.currentPage) }
} }
CarouselNextButton(pagerState) CarouselNextButton(pagerState)
@ -313,7 +312,7 @@ fun ExpandButton(modifier: Modifier = Modifier, onClick: () -> Unit) {
@Preview @Preview
@Composable @Composable
fun PreviewMessageDetails( fun PreviewMessageDetails(
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
) { ) {
PreviewTheme(colors) { PreviewTheme(colors) {
MessageDetails( MessageDetails(
@ -340,8 +339,8 @@ fun FileDetails(fileDetails: List<TitledText>) {
Cell { Cell {
FlowRow( FlowRow(
modifier = Modifier.padding(horizontal = LocalDimensions.current.xsItemSpacing, vertical = LocalDimensions.current.itemSpacing), modifier = Modifier.padding(horizontal = LocalDimensions.current.xsSpacing, vertical = LocalDimensions.current.spacing),
verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing) verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing)
) { ) {
fileDetails.forEach { fileDetails.forEach {
BoxWithConstraints { BoxWithConstraints {
@ -349,7 +348,7 @@ fun FileDetails(fileDetails: List<TitledText>) {
it, it,
modifier = Modifier modifier = Modifier
.widthIn(min = maxWidth.div(2)) .widthIn(min = maxWidth.div(2))
.padding(horizontal = LocalDimensions.current.xsItemSpacing) .padding(horizontal = LocalDimensions.current.xsSpacing)
.width(IntrinsicSize.Max) .width(IntrinsicSize.Max)
) )
} }
@ -362,7 +361,7 @@ fun FileDetails(fileDetails: List<TitledText>) {
fun TitledErrorText(titledText: TitledText?) { fun TitledErrorText(titledText: TitledText?) {
TitledText( TitledText(
titledText, titledText,
style = base, style = LocalType.current.base,
color = LocalColors.current.danger color = LocalColors.current.danger
) )
} }
@ -371,7 +370,7 @@ fun TitledErrorText(titledText: TitledText?) {
fun TitledMonospaceText(titledText: TitledText?) { fun TitledMonospaceText(titledText: TitledText?) {
TitledText( TitledText(
titledText, titledText,
style = baseMonospace style = LocalType.current.base.monospace()
) )
} }
@ -379,7 +378,7 @@ fun TitledMonospaceText(titledText: TitledText?) {
fun TitledText( fun TitledText(
titledText: TitledText?, titledText: TitledText?,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
style: TextStyle = base, style: TextStyle = LocalType.current.base,
color: Color = Color.Unspecified color: Color = Color.Unspecified
) { ) {
titledText?.apply { titledText?.apply {
@ -396,8 +395,8 @@ fun TitledText(
@Composable @Composable
fun TitledView(title: GetString, modifier: Modifier = Modifier, content: @Composable () -> Unit) { fun TitledView(title: GetString, modifier: Modifier = Modifier, content: @Composable () -> Unit) {
Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.xxxsItemSpacing)) { Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.xxxsSpacing)) {
Text(title.string(), style = baseBold) Text(title.string(), style = LocalType.current.base.bold())
content() content()
} }
} }

View File

@ -1,381 +0,0 @@
/*
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.conversation.v2;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.StyleSpan;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.annimon.stream.Stream;
import com.google.android.mms.pdu_alt.CharacterSets;
import com.google.android.mms.pdu_alt.EncodedStringValue;
import org.session.libsignal.utilities.Log;
import org.thoughtcrime.securesms.components.ComposeText;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import network.loki.messenger.R;
public class Util {
private static final String TAG = Log.tag(Util.class);
private static final long BUILD_LIFESPAN = TimeUnit.DAYS.toMillis(90);
public static <T> List<T> asList(T... elements) {
List<T> result = new LinkedList<>();
Collections.addAll(result, elements);
return result;
}
public static String join(String[] list, String delimiter) {
return join(Arrays.asList(list), delimiter);
}
public static <T> String join(Collection<T> list, String delimiter) {
StringBuilder result = new StringBuilder();
int i = 0;
for (T item : list) {
result.append(item);
if (++i < list.size())
result.append(delimiter);
}
return result.toString();
}
public static String join(long[] list, String delimeter) {
List<Long> boxed = new ArrayList<>(list.length);
for (int i = 0; i < list.length; i++) {
boxed.add(list[i]);
}
return join(boxed, delimeter);
}
@SafeVarargs
public static @NonNull <E> List<E> join(@NonNull List<E>... lists) {
int totalSize = Stream.of(lists).reduce(0, (sum, list) -> sum + list.size());
List<E> joined = new ArrayList<>(totalSize);
for (List<E> list : lists) {
joined.addAll(list);
}
return joined;
}
public static String join(List<Long> list, String delimeter) {
StringBuilder sb = new StringBuilder();
for (int j = 0; j < list.size(); j++) {
if (j != 0) sb.append(delimeter);
sb.append(list.get(j));
}
return sb.toString();
}
public static String rightPad(String value, int length) {
if (value.length() >= length) {
return value;
}
StringBuilder out = new StringBuilder(value);
while (out.length() < length) {
out.append(" ");
}
return out.toString();
}
public static boolean isEmpty(EncodedStringValue[] value) {
return value == null || value.length == 0;
}
public static boolean isEmpty(ComposeText value) {
return value == null || value.getText() == null || TextUtils.isEmpty(value.getTextTrimmed());
}
public static boolean isEmpty(Collection<?> collection) {
return collection == null || collection.isEmpty();
}
public static boolean isEmpty(@Nullable CharSequence charSequence) {
return charSequence == null || charSequence.length() == 0;
}
public static boolean hasItems(@Nullable Collection<?> collection) {
return collection != null && !collection.isEmpty();
}
public static <K, V> V getOrDefault(@NonNull Map<K, V> map, K key, V defaultValue) {
return map.containsKey(key) ? map.get(key) : defaultValue;
}
public static String getFirstNonEmpty(String... values) {
for (String value : values) {
if (!Util.isEmpty(value)) {
return value;
}
}
return "";
}
public static @NonNull String emptyIfNull(@Nullable String value) {
return value != null ? value : "";
}
public static @NonNull CharSequence emptyIfNull(@Nullable CharSequence value) {
return value != null ? value : "";
}
public static CharSequence getBoldedString(String value) {
SpannableString spanned = new SpannableString(value);
spanned.setSpan(new StyleSpan(Typeface.BOLD), 0,
spanned.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return spanned;
}
public static @NonNull String toIsoString(byte[] bytes) {
try {
return new String(bytes, CharacterSets.MIMENAME_ISO_8859_1);
} catch (UnsupportedEncodingException e) {
throw new AssertionError("ISO_8859_1 must be supported!");
}
}
public static byte[] toIsoBytes(String isoString) {
try {
return isoString.getBytes(CharacterSets.MIMENAME_ISO_8859_1);
} catch (UnsupportedEncodingException e) {
throw new AssertionError("ISO_8859_1 must be supported!");
}
}
public static byte[] toUtf8Bytes(String utf8String) {
try {
return utf8String.getBytes(CharacterSets.MIMENAME_UTF_8);
} catch (UnsupportedEncodingException e) {
throw new AssertionError("UTF_8 must be supported!");
}
}
public static void wait(Object lock, long timeout) {
try {
lock.wait(timeout);
} catch (InterruptedException ie) {
throw new AssertionError(ie);
}
}
public static List<String> split(String source, String delimiter) {
List<String> results = new LinkedList<>();
if (TextUtils.isEmpty(source)) {
return results;
}
String[] elements = source.split(delimiter);
Collections.addAll(results, elements);
return results;
}
public static byte[][] split(byte[] input, int firstLength, int secondLength) {
byte[][] parts = new byte[2][];
parts[0] = new byte[firstLength];
System.arraycopy(input, 0, parts[0], 0, firstLength);
parts[1] = new byte[secondLength];
System.arraycopy(input, firstLength, parts[1], 0, secondLength);
return parts;
}
public static byte[] combine(byte[]... elements) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (byte[] element : elements) {
baos.write(element);
}
return baos.toByteArray();
} catch (IOException e) {
throw new AssertionError(e);
}
}
public static byte[] trim(byte[] input, int length) {
byte[] result = new byte[length];
System.arraycopy(input, 0, result, 0, result.length);
return result;
}
public static byte[] getSecretBytes(int size) {
return getSecretBytes(new SecureRandom(), size);
}
public static byte[] getSecretBytes(@NonNull SecureRandom secureRandom, int size) {
byte[] secret = new byte[size];
secureRandom.nextBytes(secret);
return secret;
}
public static <T> T getRandomElement(T[] elements) {
return elements[new SecureRandom().nextInt(elements.length)];
}
public static <T> T getRandomElement(List<T> elements) {
return elements.get(new SecureRandom().nextInt(elements.size()));
}
public static boolean equals(@Nullable Object a, @Nullable Object b) {
return a == b || (a != null && a.equals(b));
}
public static int hashCode(@Nullable Object... objects) {
return Arrays.hashCode(objects);
}
public static @Nullable Uri uri(@Nullable String uri) {
if (uri == null) return null;
else return Uri.parse(uri);
}
@TargetApi(VERSION_CODES.KITKAT)
public static boolean isLowMemory(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
return (VERSION.SDK_INT >= VERSION_CODES.KITKAT && activityManager.isLowRamDevice()) ||
activityManager.getLargeMemoryClass() <= 64;
}
public static int clamp(int value, int min, int max) {
return Math.min(Math.max(value, min), max);
}
public static long clamp(long value, long min, long max) {
return Math.min(Math.max(value, min), max);
}
public static float clamp(float value, float min, float max) {
return Math.min(Math.max(value, min), max);
}
/**
* Returns half of the difference between the given length, and the length when scaled by the
* given scale.
*/
public static float halfOffsetFromScale(int length, float scale) {
float scaledLength = length * scale;
return (length - scaledLength) / 2;
}
public static @Nullable String readTextFromClipboard(@NonNull Context context) {
{
ClipboardManager clipboardManager = (ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE);
if (clipboardManager.hasPrimaryClip() && clipboardManager.getPrimaryClip().getItemCount() > 0) {
return clipboardManager.getPrimaryClip().getItemAt(0).getText().toString();
} else {
return null;
}
}
}
public static void writeTextToClipboard(@NonNull Context context, @NonNull String text) {
writeTextToClipboard(context, context.getString(R.string.app_name), text);
}
public static void writeTextToClipboard(@NonNull Context context, @NonNull String label, @NonNull String text) {
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText(label, text);
clipboard.setPrimaryClip(clip);
}
public static int toIntExact(long value) {
if ((int)value != value) {
throw new ArithmeticException("integer overflow");
}
return (int)value;
}
public static boolean isEquals(@Nullable Long first, long second) {
return first != null && first == second;
}
@SafeVarargs
public static <T> List<T> concatenatedList(Collection <T>... items) {
final List<T> concat = new ArrayList<>(Stream.of(items).reduce(0, (sum, list) -> sum + list.size()));
for (Collection<T> list : items) {
concat.addAll(list);
}
return concat;
}
public static boolean isLong(String value) {
try {
Long.parseLong(value);
return true;
} catch (NumberFormatException e) {
return false;
}
}
public static int parseInt(String integer, int defaultValue) {
try {
return Integer.parseInt(integer);
} catch (NumberFormatException e) {
return defaultValue;
}
}
}

View File

@ -0,0 +1,384 @@
/*
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.conversation.v2
import android.annotation.TargetApi
import android.app.ActivityManager
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.graphics.Typeface
import android.net.Uri
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.text.Spannable
import android.text.SpannableString
import android.text.TextUtils
import android.text.style.StyleSpan
import android.view.View
import com.annimon.stream.Stream
import com.google.android.mms.pdu_alt.CharacterSets
import com.google.android.mms.pdu_alt.EncodedStringValue
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.UnsupportedEncodingException
import java.security.SecureRandom
import java.util.Arrays
import java.util.Collections
import java.util.concurrent.TimeUnit
import kotlin.math.max
import kotlin.math.min
import network.loki.messenger.R
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.components.ComposeText
object Util {
private val TAG: String = Log.tag(Util::class.java)
private val BUILD_LIFESPAN = TimeUnit.DAYS.toMillis(90)
fun <T> asList(vararg elements: T): List<T> {
val result = mutableListOf<T>() // LinkedList()
Collections.addAll(result, *elements)
return result
}
fun join(list: Array<String?>, delimiter: String?): String {
return join(listOf(*list), delimiter)
}
fun <T> join(list: Collection<T>, delimiter: String?): String {
val result = StringBuilder()
var i = 0
for (item in list) {
result.append(item)
if (++i < list.size) result.append(delimiter)
}
return result.toString()
}
fun join(list: LongArray, delimeter: String?): String {
val boxed: MutableList<Long> = ArrayList(list.size)
for (i in list.indices) {
boxed.add(list[i])
}
return join(boxed, delimeter)
}
@SafeVarargs
fun <E> join(vararg lists: List<E>): List<E> {
val totalSize = Stream.of(*lists).reduce(0) { sum: Int, list: List<E> -> sum + list.size }
val joined: MutableList<E> = ArrayList(totalSize)
for (list in lists) {
joined.addAll(list)
}
return joined
}
fun join(list: List<Long>, delimeter: String?): String {
val sb = StringBuilder()
for (j in list.indices) {
if (j != 0) sb.append(delimeter)
sb.append(list[j])
}
return sb.toString()
}
fun rightPad(value: String, length: Int): String {
if (value.length >= length) {
return value
}
val out = StringBuilder(value)
while (out.length < length) {
out.append(" ")
}
return out.toString()
}
fun isEmpty(value: Array<EncodedStringValue?>?): Boolean {
return value == null || value.size == 0
}
fun isEmpty(value: ComposeText?): Boolean {
return value == null || value.text == null || TextUtils.isEmpty(value.textTrimmed)
}
fun isEmpty(collection: Collection<*>?): Boolean {
return collection == null || collection.isEmpty()
}
fun isEmpty(charSequence: CharSequence?): Boolean {
return charSequence == null || charSequence.length == 0
}
fun hasItems(collection: Collection<*>?): Boolean {
return collection != null && !collection.isEmpty()
}
fun <K, V> getOrDefault(map: Map<K, V>, key: K, defaultValue: V): V? {
return if (map.containsKey(key)) map[key] else defaultValue
}
fun getFirstNonEmpty(vararg values: String?): String {
for (value in values) {
if (!value.isNullOrEmpty()) { return value }
}
return ""
}
fun emptyIfNull(value: String?): String {
return value ?: ""
}
fun emptyIfNull(value: CharSequence?): CharSequence {
return value ?: ""
}
fun getBoldedString(value: String?): CharSequence {
val spanned = SpannableString(value)
spanned.setSpan(
StyleSpan(Typeface.BOLD), 0,
spanned.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
return spanned
}
fun toIsoString(bytes: ByteArray?): String {
try {
return String(bytes!!, charset(CharacterSets.MIMENAME_ISO_8859_1))
} catch (e: UnsupportedEncodingException) {
throw AssertionError("ISO_8859_1 must be supported!")
}
}
fun toIsoBytes(isoString: String): ByteArray {
try {
return isoString.toByteArray(charset(CharacterSets.MIMENAME_ISO_8859_1))
} catch (e: UnsupportedEncodingException) {
throw AssertionError("ISO_8859_1 must be supported!")
}
}
fun toUtf8Bytes(utf8String: String): ByteArray {
try {
return utf8String.toByteArray(charset(CharacterSets.MIMENAME_UTF_8))
} catch (e: UnsupportedEncodingException) {
throw AssertionError("UTF_8 must be supported!")
}
}
fun wait(lock: Any, timeout: Long) {
try {
(lock as Object).wait(timeout)
} catch (ie: InterruptedException) {
throw AssertionError(ie)
}
}
fun split(source: String, delimiter: String): List<String> {
val results = mutableListOf<String>()
if (TextUtils.isEmpty(source)) {
return results
}
val elements =
source.split(delimiter.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
Collections.addAll(results, *elements)
return results
}
fun split(input: ByteArray?, firstLength: Int, secondLength: Int): Array<ByteArray?> {
val parts = arrayOfNulls<ByteArray>(2)
parts[0] = ByteArray(firstLength)
System.arraycopy(input, 0, parts[0], 0, firstLength)
parts[1] = ByteArray(secondLength)
System.arraycopy(input, firstLength, parts[1], 0, secondLength)
return parts
}
fun combine(vararg elements: ByteArray?): ByteArray {
try {
val baos = ByteArrayOutputStream()
for (element in elements) {
baos.write(element)
}
return baos.toByteArray()
} catch (e: IOException) {
throw AssertionError(e)
}
}
fun trim(input: ByteArray?, length: Int): ByteArray {
val result = ByteArray(length)
System.arraycopy(input, 0, result, 0, result.size)
return result
}
fun getSecretBytes(size: Int): ByteArray {
return getSecretBytes(SecureRandom(), size)
}
fun getSecretBytes(secureRandom: SecureRandom, size: Int): ByteArray {
val secret = ByteArray(size)
secureRandom.nextBytes(secret)
return secret
}
fun <T> getRandomElement(elements: Array<T>): T {
return elements[SecureRandom().nextInt(elements.size)]
}
fun <T> getRandomElement(elements: List<T>): T {
return elements[SecureRandom().nextInt(elements.size)]
}
fun equals(a: Any?, b: Any?): Boolean {
return a === b || (a != null && a == b)
}
fun hashCode(vararg objects: Any?): Int {
return objects.contentHashCode()
}
fun uri(uri: String?): Uri? {
return if (uri == null) null
else Uri.parse(uri)
}
@TargetApi(VERSION_CODES.KITKAT)
fun isLowMemory(context: Context): Boolean {
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
return (VERSION.SDK_INT >= VERSION_CODES.KITKAT && activityManager.isLowRamDevice) ||
activityManager.largeMemoryClass <= 64
}
fun clamp(value: Int, min: Int, max: Int): Int {
return min(max(value.toDouble(), min.toDouble()), max.toDouble()).toInt()
}
fun clamp(value: Long, min: Long, max: Long): Long {
return min(max(value.toDouble(), min.toDouble()), max.toDouble()).toLong()
}
fun clamp(value: Float, min: Float, max: Float): Float {
return min(max(value.toDouble(), min.toDouble()), max.toDouble()).toFloat()
}
/**
* Returns half of the difference between the given length, and the length when scaled by the
* given scale.
*/
fun halfOffsetFromScale(length: Int, scale: Float): Float {
val scaledLength = length * scale
return (length - scaledLength) / 2
}
fun readTextFromClipboard(context: Context): String? {
run {
val clipboardManager =
context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
return if (clipboardManager.hasPrimaryClip() && clipboardManager.primaryClip!!.itemCount > 0) {
clipboardManager.primaryClip!!.getItemAt(0).text.toString()
} else {
null
}
}
}
fun writeTextToClipboard(context: Context, text: String) {
writeTextToClipboard(context, context.getString(R.string.app_name), text)
}
fun writeTextToClipboard(context: Context, label: String, text: String) {
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText(label, text)
clipboard.setPrimaryClip(clip)
}
fun toIntExact(value: Long): Int {
if (value.toInt().toLong() != value) {
throw ArithmeticException("integer overflow")
}
return value.toInt()
}
fun isEquals(first: Long?, second: Long): Boolean {
return first != null && first == second
}
@SafeVarargs
fun <T> concatenatedList(vararg items: Collection<T>): List<T> {
val concat: MutableList<T> = ArrayList(
Stream.of(*items).reduce(0) { sum: Int, list: Collection<T> -> sum + list.size })
for (list in items) {
concat.addAll(list)
}
return concat
}
fun isLong(value: String): Boolean {
try {
value.toLong()
return true
} catch (e: NumberFormatException) {
return false
}
}
fun parseInt(integer: String, defaultValue: Int): Int {
return try {
integer.toInt()
} catch (e: NumberFormatException) {
defaultValue
}
}
// Method to determine if we're currently in a left-to-right or right-to-left language like Arabic
fun usingRightToLeftLanguage(context: Context): Boolean {
val config = context.resources.configuration
return config.layoutDirection == View.LAYOUT_DIRECTION_RTL
}
// Method to determine if we're currently in a left-to-right or right-to-left language like Arabic
fun usingLeftToRightLanguage(context: Context): Boolean {
val config = context.resources.configuration
return config.layoutDirection == View.LAYOUT_DIRECTION_LTR
}
}

View File

@ -36,6 +36,7 @@ import org.session.libsession.messaging.contacts.Contact.ContactContext
import org.session.libsession.messaging.open_groups.OpenGroupApi import org.session.libsession.messaging.open_groups.OpenGroupApi
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
import org.session.libsession.utilities.Address import org.session.libsession.utilities.Address
import org.session.libsession.utilities.ThemeUtil.getThemedColor
import org.session.libsession.utilities.ViewUtil import org.session.libsession.utilities.ViewUtil
import org.session.libsession.utilities.getColorFromAttr import org.session.libsession.utilities.getColorFromAttr
import org.session.libsession.utilities.modifyLayoutParams import org.session.libsession.utilities.modifyLayoutParams
@ -382,7 +383,7 @@ class VisibleMessageView : FrameLayout {
private fun getMessageStatusInfo(message: MessageRecord): MessageStatusInfo = when { private fun getMessageStatusInfo(message: MessageRecord): MessageStatusInfo = when {
message.isFailed -> message.isFailed ->
MessageStatusInfo(R.drawable.ic_delivery_status_failed, MessageStatusInfo(R.drawable.ic_delivery_status_failed,
resources.getColor(R.color.destructive, context.theme), getThemedColor(context, R.attr.danger),
R.string.delivery_status_failed R.string.delivery_status_failed
) )
message.isSyncFailed -> message.isSyncFailed ->

View File

@ -15,6 +15,7 @@ import androidx.recyclerview.widget.RecyclerView
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import network.loki.messenger.R import network.loki.messenger.R
import network.loki.messenger.databinding.ViewConversationBinding import network.loki.messenger.databinding.ViewConversationBinding
import org.session.libsession.utilities.ThemeUtil
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities.highlightMentions import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities.highlightMentions
import org.thoughtcrime.securesms.database.RecipientDatabase.NOTIFY_TYPE_ALL import org.thoughtcrime.securesms.database.RecipientDatabase.NOTIFY_TYPE_ALL
@ -67,7 +68,7 @@ class ConversationView : LinearLayout {
} }
val unreadCount = thread.unreadCount val unreadCount = thread.unreadCount
if (thread.recipient.isBlocked) { if (thread.recipient.isBlocked) {
binding.accentView.setBackgroundResource(R.color.destructive) binding.accentView.setBackgroundColor(ThemeUtil.getThemedColor(context, R.attr.danger))
binding.accentView.visibility = View.VISIBLE binding.accentView.visibility = View.VISIBLE
} else { } else {
val accentColor = context.getAccentColor() val accentColor = context.getAccentColor()
@ -120,7 +121,7 @@ class ConversationView : LinearLayout {
!thread.isOutgoing -> binding.statusIndicatorImageView.visibility = View.GONE !thread.isOutgoing -> binding.statusIndicatorImageView.visibility = View.GONE
thread.isFailed -> { thread.isFailed -> {
val drawable = ContextCompat.getDrawable(context, R.drawable.ic_error)?.mutate() val drawable = ContextCompat.getDrawable(context, R.drawable.ic_error)?.mutate()
drawable?.setTint(ContextCompat.getColor(context, R.color.destructive)) drawable?.setTint(ThemeUtil.getThemedColor(context, R.attr.danger))
binding.statusIndicatorImageView.setImageDrawable(drawable) binding.statusIndicatorImageView.setImageDrawable(drawable)
} }
thread.isPending -> binding.statusIndicatorImageView.setImageResource(R.drawable.ic_circle_dot_dot_dot) thread.isPending -> binding.statusIndicatorImageView.setImageResource(R.drawable.ic_circle_dot_dot_dot)

View File

@ -3,8 +3,8 @@ package org.thoughtcrime.securesms.home
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.Icon import androidx.compose.material3.Icon
import androidx.compose.material.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -14,24 +14,22 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.ui.Divider import org.thoughtcrime.securesms.ui.Divider
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
import org.thoughtcrime.securesms.ui.base import org.thoughtcrime.securesms.ui.theme.ThemeColors
import org.thoughtcrime.securesms.ui.color.Colors import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalType
import org.thoughtcrime.securesms.ui.h4
import org.thoughtcrime.securesms.ui.h8
import org.thoughtcrime.securesms.ui.small
@Composable @Composable
internal fun EmptyView(newAccount: Boolean) { internal fun EmptyView(newAccount: Boolean) {
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier modifier = Modifier
.padding(horizontal = LocalDimensions.current.homeEmptyViewMargin) .padding(horizontal = 50.dp)
) { ) {
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
Icon( Icon(
@ -42,27 +40,27 @@ internal fun EmptyView(newAccount: Boolean) {
if (newAccount) { if (newAccount) {
Text( Text(
stringResource(R.string.onboardingAccountCreated), stringResource(R.string.onboardingAccountCreated),
style = h4, style = LocalType.current.h4,
textAlign = TextAlign.Center textAlign = TextAlign.Center
) )
Text( Text(
stringResource(R.string.welcome_to_session), stringResource(R.string.welcome_to_session),
style = base, style = LocalType.current.base,
color = LocalColors.current.primary, color = LocalColors.current.primary,
textAlign = TextAlign.Center textAlign = TextAlign.Center
) )
} }
Divider(modifier = Modifier.padding(vertical = LocalDimensions.current.xsMargin)) Divider(modifier = Modifier.padding(vertical = LocalDimensions.current.smallSpacing))
Text( Text(
stringResource(R.string.conversationsNone), stringResource(R.string.conversationsNone),
style = h8, style = LocalType.current.h8,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.padding(bottom = LocalDimensions.current.xsItemSpacing)) modifier = Modifier.padding(bottom = LocalDimensions.current.xsSpacing))
Text( Text(
stringResource(R.string.onboardingHitThePlusButton), stringResource(R.string.onboardingHitThePlusButton),
style = small, style = LocalType.current.small,
textAlign = TextAlign.Center textAlign = TextAlign.Center
) )
Spacer(modifier = Modifier.weight(2f)) Spacer(modifier = Modifier.weight(2f))
@ -72,7 +70,7 @@ internal fun EmptyView(newAccount: Boolean) {
@Preview @Preview
@Composable @Composable
fun PreviewEmptyView( fun PreviewEmptyView(
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
) { ) {
PreviewTheme(colors) { PreviewTheme(colors) {
EmptyView(newAccount = false) EmptyView(newAccount = false)
@ -82,7 +80,7 @@ fun PreviewEmptyView(
@Preview @Preview
@Composable @Composable
fun PreviewEmptyViewNew( fun PreviewEmptyViewNew(
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
) { ) {
PreviewTheme(colors) { PreviewTheme(colors) {
EmptyView(newAccount = true) EmptyView(newAccount = true)

View File

@ -10,7 +10,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredWidth import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.material.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -18,16 +18,15 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
import org.thoughtcrime.securesms.ui.SessionShieldIcon import org.thoughtcrime.securesms.ui.SessionShieldIcon
import org.thoughtcrime.securesms.ui.color.Colors import org.thoughtcrime.securesms.ui.theme.ThemeColors
import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.components.SlimPrimaryOutlineButton import org.thoughtcrime.securesms.ui.components.SlimPrimaryOutlineButton
import org.thoughtcrime.securesms.ui.contentDescription import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.h8 import org.thoughtcrime.securesms.ui.theme.LocalType
import org.thoughtcrime.securesms.ui.small
@Composable @Composable
internal fun SeedReminder(startRecoveryPasswordActivity: () -> Unit) { internal fun SeedReminder(startRecoveryPasswordActivity: () -> Unit) {
@ -43,25 +42,25 @@ internal fun SeedReminder(startRecoveryPasswordActivity: () -> Unit) {
Modifier Modifier
.background(LocalColors.current.backgroundSecondary) .background(LocalColors.current.backgroundSecondary)
.padding( .padding(
horizontal = LocalDimensions.current.smallMargin, horizontal = LocalDimensions.current.spacing,
vertical = LocalDimensions.current.xsMargin vertical = LocalDimensions.current.smallSpacing
) )
) { ) {
Column(Modifier.weight(1f)) { Column(Modifier.weight(1f)) {
Row { Row {
Text( Text(
stringResource(R.string.save_your_recovery_password), stringResource(R.string.save_your_recovery_password),
style = h8 style = LocalType.current.h8
) )
Spacer(Modifier.requiredWidth(LocalDimensions.current.xxsItemSpacing)) Spacer(Modifier.requiredWidth(LocalDimensions.current.xxsSpacing))
SessionShieldIcon() SessionShieldIcon()
} }
Text( Text(
stringResource(R.string.save_your_recovery_password_to_make_sure_you_don_t_lose_access_to_your_account), stringResource(R.string.save_your_recovery_password_to_make_sure_you_don_t_lose_access_to_your_account),
style = small style = LocalType.current.small
) )
} }
Spacer(Modifier.width(LocalDimensions.current.xxsMargin)) Spacer(Modifier.width(LocalDimensions.current.xsSpacing))
SlimPrimaryOutlineButton( SlimPrimaryOutlineButton(
text = stringResource(R.string.continue_2), text = stringResource(R.string.continue_2),
modifier = Modifier modifier = Modifier
@ -76,7 +75,7 @@ internal fun SeedReminder(startRecoveryPasswordActivity: () -> Unit) {
@Preview @Preview
@Composable @Composable
private fun PreviewSeedReminder( private fun PreviewSeedReminder(
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
) { ) {
PreviewTheme(colors) { PreviewTheme(colors) {
SeedReminder {} SeedReminder {}

View File

@ -10,6 +10,7 @@ import android.view.ViewGroup
import android.widget.PopupMenu import android.widget.PopupMenu
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import network.loki.messenger.R import network.loki.messenger.R
import org.session.libsession.utilities.ThemeUtil
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.dependencies.DatabaseComponent import org.thoughtcrime.securesms.dependencies.DatabaseComponent
@ -60,8 +61,9 @@ class MessageRequestsAdapter(
for (i in 0 until popupMenu.menu.size()) { for (i in 0 until popupMenu.menu.size()) {
val item = popupMenu.menu.getItem(i) val item = popupMenu.menu.getItem(i)
val s = SpannableString(item.title) val s = SpannableString(item.title)
s.setSpan(ForegroundColorSpan(context.getColor(R.color.destructive)), 0, s.length, 0) val danger = ThemeUtil.getThemedColor(context, R.attr.danger)
item.iconTintList = ColorStateList.valueOf(context.getColor(R.color.destructive)) s.setSpan(ForegroundColorSpan(danger), 0, s.length, 0)
item.iconTintList = ColorStateList.valueOf(danger)
item.title = s item.title = s
} }
popupMenu.setForceShowIcon(true) popupMenu.setForceShowIcon(true)

View File

@ -1,209 +0,0 @@
/**
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.mms;
import static org.session.libsession.utilities.StringSubstitutionConstants.EMOJI_KEY;
import android.content.Context;
import android.content.res.Resources.Theme;
import android.net.Uri;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.squareup.phrase.Phrase;
import java.security.SecureRandom;
import network.loki.messenger.R;
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress;
import org.session.libsession.messaging.sending_receiving.attachments.UriAttachment;
import org.session.libsession.utilities.Util;
import org.session.libsignal.utilities.guava.Optional;
import org.thoughtcrime.securesms.util.MediaUtil;
public abstract class Slide {
protected final Attachment attachment;
protected final Context context;
public Slide(@NonNull Context context, @NonNull Attachment attachment) {
this.context = context;
this.attachment = attachment;
}
public String getContentType() {
return attachment.getContentType();
}
@Nullable
public Uri getUri() {
return attachment.getDataUri();
}
@Nullable
public Uri getThumbnailUri() {
return attachment.getThumbnailUri();
}
@NonNull
public Optional<String> getBody() {
String attachmentString = context.getString(R.string.attachment);
if (MediaUtil.isAudio(attachment)) {
// A missing file name is the legacy way to determine if an audio attachment is
// a voice note vs. other arbitrary audio attachments.
if (attachment.isVoiceNote() || attachment.getFileName() == null ||
attachment.getFileName().isEmpty()) {
attachmentString = context.getString(R.string.attachment_type_voice_message);
return Optional.fromNullable("🎤 " + attachmentString);
}
}
String txt = Phrase.from(context, R.string.attachmentsNotification)
.put(EMOJI_KEY, emojiForMimeType())
.format().toString();
return Optional.fromNullable(txt);
}
private String emojiForMimeType() {
if (MediaUtil.isImage(attachment)) {
return "📷";
} else if (MediaUtil.isVideo(attachment)) {
return "🎥";
} else if (MediaUtil.isAudio(attachment)) {
return "🎧";
} else if (MediaUtil.isFile(attachment)) {
return "📎";
} else {
return "🎡"; // `isGif`
}
}
@NonNull
public Optional<String> getCaption() {
return Optional.fromNullable(attachment.getCaption());
}
@NonNull
public Optional<String> getFileName() {
return Optional.fromNullable(attachment.getFileName());
}
@Nullable
public String getFastPreflightId() {
return attachment.getFastPreflightId();
}
public long getFileSize() {
return attachment.getSize();
}
public boolean hasImage() {
return false;
}
public boolean hasVideo() {
return false;
}
public boolean hasAudio() {
return false;
}
public boolean hasDocument() {
return false;
}
public @NonNull String getContentDescription() { return ""; }
public @NonNull Attachment asAttachment() {
return attachment;
}
public boolean isInProgress() {
return attachment.isInProgress();
}
public boolean isPendingDownload() {
return getTransferState() == AttachmentTransferProgress.TRANSFER_PROGRESS_FAILED ||
getTransferState() == AttachmentTransferProgress.TRANSFER_PROGRESS_PENDING;
}
public int getTransferState() {
return attachment.getTransferState();
}
public @DrawableRes int getPlaceholderRes(Theme theme) {
throw new AssertionError("getPlaceholderRes() called for non-drawable slide");
}
public boolean hasPlaceholder() {
return false;
}
public boolean hasPlayOverlay() {
return false;
}
protected static Attachment constructAttachmentFromUri(@NonNull Context context,
@NonNull Uri uri,
@NonNull String defaultMime,
long size,
int width,
int height,
boolean hasThumbnail,
@Nullable String fileName,
@Nullable String caption,
boolean voiceNote,
boolean quote)
{
String resolvedType = Optional.fromNullable(MediaUtil.getMimeType(context, uri)).or(defaultMime);
String fastPreflightId = String.valueOf(new SecureRandom().nextLong());
return new UriAttachment(uri,
hasThumbnail ? uri : null,
resolvedType,
AttachmentTransferProgress.TRANSFER_PROGRESS_STARTED,
size,
width,
height,
fileName,
fastPreflightId,
voiceNote,
quote,
caption);
}
@Override
public boolean equals(Object other) {
if (other == null) return false;
if (!(other instanceof Slide)) return false;
Slide that = (Slide)other;
return Util.equals(this.getContentType(), that.getContentType()) &&
this.hasAudio() == that.hasAudio() &&
this.hasImage() == that.hasImage() &&
this.hasVideo() == that.hasVideo() &&
this.getTransferState() == that.getTransferState() &&
Util.equals(this.getUri(), that.getUri()) &&
Util.equals(this.getThumbnailUri(), that.getThumbnailUri());
}
@Override
public int hashCode() {
return Util.hashCode(getContentType(), hasAudio(), hasImage(),
hasVideo(), getUri(), getThumbnailUri(), getTransferState());
}
}

View File

@ -0,0 +1,180 @@
/**
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http:></http:>//www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.mms
import android.content.Context
import android.content.res.Resources
import android.net.Uri
import androidx.annotation.DrawableRes
import com.squareup.phrase.Phrase
import java.security.SecureRandom
import network.loki.messenger.R
import org.session.libsession.messaging.sending_receiving.attachments.Attachment
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress
import org.session.libsession.messaging.sending_receiving.attachments.UriAttachment
import org.session.libsession.utilities.StringSubstitutionConstants.EMOJI_KEY
import org.session.libsession.utilities.Util.equals
import org.session.libsession.utilities.Util.hashCode
import org.session.libsignal.utilities.guava.Optional
import org.thoughtcrime.securesms.conversation.v2.Util
import org.thoughtcrime.securesms.util.MediaUtil
abstract class Slide(@JvmField protected val context: Context, protected val attachment: Attachment) {
val contentType: String
get() = attachment.contentType
val uri: Uri?
get() = attachment.dataUri
open val thumbnailUri: Uri?
get() = attachment.thumbnailUri
val body: Optional<String>
get() {
if (MediaUtil.isAudio(attachment)) {
// A missing file name is the legacy way to determine if an audio attachment is
// a voice note vs. other arbitrary audio attachments.
if (attachment.isVoiceNote || attachment.fileName.isNullOrEmpty()) {
val baseString = context.getString(R.string.attachment_type_voice_message)
val languageIsLTR = Util.usingLeftToRightLanguage(context)
val attachmentString = if (languageIsLTR) {
"🎙 $baseString"
} else {
"$baseString 🎙"
}
return Optional.fromNullable(attachmentString)
}
}
val txt = Phrase.from(context, R.string.attachmentsNotification)
.put(EMOJI_KEY, emojiForMimeType())
.format().toString()
return Optional.fromNullable(txt)
}
private fun emojiForMimeType(): String {
return if (MediaUtil.isGif(attachment)) {
"🎡"
} else if (MediaUtil.isImage(attachment)) {
"📷"
} else if (MediaUtil.isVideo(attachment)) {
"🎥"
} else if (MediaUtil.isAudio(attachment)) {
"🎧"
} else if (MediaUtil.isFile(attachment)) {
"📎"
} else {
// We don't provide emojis for other mime-types such as VCARD
""
}
}
val caption: Optional<String?>
get() = Optional.fromNullable(attachment.caption)
val fileName: Optional<String?>
get() = Optional.fromNullable(attachment.fileName)
val fastPreflightId: String?
get() = attachment.fastPreflightId
val fileSize: Long
get() = attachment.size
open fun hasImage(): Boolean { return false }
open fun hasVideo(): Boolean { return false }
open fun hasAudio(): Boolean { return false }
open fun hasDocument(): Boolean { return false }
open val contentDescription: String
get() = ""
fun asAttachment(): Attachment { return attachment }
val isInProgress: Boolean
get() = attachment.isInProgress
val isPendingDownload: Boolean
get() = transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_FAILED ||
transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_PENDING
val transferState: Int
get() = attachment.transferState
@DrawableRes
open fun getPlaceholderRes(theme: Resources.Theme?): Int {
throw AssertionError("getPlaceholderRes() called for non-drawable slide")
}
open fun hasPlaceholder(): Boolean { return false }
open fun hasPlayOverlay(): Boolean { return false }
override fun equals(other: Any?): Boolean {
if (other == null) return false
if (other !is Slide) return false
return (equals(this.contentType, other.contentType) &&
hasAudio() == other.hasAudio() &&
hasImage() == other.hasImage() &&
hasVideo() == other.hasVideo()) &&
this.transferState == other.transferState &&
equals(this.uri, other.uri) &&
equals(this.thumbnailUri, other.thumbnailUri)
}
override fun hashCode(): Int {
return hashCode(contentType, hasAudio(), hasImage(), hasVideo(), uri, thumbnailUri, transferState)
}
companion object {
@JvmStatic
protected fun constructAttachmentFromUri(
context: Context,
uri: Uri,
defaultMime: String,
size: Long,
width: Int,
height: Int,
hasThumbnail: Boolean,
fileName: String?,
caption: String?,
voiceNote: Boolean,
quote: Boolean
): Attachment {
val resolvedType =
Optional.fromNullable(MediaUtil.getMimeType(context, uri)).or(defaultMime)
val fastPreflightId = SecureRandom().nextLong().toString()
return UriAttachment(
uri,
if (hasThumbnail) uri else null,
resolvedType!!,
AttachmentTransferProgress.TRANSFER_PROGRESS_STARTED,
size,
width,
height,
fileName,
fastPreflightId,
voiceNote,
quote,
caption
)
}
}
}

View File

@ -7,7 +7,7 @@ import network.loki.messenger.R
import org.thoughtcrime.securesms.ui.AlertDialog import org.thoughtcrime.securesms.ui.AlertDialog
import org.thoughtcrime.securesms.ui.DialogButtonModel import org.thoughtcrime.securesms.ui.DialogButtonModel
import org.thoughtcrime.securesms.ui.GetString import org.thoughtcrime.securesms.ui.GetString
import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalColors
@Composable @Composable
fun OnboardingBackPressAlertDialog( fun OnboardingBackPressAlertDialog(

View File

@ -5,6 +5,7 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideInVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -16,10 +17,8 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme
import androidx.compose.material.Card import androidx.compose.material3.Text
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -40,23 +39,22 @@ import network.loki.messenger.R
import org.thoughtcrime.securesms.ui.AlertDialog import org.thoughtcrime.securesms.ui.AlertDialog
import org.thoughtcrime.securesms.ui.DialogButtonModel import org.thoughtcrime.securesms.ui.DialogButtonModel
import org.thoughtcrime.securesms.ui.GetString import org.thoughtcrime.securesms.ui.GetString
import org.thoughtcrime.securesms.ui.LocalDimensions
import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider
import org.thoughtcrime.securesms.ui.color.Colors
import org.thoughtcrime.securesms.ui.color.LocalColors
import org.thoughtcrime.securesms.ui.components.BorderlessHtmlButton import org.thoughtcrime.securesms.ui.components.BorderlessHtmlButton
import org.thoughtcrime.securesms.ui.components.PrimaryFillButton import org.thoughtcrime.securesms.ui.components.PrimaryFillButton
import org.thoughtcrime.securesms.ui.components.PrimaryOutlineButton import org.thoughtcrime.securesms.ui.components.PrimaryOutlineButton
import org.thoughtcrime.securesms.ui.contentDescription import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.h4 import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.large import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.theme.LocalType
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
import org.thoughtcrime.securesms.ui.theme.ThemeColors
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
@Preview @Preview
@Composable @Composable
private fun PreviewLandingScreen( private fun PreviewLandingScreen(
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
) { ) {
PreviewTheme(colors) { PreviewTheme(colors) {
LandingScreen({}, {}, {}, {}) LandingScreen({}, {}, {}, {})
@ -80,6 +78,7 @@ internal fun LandingScreen(
onDismissRequest = { isUrlDialogVisible = false }, onDismissRequest = { isUrlDialogVisible = false },
title = stringResource(R.string.urlOpen), title = stringResource(R.string.urlOpen),
text = stringResource(R.string.urlOpenBrowser), text = stringResource(R.string.urlOpenBrowser),
showCloseButton = true, // display the 'x' button
buttons = listOf( buttons = listOf(
DialogButtonModel( DialogButtonModel(
text = GetString(R.string.activity_landing_terms_of_service), text = GetString(R.string.activity_landing_terms_of_service),
@ -107,24 +106,24 @@ internal fun LandingScreen(
Column { Column {
Column(modifier = Modifier Column(modifier = Modifier
.weight(1f) .weight(1f)
.padding(horizontal = LocalDimensions.current.onboardingMargin) .padding(horizontal = LocalDimensions.current.mediumSpacing)
) { ) {
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
Text( Text(
stringResource(R.string.onboardingBubblePrivacyInYourPocket), stringResource(R.string.onboardingBubblePrivacyInYourPocket),
modifier = Modifier.align(Alignment.CenterHorizontally), modifier = Modifier.align(Alignment.CenterHorizontally),
style = h4, style = LocalType.current.h4,
textAlign = TextAlign.Center textAlign = TextAlign.Center
) )
Spacer(modifier = Modifier.height(LocalDimensions.current.itemSpacing)) Spacer(modifier = Modifier.height(LocalDimensions.current.spacing))
LazyColumn( LazyColumn(
state = listState, state = listState,
modifier = Modifier modifier = Modifier
.heightIn(min = LocalDimensions.current.minScrollableViewHeight) .heightIn(min = 200.dp)
.fillMaxWidth() .fillMaxWidth()
.weight(3f), .weight(3f),
verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing) verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing)
) { ) {
items( items(
MESSAGES.take(count), MESSAGES.take(count),
@ -140,7 +139,7 @@ internal fun LandingScreen(
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
} }
Column(modifier = Modifier.padding(horizontal = LocalDimensions.current.largeMargin)) { Column(modifier = Modifier.padding(horizontal = LocalDimensions.current.xlargeSpacing)) {
PrimaryFillButton( PrimaryFillButton(
text = stringResource(R.string.onboardingAccountCreate), text = stringResource(R.string.onboardingAccountCreate),
modifier = Modifier modifier = Modifier
@ -149,7 +148,7 @@ internal fun LandingScreen(
.contentDescription(R.string.AccessibilityId_create_account_button), .contentDescription(R.string.AccessibilityId_create_account_button),
onClick = createAccount onClick = createAccount
) )
Spacer(modifier = Modifier.height(LocalDimensions.current.smallItemSpacing)) Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing))
PrimaryOutlineButton( PrimaryOutlineButton(
stringResource(R.string.onboardingAccountExists), stringResource(R.string.onboardingAccountExists),
modifier = Modifier modifier = Modifier
@ -166,7 +165,7 @@ internal fun LandingScreen(
.contentDescription(R.string.AccessibilityId_open_url), .contentDescription(R.string.AccessibilityId_open_url),
onClick = { isUrlDialogVisible = true } onClick = { isUrlDialogVisible = true }
) )
Spacer(modifier = Modifier.height(LocalDimensions.current.xxsItemSpacing)) Spacer(modifier = Modifier.height(LocalDimensions.current.xxsSpacing))
} }
} }
} }
@ -195,7 +194,7 @@ private fun MessageText(text: String, isOutgoing: Boolean, modifier: Modifier) {
Box(modifier = modifier then Modifier.fillMaxWidth()) { Box(modifier = modifier then Modifier.fillMaxWidth()) {
MessageText( MessageText(
text, text,
color = if (isOutgoing) LocalColors.current.backgroundBubbleSent else LocalColors.current.backgroundBubbleReceived, color = if (isOutgoing) LocalColors.current.primary else LocalColors.current.backgroundBubbleReceived,
textColor = if (isOutgoing) LocalColors.current.textBubbleSent else LocalColors.current.textBubbleReceived, textColor = if (isOutgoing) LocalColors.current.textBubbleSent else LocalColors.current.textBubbleReceived,
modifier = Modifier.align(if (isOutgoing) Alignment.TopEnd else Alignment.TopStart) modifier = Modifier.align(if (isOutgoing) Alignment.TopEnd else Alignment.TopStart)
) )
@ -209,19 +208,17 @@ private fun MessageText(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
textColor: Color = Color.Unspecified textColor: Color = Color.Unspecified
) { ) {
Card( Box(
modifier = modifier.fillMaxWidth(0.666f), modifier = modifier.fillMaxWidth(0.666f)
shape = MaterialTheme.shapes.small, .background(color = color, shape = MaterialTheme.shapes.small)
backgroundColor = color,
elevation = 0.dp
) { ) {
Text( Text(
text, text,
style = large, style = LocalType.current.large,
color = textColor, color = textColor,
modifier = Modifier.padding( modifier = Modifier.padding(
horizontal = LocalDimensions.current.smallItemSpacing, horizontal = LocalDimensions.current.smallSpacing,
vertical = LocalDimensions.current.xsItemSpacing vertical = LocalDimensions.current.xsSpacing
) )
) )
} }

View File

@ -13,8 +13,8 @@ import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Icon import androidx.compose.material3.Icon
import androidx.compose.material.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -24,13 +24,12 @@ import androidx.compose.ui.tooling.preview.Preview
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.onboarding.ui.ContinuePrimaryOutlineButton import org.thoughtcrime.securesms.onboarding.ui.ContinuePrimaryOutlineButton
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.base
import org.thoughtcrime.securesms.ui.components.MaybeScanQrCode import org.thoughtcrime.securesms.ui.components.MaybeScanQrCode
import org.thoughtcrime.securesms.ui.components.SessionOutlinedTextField import org.thoughtcrime.securesms.ui.components.SessionOutlinedTextField
import org.thoughtcrime.securesms.ui.components.SessionTabRow import org.thoughtcrime.securesms.ui.components.SessionTabRow
import org.thoughtcrime.securesms.ui.h4 import org.thoughtcrime.securesms.ui.theme.LocalType
private val TITLES = listOf(R.string.sessionRecoveryPassword, R.string.qrScan) private val TITLES = listOf(R.string.sessionRecoveryPassword, R.string.qrScan)
@ -75,29 +74,29 @@ private fun RecoveryPassword(state: State, onChange: (String) -> Unit = {}, onCo
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
) { ) {
Spacer(Modifier.weight(1f)) Spacer(Modifier.weight(1f))
Spacer(modifier = Modifier.height(LocalDimensions.current.smallItemSpacing)) Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing))
Column( Column(
modifier = Modifier.padding(horizontal = LocalDimensions.current.largeMargin) modifier = Modifier.padding(horizontal = LocalDimensions.current.mediumSpacing)
) { ) {
Row { Row {
Text( Text(
text = stringResource(R.string.sessionRecoveryPassword), text = stringResource(R.string.sessionRecoveryPassword),
style = h4 style = LocalType.current.h4
) )
Spacer(Modifier.width(LocalDimensions.current.xxsItemSpacing)) Spacer(Modifier.width(LocalDimensions.current.xxsSpacing))
Icon( Icon(
modifier = Modifier.align(Alignment.CenterVertically), modifier = Modifier.align(Alignment.CenterVertically),
painter = painterResource(id = R.drawable.ic_shield_outline), painter = painterResource(id = R.drawable.ic_shield_outline),
contentDescription = null, contentDescription = null,
) )
} }
Spacer(Modifier.height(LocalDimensions.current.smallItemSpacing)) Spacer(Modifier.height(LocalDimensions.current.smallSpacing))
Text( Text(
stringResource(R.string.activity_link_enter_your_recovery_password_to_load_your_account_if_you_haven_t_saved_it_you_can_find_it_in_your_app_settings), stringResource(R.string.activity_link_enter_your_recovery_password_to_load_your_account_if_you_haven_t_saved_it_you_can_find_it_in_your_app_settings),
style = base style = LocalType.current.base
) )
Spacer(Modifier.height(LocalDimensions.current.itemSpacing)) Spacer(Modifier.height(LocalDimensions.current.spacing))
SessionOutlinedTextField( SessionOutlinedTextField(
text = state.recoveryPhrase, text = state.recoveryPhrase,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@ -110,7 +109,7 @@ private fun RecoveryPassword(state: State, onChange: (String) -> Unit = {}, onCo
) )
} }
Spacer(modifier = Modifier.height(LocalDimensions.current.smallItemSpacing)) Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing))
Spacer(Modifier.weight(2f)) Spacer(Modifier.weight(2f))
ContinuePrimaryOutlineButton(modifier = Modifier.align(Alignment.CenterHorizontally), onContinue) ContinuePrimaryOutlineButton(modifier = Modifier.align(Alignment.CenterHorizontally), onContinue)

View File

@ -3,17 +3,16 @@ package org.thoughtcrime.securesms.onboarding.loading
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.material.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.ProgressArc import org.thoughtcrime.securesms.ui.ProgressArc
import org.thoughtcrime.securesms.ui.base
import org.thoughtcrime.securesms.ui.contentDescription import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.h7 import org.thoughtcrime.securesms.ui.theme.LocalType
@Composable @Composable
internal fun LoadingScreen(progress: Float) { internal fun LoadingScreen(progress: Float) {
@ -25,12 +24,12 @@ internal fun LoadingScreen(progress: Float) {
) )
Text( Text(
stringResource(R.string.waitOneMoment), stringResource(R.string.waitOneMoment),
style = h7 style = LocalType.current.h7
) )
Spacer(modifier = Modifier.height(LocalDimensions.current.xxxsItemSpacing)) Spacer(modifier = Modifier.height(LocalDimensions.current.xxxsSpacing))
Text( Text(
stringResource(R.string.loadAccountProgressMessage), stringResource(R.string.loadAccountProgressMessage),
style = base style = LocalType.current.base
) )
Spacer(modifier = Modifier.weight(2f)) Spacer(modifier = Modifier.weight(2f))
} }

View File

@ -10,7 +10,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -22,22 +22,15 @@ import network.loki.messenger.R
import org.thoughtcrime.securesms.onboarding.OnboardingBackPressAlertDialog import org.thoughtcrime.securesms.onboarding.OnboardingBackPressAlertDialog
import org.thoughtcrime.securesms.onboarding.messagenotifications.MessageNotificationsViewModel.UiState import org.thoughtcrime.securesms.onboarding.messagenotifications.MessageNotificationsViewModel.UiState
import org.thoughtcrime.securesms.onboarding.ui.ContinuePrimaryOutlineButton import org.thoughtcrime.securesms.onboarding.ui.ContinuePrimaryOutlineButton
import org.thoughtcrime.securesms.ui.AlertDialog import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.DialogButtonModel import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.GetString import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.ThemeColors
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider
import org.thoughtcrime.securesms.ui.base
import org.thoughtcrime.securesms.ui.color.Colors
import org.thoughtcrime.securesms.ui.color.LocalColors
import org.thoughtcrime.securesms.ui.components.CircularProgressIndicator import org.thoughtcrime.securesms.ui.components.CircularProgressIndicator
import org.thoughtcrime.securesms.ui.components.RadioButton import org.thoughtcrime.securesms.ui.components.RadioButton
import org.thoughtcrime.securesms.ui.contentDescription import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.h4 import org.thoughtcrime.securesms.ui.theme.LocalType
import org.thoughtcrime.securesms.ui.h8
import org.thoughtcrime.securesms.ui.h9
import org.thoughtcrime.securesms.ui.small
@Composable @Composable
internal fun MessageNotificationsScreen( internal fun MessageNotificationsScreen(
@ -63,11 +56,11 @@ internal fun MessageNotificationsScreen(
Column { Column {
Spacer(Modifier.weight(1f)) Spacer(Modifier.weight(1f))
Column(modifier = Modifier.padding(horizontal = LocalDimensions.current.onboardingMargin)) { Column(modifier = Modifier.padding(horizontal = LocalDimensions.current.mediumSpacing)) {
Text(stringResource(R.string.notificationsMessage), style = h4) Text(stringResource(R.string.notificationsMessage), style = LocalType.current.h4)
Spacer(Modifier.height(LocalDimensions.current.xsMargin)) Spacer(Modifier.height(LocalDimensions.current.smallSpacing))
Text(stringResource(R.string.onboardingMessageNotificationExplaination), style = base) Text(stringResource(R.string.onboardingMessageNotificationExplaination), style = LocalType.current.base)
Spacer(Modifier.height(LocalDimensions.current.itemSpacing)) Spacer(Modifier.height(LocalDimensions.current.spacing))
} }
NotificationRadioButton( NotificationRadioButton(
@ -107,8 +100,8 @@ private fun NotificationRadioButton(
RadioButton( RadioButton(
onClick = onClick, onClick = onClick,
modifier = modifier, modifier = modifier,
checked = checked, selected = checked,
contentPadding = PaddingValues(horizontal = LocalDimensions.current.margin, vertical = 7.dp) contentPadding = PaddingValues(horizontal = LocalDimensions.current.mediumSpacing, vertical = 7.dp)
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
@ -120,17 +113,18 @@ private fun NotificationRadioButton(
), ),
) { ) {
Column(modifier = Modifier Column(modifier = Modifier
.padding(horizontal = 15.dp) .padding(horizontal = LocalDimensions.current.smallSpacing,
.padding(top = 10.dp, bottom = 11.dp)) { vertical = LocalDimensions.current.xsSpacing)
Text(stringResource(title), style = h8) ) {
Text(stringResource(title), style = LocalType.current.h8)
Text(stringResource(explanation), style = small, modifier = Modifier.padding(top = 7.dp)) Text(stringResource(explanation), style = LocalType.current.small, modifier = Modifier.padding(top = LocalDimensions.current.xxsSpacing))
tag?.let { tag?.let {
Text( Text(
stringResource(it), stringResource(it),
modifier = Modifier.padding(top = 6.dp), modifier = Modifier.padding(top = LocalDimensions.current.xxsSpacing),
color = LocalColors.current.primary, color = LocalColors.current.primary,
style = h9 style = LocalType.current.h9
) )
} }
} }
@ -141,7 +135,7 @@ private fun NotificationRadioButton(
@Preview @Preview
@Composable @Composable
private fun MessageNotificationsScreenPreview( private fun MessageNotificationsScreenPreview(
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
) { ) {
PreviewTheme(colors) { PreviewTheme(colors) {
MessageNotificationsScreen() MessageNotificationsScreen()

View File

@ -8,7 +8,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -17,11 +17,10 @@ import androidx.compose.ui.tooling.preview.Preview
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.onboarding.OnboardingBackPressAlertDialog import org.thoughtcrime.securesms.onboarding.OnboardingBackPressAlertDialog
import org.thoughtcrime.securesms.onboarding.ui.ContinuePrimaryOutlineButton import org.thoughtcrime.securesms.onboarding.ui.ContinuePrimaryOutlineButton
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.base
import org.thoughtcrime.securesms.ui.components.SessionOutlinedTextField import org.thoughtcrime.securesms.ui.components.SessionOutlinedTextField
import org.thoughtcrime.securesms.ui.h4 import org.thoughtcrime.securesms.ui.theme.LocalType
@Preview @Preview
@Composable @Composable
@ -52,18 +51,18 @@ internal fun PickDisplayName(
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
) { ) {
Spacer(Modifier.weight(1f)) Spacer(Modifier.weight(1f))
Spacer(modifier = Modifier.height(LocalDimensions.current.smallItemSpacing)) Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing))
Column( Column(
modifier = Modifier.padding(horizontal = LocalDimensions.current.largeMargin) modifier = Modifier.padding(horizontal = LocalDimensions.current.mediumSpacing)
) { ) {
Text(stringResource(state.title), style = h4) Text(stringResource(state.title), style = LocalType.current.h4)
Spacer(Modifier.height(LocalDimensions.current.smallItemSpacing)) Spacer(Modifier.height(LocalDimensions.current.smallSpacing))
Text( Text(
stringResource(state.description), stringResource(state.description),
style = base, style = LocalType.current.base,
modifier = Modifier.padding(bottom = LocalDimensions.current.xsItemSpacing)) modifier = Modifier.padding(bottom = LocalDimensions.current.xsSpacing))
Spacer(Modifier.height(LocalDimensions.current.itemSpacing)) Spacer(Modifier.height(LocalDimensions.current.spacing))
SessionOutlinedTextField( SessionOutlinedTextField(
text = state.displayName, text = state.displayName,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@ -76,7 +75,7 @@ internal fun PickDisplayName(
) )
} }
Spacer(modifier = Modifier.height(LocalDimensions.current.smallItemSpacing)) Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing))
Spacer(Modifier.weight(2f)) Spacer(Modifier.weight(2f))
ContinuePrimaryOutlineButton(modifier = Modifier.align(Alignment.CenterHorizontally), onContinue) ContinuePrimaryOutlineButton(modifier = Modifier.align(Alignment.CenterHorizontally), onContinue)

View File

@ -6,7 +6,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.components.PrimaryOutlineButton import org.thoughtcrime.securesms.ui.components.PrimaryOutlineButton
import org.thoughtcrime.securesms.ui.contentDescription import org.thoughtcrime.securesms.ui.contentDescription
@ -17,8 +17,8 @@ fun ContinuePrimaryOutlineButton(modifier: Modifier, onContinue: () -> Unit) {
modifier = modifier modifier = modifier
.contentDescription(R.string.AccessibilityId_continue) .contentDescription(R.string.AccessibilityId_continue)
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = LocalDimensions.current.largeMargin) .padding(horizontal = LocalDimensions.current.xlargeSpacing)
.padding(bottom = LocalDimensions.current.xxsMargin), .padding(bottom = LocalDimensions.current.smallSpacing),
onClick = onContinue, onClick = onContinue,
) )
} }

View File

@ -8,7 +8,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@ -27,14 +27,14 @@ import org.session.libsignal.utilities.PublicKeyValidation
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
import org.thoughtcrime.securesms.database.threadDatabase import org.thoughtcrime.securesms.database.threadDatabase
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.components.MaybeScanQrCode import org.thoughtcrime.securesms.ui.components.MaybeScanQrCode
import org.thoughtcrime.securesms.ui.components.QrImage import org.thoughtcrime.securesms.ui.components.QrImage
import org.thoughtcrime.securesms.ui.components.SessionTabRow import org.thoughtcrime.securesms.ui.components.SessionTabRow
import org.thoughtcrime.securesms.ui.contentDescription import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.setComposeContent import org.thoughtcrime.securesms.ui.setComposeContent
import org.thoughtcrime.securesms.ui.small import org.thoughtcrime.securesms.ui.theme.LocalType
import org.thoughtcrime.securesms.util.start import org.thoughtcrime.securesms.util.start
private val TITLES = listOf(R.string.view, R.string.scan) private val TITLES = listOf(R.string.view, R.string.scan)
@ -95,14 +95,14 @@ private fun Tabs(accountId: String, errors: Flow<String>, onScan: (String) -> Un
fun QrPage(string: String) { fun QrPage(string: String) {
Column( Column(
modifier = Modifier modifier = Modifier
.background(LocalColors.current.backgroundSecondary) .background(LocalColors.current.background)
.padding(horizontal = LocalDimensions.current.margin) .padding(horizontal = LocalDimensions.current.mediumSpacing)
.fillMaxSize() .fillMaxSize()
) { ) {
QrImage( QrImage(
string = string, string = string,
modifier = Modifier modifier = Modifier
.padding(top = LocalDimensions.current.margin, bottom = LocalDimensions.current.xxsMargin) .padding(top = LocalDimensions.current.mediumSpacing, bottom = LocalDimensions.current.xsSpacing)
.contentDescription(R.string.AccessibilityId_qr_code), .contentDescription(R.string.AccessibilityId_qr_code),
icon = R.drawable.session icon = R.drawable.session
) )
@ -111,7 +111,7 @@ fun QrPage(string: String) {
text = stringResource(R.string.this_is_your_account_id_other_users_can_scan_it_to_start_a_conversation_with_you), text = stringResource(R.string.this_is_your_account_id_other_users_can_scan_it_to_start_a_conversation_with_you),
color = LocalColors.current.textSecondary, color = LocalColors.current.textSecondary,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
style = small style = LocalType.current.small
) )
} }
} }

View File

@ -77,8 +77,8 @@ import org.thoughtcrime.securesms.ui.Cell
import org.thoughtcrime.securesms.ui.Divider import org.thoughtcrime.securesms.ui.Divider
import org.thoughtcrime.securesms.ui.LargeItemButton import org.thoughtcrime.securesms.ui.LargeItemButton
import org.thoughtcrime.securesms.ui.LargeItemButtonWithDrawable import org.thoughtcrime.securesms.ui.LargeItemButtonWithDrawable
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.color.destructiveButtonColors import org.thoughtcrime.securesms.ui.theme.dangerButtonColors
import org.thoughtcrime.securesms.ui.components.PrimaryOutlineButton import org.thoughtcrime.securesms.ui.components.PrimaryOutlineButton
import org.thoughtcrime.securesms.ui.components.PrimaryOutlineCopyButton import org.thoughtcrime.securesms.ui.components.PrimaryOutlineCopyButton
import org.thoughtcrime.securesms.ui.contentDescription import org.thoughtcrime.securesms.ui.contentDescription
@ -448,9 +448,9 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
Column { Column {
Row( Row(
modifier = Modifier modifier = Modifier
.padding(horizontal = LocalDimensions.current.smallMargin) .padding(horizontal = LocalDimensions.current.spacing)
.padding(top = LocalDimensions.current.xxxsMargin), .padding(top = LocalDimensions.current.xxsSpacing),
horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing), horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing),
) { ) {
PrimaryOutlineButton( PrimaryOutlineButton(
stringResource(R.string.share), stringResource(R.string.share),
@ -464,7 +464,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
) )
} }
Spacer(modifier = Modifier.height(LocalDimensions.current.itemSpacing)) Spacer(modifier = Modifier.height(LocalDimensions.current.spacing))
val hasPaths by hasPaths().collectAsState(initial = false) val hasPaths by hasPaths().collectAsState(initial = false)
@ -492,7 +492,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
} }
LargeItemButton(R.string.activity_settings_help_button, R.drawable.ic_help, Modifier.contentDescription(R.string.AccessibilityId_help)) { show<HelpSettingsActivity>() } LargeItemButton(R.string.activity_settings_help_button, R.drawable.ic_help, Modifier.contentDescription(R.string.AccessibilityId_help)) { show<HelpSettingsActivity>() }
Divider() Divider()
LargeItemButton(R.string.activity_settings_clear_all_data_button_title, R.drawable.ic_clear_data, Modifier.contentDescription(R.string.AccessibilityId_clear_data), destructiveButtonColors()) { ClearAllDataDialog().show(supportFragmentManager, "Clear All Data Dialog") } LargeItemButton(R.string.activity_settings_clear_all_data_button_title, R.drawable.ic_message_details__trash, Modifier.contentDescription(R.string.AccessibilityId_clear_data), dangerButtonColors()) { ClearAllDataDialog().show(supportFragmentManager, "Clear All Data Dialog") }
} }
} }
} }

View File

@ -6,6 +6,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.thoughtcrime.securesms.ui.theme.selectedTheme
import org.thoughtcrime.securesms.util.ThemeState import org.thoughtcrime.securesms.util.ThemeState
import org.thoughtcrime.securesms.util.themeState import org.thoughtcrime.securesms.util.themeState
import javax.inject.Inject import javax.inject.Inject
@ -26,11 +27,17 @@ class AppearanceSettingsViewModel @Inject constructor(private val prefs: TextSec
prefs.setThemeStyle(newThemeStyle) prefs.setThemeStyle(newThemeStyle)
// update UI state // update UI state
_uiState.value = prefs.themeState() _uiState.value = prefs.themeState()
// force compose to refresh its style reference
selectedTheme = null
} }
fun setNewFollowSystemSettings(followSystemSettings: Boolean) { fun setNewFollowSystemSettings(followSystemSettings: Boolean) {
prefs.setFollowSystemSettings(followSystemSettings) prefs.setFollowSystemSettings(followSystemSettings)
_uiState.value = prefs.themeState() _uiState.value = prefs.themeState()
// force compose to refresh its style reference
selectedTheme = null
} }
} }

View File

@ -11,7 +11,7 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -26,20 +26,19 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
import org.thoughtcrime.securesms.ui.SessionShieldIcon import org.thoughtcrime.securesms.ui.SessionShieldIcon
import org.thoughtcrime.securesms.ui.base import org.thoughtcrime.securesms.ui.theme.ThemeColors
import org.thoughtcrime.securesms.ui.color.Colors import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.color.LocalColors
import org.thoughtcrime.securesms.ui.components.QrImage import org.thoughtcrime.securesms.ui.components.QrImage
import org.thoughtcrime.securesms.ui.components.SlimOutlineButton import org.thoughtcrime.securesms.ui.components.SlimOutlineButton
import org.thoughtcrime.securesms.ui.components.SlimOutlineCopyButton import org.thoughtcrime.securesms.ui.components.SlimOutlineCopyButton
import org.thoughtcrime.securesms.ui.components.border import org.thoughtcrime.securesms.ui.components.border
import org.thoughtcrime.securesms.ui.contentDescription import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.extraSmallMonospace import org.thoughtcrime.securesms.ui.theme.LocalType
import org.thoughtcrime.securesms.ui.h8 import org.thoughtcrime.securesms.ui.theme.monospace
@Composable @Composable
internal fun RecoveryPasswordScreen( internal fun RecoveryPasswordScreen(
@ -49,11 +48,11 @@ internal fun RecoveryPasswordScreen(
onHide:() -> Unit = {} onHide:() -> Unit = {}
) { ) {
Column( Column(
verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.xsMargin), verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing),
modifier = Modifier modifier = Modifier
.contentDescription(R.string.AccessibilityId_recovery_password) .contentDescription(R.string.AccessibilityId_recovery_password)
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(bottom = LocalDimensions.current.xsMargin) .padding(bottom = LocalDimensions.current.smallSpacing)
) { ) {
RecoveryPasswordCell(mnemonic, seed, copyMnemonic) RecoveryPasswordCell(mnemonic, seed, copyMnemonic)
HideRecoveryPasswordCell(onHide) HideRecoveryPasswordCell(onHide)
@ -75,17 +74,17 @@ private fun RecoveryPasswordCell(
Row { Row {
Text( Text(
stringResource(R.string.sessionRecoveryPassword), stringResource(R.string.sessionRecoveryPassword),
style = h8 style = LocalType.current.h8
) )
Spacer(Modifier.width(LocalDimensions.current.xxsItemSpacing)) Spacer(Modifier.width(LocalDimensions.current.xxsSpacing))
SessionShieldIcon() SessionShieldIcon()
} }
Spacer(modifier = Modifier.height(LocalDimensions.current.xxxsMargin)) Spacer(modifier = Modifier.height(LocalDimensions.current.xxsSpacing))
Text( Text(
stringResource(R.string.recoveryPasswordDescription), stringResource(R.string.recoveryPasswordDescription),
style = base style = LocalType.current.base
) )
AnimatedVisibility(!showQr) { AnimatedVisibility(!showQr) {
@ -99,7 +98,7 @@ private fun RecoveryPasswordCell(
QrImage( QrImage(
seed, seed,
modifier = Modifier modifier = Modifier
.padding(vertical = LocalDimensions.current.smallMargin) .padding(vertical = LocalDimensions.current.spacing)
.contentDescription(R.string.AccessibilityId_qr_code), .contentDescription(R.string.AccessibilityId_qr_code),
contentPadding = 10.dp, contentPadding = 10.dp,
icon = R.drawable.session_shield icon = R.drawable.session_shield
@ -108,7 +107,7 @@ private fun RecoveryPasswordCell(
AnimatedVisibility(!showQr) { AnimatedVisibility(!showQr) {
Row( Row(
horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing), horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
SlimOutlineCopyButton( SlimOutlineCopyButton(
@ -138,11 +137,11 @@ private fun RecoveryPassword(mnemonic: String) {
mnemonic, mnemonic,
modifier = Modifier modifier = Modifier
.contentDescription(R.string.AccessibilityId_recovery_password_container) .contentDescription(R.string.AccessibilityId_recovery_password_container)
.padding(vertical = LocalDimensions.current.smallMargin) .padding(vertical = LocalDimensions.current.spacing)
.border() .border()
.padding(LocalDimensions.current.smallMargin), .padding(LocalDimensions.current.spacing),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
style = extraSmallMonospace, style = LocalType.current.extraSmall.monospace(),
color = LocalColors.current.run { if (isLight) text else primary }, color = LocalColors.current.run { if (isLight) text else primary },
) )
} }
@ -156,14 +155,14 @@ private fun HideRecoveryPasswordCell(onHide: () -> Unit = {}) {
) { ) {
Text( Text(
stringResource(R.string.recoveryPasswordHideRecoveryPassword), stringResource(R.string.recoveryPasswordHideRecoveryPassword),
style = h8 style = LocalType.current.h8
) )
Text( Text(
stringResource(R.string.recoveryPasswordHideRecoveryPasswordDescription), stringResource(R.string.recoveryPasswordHideRecoveryPasswordDescription),
style = base style = LocalType.current.base
) )
} }
Spacer(modifier = Modifier.width(LocalDimensions.current.xxsMargin)) Spacer(modifier = Modifier.width(LocalDimensions.current.xsSpacing))
SlimOutlineButton( SlimOutlineButton(
text = stringResource(R.string.hide), text = stringResource(R.string.hide),
modifier = Modifier modifier = Modifier
@ -180,7 +179,7 @@ private fun HideRecoveryPasswordCell(onHide: () -> Unit = {}) {
@Preview @Preview
@Composable @Composable
private fun PreviewRecoveryPasswordScreen( private fun PreviewRecoveryPasswordScreen(
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
) { ) {
PreviewTheme(colors) { PreviewTheme(colors) {
RecoveryPasswordScreen(mnemonic = "voyage urban toyed maverick peculiar tuxedo penguin tree grass building listen speak withdraw terminal plane") RecoveryPasswordScreen(mnemonic = "voyage urban toyed maverick peculiar tuxedo penguin tree grass building listen speak withdraw terminal plane")

View File

@ -34,7 +34,7 @@ class RecoveryPasswordActivity : BaseActionBarActivity() {
showSessionDialog { showSessionDialog {
title(R.string.recoveryPasswordHidePermanently) title(R.string.recoveryPasswordHidePermanently)
htmlText(R.string.recoveryPasswordHidePermanentlyDescription1) htmlText(R.string.recoveryPasswordHidePermanentlyDescription1)
destructiveButton(R.string.continue_2, R.string.AccessibilityId_continue) { onHideConfirm() } dangerButton(R.string.continue_2, R.string.AccessibilityId_continue) { onHideConfirm() }
cancelButton() cancelButton()
} }
} }
@ -44,7 +44,7 @@ class RecoveryPasswordActivity : BaseActionBarActivity() {
title(R.string.recoveryPasswordHidePermanently) title(R.string.recoveryPasswordHidePermanently)
text(R.string.recoveryPasswordHidePermanentlyDescription2) text(R.string.recoveryPasswordHidePermanentlyDescription2)
cancelButton() cancelButton()
destructiveButton( dangerButton(
R.string.yes, R.string.yes,
contentDescription = R.string.AccessibilityId_confirm_button contentDescription = R.string.AccessibilityId_confirm_button
) { ) {

View File

@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.ui package org.thoughtcrime.securesms.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.IntrinsicSize
@ -8,11 +9,13 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.Icon import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material.IconButton import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material.MaterialTheme import androidx.compose.material3.Icon
import androidx.compose.material.Text import androidx.compose.material3.IconButton
import androidx.compose.material.TextButton import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -20,9 +23,16 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.takeOrElse import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.theme.LocalType
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.theme.bold
class DialogButtonModel( class DialogButtonModel(
val text: GetString, val text: GetString,
@ -32,20 +42,25 @@ class DialogButtonModel(
val onClick: () -> Unit = {}, val onClick: () -> Unit = {},
) )
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun AlertDialog( fun AlertDialog(
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
title: String? = null, title: String? = null,
text: String? = null, text: String? = null,
content: @Composable () -> Unit = {}, buttons: List<DialogButtonModel>? = null,
buttons: List<DialogButtonModel>? = null showCloseButton: Boolean = false,
content: @Composable () -> Unit = {}
) { ) {
androidx.compose.material.AlertDialog( BasicAlertDialog(
onDismissRequest, onDismissRequest = onDismissRequest,
shape = MaterialTheme.shapes.small, content = {
backgroundColor = LocalColors.current.backgroundSecondary, Box(
buttons = { modifier = Modifier.background(color = LocalColors.current.backgroundSecondary,
Box { shape = MaterialTheme.shapes.small)
) {
// only show the 'x' button is required
if(showCloseButton) {
IconButton( IconButton(
onClick = onDismissRequest, onClick = onDismissRequest,
modifier = Modifier.align(Alignment.TopEnd) modifier = Modifier.align(Alignment.TopEnd)
@ -56,29 +71,30 @@ fun AlertDialog(
contentDescription = "back" contentDescription = "back"
) )
} }
}
Column(modifier = Modifier.fillMaxWidth()) { Column(modifier = Modifier.fillMaxWidth()) {
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(top = LocalDimensions.current.smallItemSpacing) .padding(top = LocalDimensions.current.smallSpacing)
.padding(horizontal = LocalDimensions.current.smallItemSpacing) .padding(horizontal = LocalDimensions.current.smallSpacing)
) { ) {
title?.let { title?.let {
Text( Text(
it, it,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
style = h7, style = LocalType.current.h7,
modifier = Modifier.padding(bottom = LocalDimensions.current.xxsItemSpacing) modifier = Modifier.padding(bottom = LocalDimensions.current.xxsSpacing)
) )
} }
text?.let { text?.let {
Text( Text(
it, it,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
style = large, style = LocalType.current.large,
modifier = Modifier.padding(bottom = LocalDimensions.current.xxsItemSpacing) modifier = Modifier.padding(bottom = LocalDimensions.current.xxsSpacing)
) )
} }
content() content()
@ -116,12 +132,59 @@ fun DialogButton(text: String, modifier: Modifier, color: Color = Color.Unspecif
Text( Text(
text, text,
color = color.takeOrElse { LocalColors.current.text }, color = color.takeOrElse { LocalColors.current.text },
style = largeBold, style = LocalType.current.large.bold(),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.padding( modifier = Modifier.padding(
top = LocalDimensions.current.smallItemSpacing, top = LocalDimensions.current.smallSpacing,
bottom = LocalDimensions.current.itemSpacing bottom = LocalDimensions.current.spacing
) )
) )
} }
} }
@Preview
@Composable
fun PreviewSimpleDialog(){
PreviewTheme {
AlertDialog(
onDismissRequest = {},
title = stringResource(R.string.warning),
text = stringResource(R.string.you_cannot_go_back_further_in_order_to_stop_loading_your_account_session_needs_to_quit),
buttons = listOf(
DialogButtonModel(
GetString(stringResource(R.string.quit)),
color = LocalColors.current.danger,
onClick = {}
),
DialogButtonModel(
GetString(stringResource(R.string.cancel))
)
)
)
}
}
@Preview
@Composable
fun PreviewXCloseDialog(){
PreviewTheme {
AlertDialog(
title = stringResource(R.string.urlOpen),
text = stringResource(R.string.urlOpenBrowser),
showCloseButton = true, // display the 'x' button
buttons = listOf(
DialogButtonModel(
text = GetString(R.string.activity_landing_terms_of_service),
contentDescription = GetString(R.string.AccessibilityId_terms_of_service_button),
onClick = {}
),
DialogButtonModel(
text = GetString(R.string.activity_landing_privacy_policy),
contentDescription = GetString(R.string.AccessibilityId_privacy_policy_button),
onClick = {}
)
),
onDismissRequest = {}
)
}
}

View File

@ -16,12 +16,8 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Card import androidx.compose.material3.Icon
import androidx.compose.material.ContentAlpha import androidx.compose.material3.IconButton
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.LocalContentAlpha
import androidx.compose.material.LocalContentColor
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
@ -37,26 +33,27 @@ import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.theme.blackAlpha40
import org.thoughtcrime.securesms.ui.theme.pillShape
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
import kotlin.math.sign import kotlin.math.sign
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun BoxScope.HorizontalPagerIndicator(pagerState: PagerState) { fun BoxScope.HorizontalPagerIndicator(pagerState: PagerState) {
if (pagerState.pageCount >= 2) Card( if (pagerState.pageCount >= 2) Box(
shape = pillShape,
backgroundColor = Color.Black.copy(alpha = 0.4f),
modifier = Modifier modifier = Modifier
.background(color = blackAlpha40, shape = pillShape)
.align(Alignment.BottomCenter) .align(Alignment.BottomCenter)
.padding(8.dp) .padding(LocalDimensions.current.xxsSpacing)
) { ) {
Box(modifier = Modifier.padding(8.dp)) { Box(modifier = Modifier.padding(LocalDimensions.current.xxsSpacing)) {
ClickableHorizontalPagerIndicator( ClickableHorizontalPagerIndicator(
pagerState = pagerState, pagerState = pagerState,
pageCount = pagerState.pageCount, pageCount = pagerState.pageCount
activeColor = Color.White, )
inactiveColor = LocalColors.current.textSecondary)
} }
} }
} }
@ -73,9 +70,9 @@ fun ClickableHorizontalPagerIndicator(
pageCount: Int, pageCount: Int,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
pageIndexMapping: (Int) -> Int = { it }, pageIndexMapping: (Int) -> Int = { it },
activeColor: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current), activeColor: Color = Color.White,
inactiveColor: Color = activeColor.copy(ContentAlpha.disabled), inactiveColor: Color = LocalColors.current.disabled,
indicatorWidth: Dp = 8.dp, indicatorWidth: Dp = LocalDimensions.current.xxsSpacing,
indicatorHeight: Dp = indicatorWidth, indicatorHeight: Dp = indicatorWidth,
spacing: Dp = indicatorWidth, spacing: Dp = indicatorWidth,
indicatorShape: Shape = CircleShape, indicatorShape: Shape = CircleShape,
@ -115,9 +112,9 @@ private fun HorizontalPagerIndicator(
pageCount: Int, pageCount: Int,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
pageIndexMapping: (Int) -> Int = { it }, pageIndexMapping: (Int) -> Int = { it },
activeColor: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current), activeColor: Color = Color.White,
inactiveColor: Color = activeColor.copy(ContentAlpha.disabled), inactiveColor: Color = LocalColors.current.disabled,
indicatorWidth: Dp = 8.dp, indicatorWidth: Dp = LocalDimensions.current.xxsSpacing,
indicatorHeight: Dp = indicatorWidth, indicatorHeight: Dp = indicatorWidth,
spacing: Dp = indicatorWidth, spacing: Dp = indicatorWidth,
indicatorShape: Shape = CircleShape, indicatorShape: Shape = CircleShape,

View File

@ -7,13 +7,13 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Canvas import androidx.compose.foundation.Canvas
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.ScrollState import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.clickable import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.heightIn
@ -24,14 +24,14 @@ import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material.ButtonColors import androidx.compose.material3.ButtonColors
import androidx.compose.material.Card import androidx.compose.material3.Card
import androidx.compose.material.Icon import androidx.compose.material3.HorizontalDivider
import androidx.compose.material.LocalContentColor import androidx.compose.material3.Icon
import androidx.compose.material.MaterialTheme import androidx.compose.material3.LocalContentColor
import androidx.compose.material.RadioButton import androidx.compose.material3.MaterialTheme
import androidx.compose.material.Text import androidx.compose.material3.Text
import androidx.compose.material.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
@ -52,6 +52,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
@ -61,14 +62,15 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import network.loki.messenger.R import network.loki.messenger.R
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.session.libsession.utilities.runIf
import org.thoughtcrime.securesms.components.ProfilePictureView import org.thoughtcrime.securesms.components.ProfilePictureView
import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.OptionsCard import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.OptionsCardData
import org.thoughtcrime.securesms.ui.color.LocalColors
import org.thoughtcrime.securesms.ui.color.divider
import org.thoughtcrime.securesms.ui.color.radioButtonColors
import org.thoughtcrime.securesms.ui.color.transparentButtonColors
import org.thoughtcrime.securesms.ui.components.SmallCircularProgressIndicator import org.thoughtcrime.securesms.ui.components.SmallCircularProgressIndicator
import org.thoughtcrime.securesms.ui.components.TitledRadioButton
import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.theme.LocalType
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.theme.transparentButtonColors
import kotlin.math.min import kotlin.math.min
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -92,18 +94,25 @@ data class RadioOption<T>(
) )
@Composable @Composable
fun <T> OptionsCard(card: OptionsCard<T>, callbacks: Callbacks<T>) { fun <T> OptionsCard(card: OptionsCardData<T>, callbacks: Callbacks<T>) {
Column {
Text( Text(
card.title(), modifier = Modifier.padding(start = LocalDimensions.current.smallSpacing),
style = base text = card.title(),
style = LocalType.current.base,
color = LocalColors.current.textSecondary
) )
Spacer(modifier = Modifier.height(LocalDimensions.current.xsSpacing))
CellNoMargin { CellNoMargin {
LazyColumn( LazyColumn(
modifier = Modifier.heightIn(max = 5000.dp) modifier = Modifier.heightIn(max = 5000.dp)
) { ) {
itemsIndexed(card.options) { i, it -> itemsIndexed(card.options) { i, it ->
if (i != 0) Divider() if (i != 0) Divider()
TitledRadioButton(it) { callbacks.setValue(it.value) } TitledRadioButton(option = it) { callbacks.setValue(it.value) }
}
} }
} }
} }
@ -117,7 +126,10 @@ fun LargeItemButtonWithDrawable(
colors: ButtonColors = transparentButtonColors(), colors: ButtonColors = transparentButtonColors(),
onClick: () -> Unit onClick: () -> Unit
) { ) {
ItemButtonWithDrawable(textId, icon, modifier.heightIn(min = LocalDimensions.current.minLargeItemButtonHeight), h8, colors, onClick) ItemButtonWithDrawable(
textId, icon, modifier.heightIn(min = LocalDimensions.current.minLargeItemButtonHeight),
LocalType.current.h8, colors, onClick
)
} }
@Composable @Composable
@ -125,7 +137,7 @@ fun ItemButtonWithDrawable(
@StringRes textId: Int, @StringRes textId: Int,
@DrawableRes icon: Int, @DrawableRes icon: Int,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
textStyle: TextStyle = xl, textStyle: TextStyle = LocalType.current.xl,
colors: ButtonColors = transparentButtonColors(), colors: ButtonColors = transparentButtonColors(),
onClick: () -> Unit onClick: () -> Unit
) { ) {
@ -155,7 +167,10 @@ fun LargeItemButton(
colors: ButtonColors = transparentButtonColors(), colors: ButtonColors = transparentButtonColors(),
onClick: () -> Unit onClick: () -> Unit
) { ) {
ItemButton(textId, icon, modifier.heightIn(min = LocalDimensions.current.minLargeItemButtonHeight), h8, colors, onClick) ItemButton(
textId, icon, modifier.heightIn(min = LocalDimensions.current.minLargeItemButtonHeight),
LocalType.current.h8, colors, onClick
)
} }
/** /**
@ -166,7 +181,7 @@ fun ItemButton(
@StringRes textId: Int, @StringRes textId: Int,
@DrawableRes icon: Int, @DrawableRes icon: Int,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
textStyle: TextStyle = xl, textStyle: TextStyle = LocalType.current.xl,
colors: ButtonColors = transparentButtonColors(), colors: ButtonColors = transparentButtonColors(),
onClick: () -> Unit onClick: () -> Unit
) { ) {
@ -196,7 +211,7 @@ fun ItemButton(
text: String, text: String,
icon: @Composable BoxScope.() -> Unit, icon: @Composable BoxScope.() -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
textStyle: TextStyle = xl, textStyle: TextStyle = LocalType.current.xl,
colors: ButtonColors = transparentButtonColors(), colors: ButtonColors = transparentButtonColors(),
onClick: () -> Unit onClick: () -> Unit
) { ) {
@ -208,27 +223,42 @@ fun ItemButton(
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
.width(80.dp) .width(50.dp)
.wrapContentHeight() .wrapContentHeight()
.align(Alignment.CenterVertically) .align(Alignment.CenterVertically)
) { ) {
icon() icon()
} }
Spacer(modifier = Modifier.width(LocalDimensions.current.smallSpacing))
Text( Text(
text, text,
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(vertical = LocalDimensions.current.xsItemSpacing) .padding(vertical = LocalDimensions.current.xsSpacing)
.align(Alignment.CenterVertically), .align(Alignment.CenterVertically),
style = textStyle style = textStyle
) )
} }
} }
@Preview
@Composable
fun PrewviewItemButton() {
PreviewTheme {
ItemButton(
textId = R.string.activity_create_group_title,
icon = R.drawable.ic_group,
onClick = {}
)
}
}
@Composable @Composable
fun Cell( fun Cell(
padding: Dp = 0.dp, padding: Dp = 0.dp,
margin: Dp = LocalDimensions.current.margin, margin: Dp = LocalDimensions.current.spacing,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
CellWithPaddingAndMargin(padding, margin) { content() } CellWithPaddingAndMargin(padding, margin) { content() }
@ -240,64 +270,22 @@ fun CellNoMargin(content: @Composable () -> Unit) {
@Composable @Composable
fun CellWithPaddingAndMargin( fun CellWithPaddingAndMargin(
padding: Dp = LocalDimensions.current.smallMargin, padding: Dp = LocalDimensions.current.spacing,
margin: Dp = LocalDimensions.current.margin, margin: Dp = LocalDimensions.current.spacing,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
Card( Box(
backgroundColor = LocalColors.current.backgroundSecondary,
shape = MaterialTheme.shapes.medium,
elevation = 0.dp,
modifier = Modifier modifier = Modifier
.padding(horizontal = margin)
.background(color = LocalColors.current.backgroundSecondary,
shape = MaterialTheme.shapes.small)
.wrapContentHeight() .wrapContentHeight()
.fillMaxWidth() .fillMaxWidth(),
.padding(horizontal = margin),
) { ) {
Box(Modifier.padding(padding)) { content() } Box(Modifier.padding(padding)) { content() }
} }
} }
@Composable
fun <T> TitledRadioButton(option: RadioOption<T>, onClick: () -> Unit) {
val color = if (option.enabled) LocalColors.current.text else LocalColors.current.disabled
Row(
horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing),
modifier = Modifier
.runIf(option.enabled) { clickable { if (!option.selected) onClick() } }
.heightIn(min = 60.dp)
.padding(horizontal = LocalDimensions.current.margin)
.contentDescription(option.contentDescription)
) {
Column(modifier = Modifier
.weight(1f)
.align(Alignment.CenterVertically)) {
Column {
Text(
text = option.title(),
style = large,
color = color
)
option.subtitle?.let {
Text(
text = it(),
style = extraSmall,
color = color
)
}
}
}
RadioButton(
selected = option.selected,
onClick = null,
modifier = Modifier
.height(26.dp)
.align(Alignment.CenterVertically),
enabled = option.enabled,
colors = LocalColors.current.radioButtonColors()
)
}
}
@Composable @Composable
fun Modifier.contentDescription(text: GetString?): Modifier { fun Modifier.contentDescription(text: GetString?): Modifier {
return text?.let { return text?.let {
@ -357,10 +345,10 @@ fun Modifier.fadingEdges(
@Composable @Composable
fun Divider(modifier: Modifier = Modifier, startIndent: Dp = 0.dp) { fun Divider(modifier: Modifier = Modifier, startIndent: Dp = 0.dp) {
androidx.compose.material.Divider( HorizontalDivider(
modifier = modifier.padding(horizontal = LocalDimensions.current.xsMargin), modifier = modifier.padding(horizontal = LocalDimensions.current.smallSpacing)
color = LocalColors.current.divider, .padding(start = startIndent),
startIndent = startIndent color = LocalColors.current.borders,
) )
} }
@ -392,7 +380,7 @@ fun ProgressArc(progress: Float, modifier: Modifier = Modifier) {
"${text}%", "${text}%",
color = Color.White, color = Color.White,
modifier = Modifier.align(Alignment.Center), modifier = Modifier.align(Alignment.Center),
style = h2 style = LocalType.current.h2
) )
} }
} }

View File

@ -1,32 +0,0 @@
package org.thoughtcrime.securesms.ui
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
val LocalDimensions = staticCompositionLocalOf { Dimensions() }
data class Dimensions(
val xxxsItemSpacing: Dp = 4.dp,
val xxsItemSpacing: Dp = 8.dp,
val xsItemSpacing: Dp = 12.dp,
val smallItemSpacing: Dp = 16.dp,
val itemSpacing: Dp = 24.dp,
val xxxsMargin: Dp = 8.dp,
val xxsMargin: Dp = 12.dp,
val xsMargin: Dp = 16.dp,
val smallMargin: Dp = 24.dp,
val margin: Dp = 32.dp,
val onboardingMargin: Dp = 36.dp,
val largeMargin: Dp = 64.dp,
val homeEmptyViewMargin: Dp = 50.dp,
val dividerIndent: Dp = 80.dp,
val appBarHeight: Dp = 64.dp,
val minScrollableViewHeight: Dp = 200.dp,
val minLargeItemButtonHeight: Dp = 60.dp,
val indicatorHeight: Dp = 4.dp,
val borderStroke: Dp = 1.dp
)

View File

@ -1,59 +0,0 @@
package org.thoughtcrime.securesms.ui
import androidx.compose.material.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontFamily.Companion.Monospace
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.sp
fun boldStyle(size: TextUnit) = TextStyle.Default.copy(
fontSize = size,
lineHeight = size * 1.2,
fontWeight = FontWeight.Bold,
)
fun defaultStyle(size: TextUnit, fontFamily: FontFamily? = TextStyle.Default.fontFamily) = TextStyle.Default.copy(
fontSize = size,
lineHeight = size * 1.2,
fontFamily = fontFamily
)
val xl = defaultStyle(18.sp)
val large = defaultStyle(16.sp)
val largeBold = boldStyle(16.sp)
val base = defaultStyle(14.sp)
val baseBold = boldStyle(14.sp)
val baseMonospace = defaultStyle(14.sp, fontFamily = Monospace)
val small = defaultStyle(12.sp)
val smallBold = boldStyle(12.sp)
val smallMonospace = defaultStyle(12.sp, fontFamily = Monospace)
val extraSmall = defaultStyle(11.sp)
val extraSmallBold = boldStyle(11.sp)
val extraSmallMonospace = defaultStyle(11.sp, fontFamily = Monospace)
val fine = defaultStyle(9.sp)
val h1 = boldStyle(36.sp)
val h2 = boldStyle(32.sp)
val h3 = boldStyle(29.sp)
val h4 = boldStyle(26.sp)
val h5 = boldStyle(23.sp)
val h6 = boldStyle(20.sp)
val h7 = boldStyle(18.sp)
val h8 = boldStyle(16.sp)
val h9 = boldStyle(14.sp)
val sessionTypography = Typography(
h1 = h1,
h2 = h2,
h3 = h3,
h4 = h4,
h5 = h5,
h6 = h6,
)

View File

@ -5,6 +5,7 @@ import android.content.Context
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import org.thoughtcrime.securesms.ui.theme.SessionMaterialTheme
fun Activity.setComposeContent(content: @Composable () -> Unit) { fun Activity.setComposeContent(content: @Composable () -> Unit) {
ComposeView(this) ComposeView(this)

View File

@ -1,233 +0,0 @@
package org.thoughtcrime.securesms.ui.color
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.text.selection.TextSelectionColors
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.MaterialTheme
import androidx.compose.material.RadioButtonDefaults
import androidx.compose.material.TabRowDefaults
import androidx.compose.material.Text
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.primarySurface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider
import org.thoughtcrime.securesms.ui.base
val LocalColors = staticCompositionLocalOf<Colors> { ClassicDark() }
interface Colors {
val isLight: Boolean
val primary: Color
val danger: Color
val disabled: Color
val background: Color
val backgroundSecondary: Color
val text: Color
val textSecondary: Color
val borders: Color
val textBubbleSent: Color
val backgroundBubbleReceived: Color
val textBubbleReceived: Color
val backgroundBubbleSent: Color get() = primary
val qrCodeContent: Color
val qrCodeBackground: Color
val primaryButtonFill: Color
val primaryButtonFillText: Color
}
fun Colors.text(isError: Boolean): Color = if (isError) danger else text
fun Colors.textSecondary(isError: Boolean): Color = if (isError) danger else textSecondary
fun Colors.borders(isError: Boolean): Color = if (isError) danger else borders
val Colors.textSelectionColors get() = TextSelectionColors(
handleColor = primary,
backgroundColor = primary.copy(alpha = 0.5f)
)
data class ClassicDark(override val primary: Color = primaryGreen): Colors {
override val isLight = false
override val danger = dangerDark
override val disabled = disabledDark
override val background = classicDark0
override val backgroundSecondary = classicDark1
override val text = classicDark6
override val textSecondary = classicDark5
override val borders = classicDark3
override val textBubbleSent = Color.Black
override val backgroundBubbleReceived = classicDark2
override val textBubbleReceived = Color.White
override val qrCodeContent = background
override val qrCodeBackground = text
override val primaryButtonFill = primary
override val primaryButtonFillText = Color.Black
}
data class ClassicLight(override val primary: Color = primaryGreen): Colors {
override val isLight = true
override val danger = dangerLight
override val disabled = disabledLight
override val background = classicLight6
override val backgroundSecondary = classicLight5
override val text = classicLight0
override val textSecondary = classicLight1
override val borders = classicLight3
override val textBubbleSent = Color.Black
override val backgroundBubbleReceived = classicLight4
override val textBubbleReceived = classicLight4
override val qrCodeContent = text
override val qrCodeBackground = backgroundSecondary
override val primaryButtonFill = text
override val primaryButtonFillText = Color.White
}
data class OceanDark(override val primary: Color = primaryBlue): Colors {
override val isLight = false
override val danger = dangerDark
override val disabled = disabledDark
override val background = oceanDark2
override val backgroundSecondary = oceanDark1
override val text = oceanDark7
override val textSecondary = oceanDark5
override val borders = oceanDark4
override val textBubbleSent = Color.Black
override val backgroundBubbleReceived = oceanDark4
override val textBubbleReceived = oceanDark4
override val qrCodeContent = background
override val qrCodeBackground = text
override val primaryButtonFill = primary
override val primaryButtonFillText = Color.Black
}
data class OceanLight(override val primary: Color = primaryBlue): Colors {
override val isLight = true
override val danger = dangerLight
override val disabled = disabledLight
override val background = oceanLight7
override val backgroundSecondary = oceanLight6
override val text = oceanLight1
override val textSecondary = oceanLight2
override val borders = oceanLight3
override val textBubbleSent = oceanLight1
override val backgroundBubbleReceived = oceanLight4
override val textBubbleReceived = oceanLight1
override val qrCodeContent = text
override val qrCodeBackground = backgroundSecondary
override val primaryButtonFill = text
override val primaryButtonFillText = Color.White
}
@Composable
fun Colors(name: String, colors: List<Color>) {
Column {
colors.forEachIndexed { i, it ->
Box(Modifier.background(it)) {
Text("$name: $i")
}
}
}
}
@Preview
@Composable
fun PreviewThemeColors(
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors
) {
PreviewTheme(colors) { ThemeColors() }
}
@Composable
private fun ThemeColors() {
Column {
Box(Modifier.background(MaterialTheme.colors.primary)) {
Text("primary", style = base)
}
Box(Modifier.background(MaterialTheme.colors.primaryVariant)) {
Text("primaryVariant", style = base)
}
Box(Modifier.background(MaterialTheme.colors.secondary)) {
Text("secondary", style = base)
}
Box(Modifier.background(MaterialTheme.colors.secondaryVariant)) {
Text("secondaryVariant", style = base)
}
Box(Modifier.background(MaterialTheme.colors.surface)) {
Text("surface", style = base)
}
Box(Modifier.background(MaterialTheme.colors.primarySurface)) {
Text("primarySurface", style = base)
}
Box(Modifier.background(MaterialTheme.colors.background)) {
Text("background", style = base)
}
Box(Modifier.background(MaterialTheme.colors.error)) {
Text("error", style = base)
}
}
}
@Composable
fun Colors.outlinedTextFieldColors(
isError: Boolean
) = TextFieldDefaults.outlinedTextFieldColors(
textColor = if (isError) danger else text,
cursorColor = if (isError) danger else text,
focusedBorderColor = borders,
unfocusedBorderColor = borders,
placeholderColor = if (isError) danger else textSecondary
)
val Colors.divider get() = text.copy(alpha = TabRowDefaults.DividerOpacity)
@Composable
fun Colors.radioButtonColors() = RadioButtonDefaults.colors(
selectedColor = primary,
unselectedColor = text,
disabledColor = disabled
)
@Composable
fun transparentButtonColors() = ButtonDefaults.buttonColors(backgroundColor = Color.Transparent)
@Composable
fun destructiveButtonColors() = ButtonDefaults.buttonColors(backgroundColor = Color.Transparent, contentColor = LocalColors.current.danger)
/**
* This class holds two instances of [Colors], [light] representing the [Colors] to use when the system is in a
* light theme, and [dark] representing the [Colors] to use when the system is in a dark theme.
*
* If the user has [followSystemSettings] turned on then [light] should be equal to [dark].
*/
data class LightDarkColors(
val light: Colors,
val dark: Colors
) {
@Composable
fun colors() = if (light == dark || isSystemInDarkTheme()) dark else light
}
/**
* Courtesy constructor that sets [light] and [dark] based on properties.
*/
fun LightDarkColors(isClassic: Boolean, isLight: Boolean, followSystemSettings: Boolean, primaryOrUnspecified: Color): LightDarkColors {
val primary = primaryOrUnspecified.takeOrElse { if (isClassic) primaryGreen else primaryBlue }
val light = when {
isLight || followSystemSettings -> if (isClassic) ClassicLight(primary) else OceanLight(primary)
else -> if (isClassic) ClassicDark(primary) else OceanDark(primary)
}
val dark = when {
isLight && !followSystemSettings -> if (isClassic) ClassicLight(primary) else OceanLight(primary)
else -> if (isClassic) ClassicDark(primary) else OceanDark(primary)
}
return LightDarkColors(light, dark)
}

View File

@ -1,35 +0,0 @@
package org.thoughtcrime.securesms.ui.color
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.TextSecurePreferences.Companion.BLUE_ACCENT
import org.session.libsession.utilities.TextSecurePreferences.Companion.CLASSIC_DARK
import org.session.libsession.utilities.TextSecurePreferences.Companion.CLASSIC_LIGHT
import org.session.libsession.utilities.TextSecurePreferences.Companion.GREEN_ACCENT
import org.session.libsession.utilities.TextSecurePreferences.Companion.OCEAN_LIGHT
import org.session.libsession.utilities.TextSecurePreferences.Companion.ORANGE_ACCENT
import org.session.libsession.utilities.TextSecurePreferences.Companion.PINK_ACCENT
import org.session.libsession.utilities.TextSecurePreferences.Companion.PURPLE_ACCENT
import org.session.libsession.utilities.TextSecurePreferences.Companion.RED_ACCENT
import org.session.libsession.utilities.TextSecurePreferences.Companion.YELLOW_ACCENT
/**
* Retrieve the current [Colors] from [TextSecurePreferences] and current system settings.
*/
@Composable
fun TextSecurePreferences.colors(): Colors = lightDarkColors().colors()
private fun TextSecurePreferences.lightDarkColors() = LightDarkColors(isClassic(), isLight(), getFollowSystemSettings(), primaryColor())
private fun TextSecurePreferences.isLight(): Boolean = getThemeStyle() in setOf(CLASSIC_LIGHT, OCEAN_LIGHT)
private fun TextSecurePreferences.isClassic(): Boolean = getThemeStyle() in setOf(CLASSIC_DARK, CLASSIC_LIGHT)
private fun TextSecurePreferences.primaryColor(): Color = when(getSelectedAccentColor()) {
GREEN_ACCENT -> primaryGreen
BLUE_ACCENT -> primaryBlue
PURPLE_ACCENT -> primaryPurple
PINK_ACCENT -> primaryPink
RED_ACCENT -> primaryRed
ORANGE_ACCENT -> primaryOrange
YELLOW_ACCENT -> primaryYellow
else -> Color.Unspecified
}

View File

@ -5,9 +5,9 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material.Icon import androidx.compose.material3.Icon
import androidx.compose.material.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -15,16 +15,16 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.theme.LocalType
import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.color.Colors import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
import org.thoughtcrime.securesms.ui.h4 import org.thoughtcrime.securesms.ui.theme.ThemeColors
@Preview @Preview
@Composable @Composable
fun AppBarPreview( fun AppBarPreview(
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
) { ) {
PreviewTheme(colors) { PreviewTheme(colors) {
AppBar(title = "Title", {}, {}) AppBar(title = "Title", {}, {})
@ -42,7 +42,7 @@ fun AppBar(title: String, onClose: () -> Unit = {}, onBack: (() -> Unit)? = null
} }
} }
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
Text(text = title, style = h4) Text(text = title, style = LocalType.current.h4)
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
Box(contentAlignment = Alignment.Center, modifier = Modifier.size(LocalDimensions.current.appBarHeight)) { Box(contentAlignment = Alignment.Center, modifier = Modifier.size(LocalDimensions.current.appBarHeight)) {
IconButton(onClick = onClose) { IconButton(onClick = onClose) {

View File

@ -1,12 +1,12 @@
package org.thoughtcrime.securesms.ui.components package org.thoughtcrime.securesms.ui.components
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.material.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.SolidColor
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalColors
@Composable @Composable
fun Modifier.border() = this.border( fun Modifier.border() = this.border(

View File

@ -16,8 +16,8 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.ButtonColors import androidx.compose.material3.ButtonColors
import androidx.compose.material.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -37,13 +37,14 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.ui.LaunchedEffectAsync import org.thoughtcrime.securesms.ui.LaunchedEffectAsync
import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider
import org.thoughtcrime.securesms.ui.baseBold
import org.thoughtcrime.securesms.ui.buttonShape
import org.thoughtcrime.securesms.ui.color.Colors
import org.thoughtcrime.securesms.ui.color.LocalColors
import org.thoughtcrime.securesms.ui.contentDescription import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.theme.LocalType
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
import org.thoughtcrime.securesms.ui.theme.ThemeColors
import org.thoughtcrime.securesms.ui.theme.bold
import org.thoughtcrime.securesms.ui.theme.buttonShape
import kotlin.time.Duration import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
@ -65,16 +66,16 @@ fun Button(
content: @Composable RowScope.() -> Unit content: @Composable RowScope.() -> Unit
) { ) {
style.applyButtonConstraints { style.applyButtonConstraints {
androidx.compose.material.Button( androidx.compose.material3.Button(
onClick, onClick = onClick,
modifier.heightIn(min = style.minHeight), modifier = modifier.heightIn(min = style.minHeight),
enabled, enabled = enabled,
interactionSource, interactionSource = interactionSource,
elevation = null, elevation = null,
shape, shape = shape,
border, border = border,
colors, colors = colors,
contentPadding contentPadding = contentPadding
) { ) {
// Button sets LocalTextStyle, so text style is applied inside to override that. // Button sets LocalTextStyle, so text style is applied inside to override that.
style.applyTextConstraints { style.applyTextConstraints {
@ -129,11 +130,11 @@ fun Button(
Button(text, onClick, ButtonType.Outline(LocalColors.current.primaryButtonFill), modifier, enabled) Button(text, onClick, ButtonType.Outline(LocalColors.current.primaryButtonFill), modifier, enabled)
} }
@Composable fun PrimaryOutlineButton(onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, content: @Composable RowScope.() -> Unit) { @Composable fun PrimaryOutlineButton(modifier: Modifier = Modifier, enabled: Boolean = true, onClick: () -> Unit, content: @Composable RowScope.() -> Unit) {
Button(onClick, ButtonType.Outline(LocalColors.current.primaryButtonFill), modifier, enabled, content = content) Button(onClick, ButtonType.Outline(LocalColors.current.primaryButtonFill), modifier, enabled, content = content)
} }
@Composable fun SlimOutlineButton(onClick: () -> Unit, modifier: Modifier = Modifier, color: Color = LocalColors.current.text, enabled: Boolean = true, content: @Composable RowScope.() -> Unit) { @Composable fun SlimOutlineButton(modifier: Modifier = Modifier, color: Color = LocalColors.current.text, enabled: Boolean = true, onClick: () -> Unit, content: @Composable RowScope.() -> Unit) {
Button(onClick, ButtonType.Outline(color), modifier, enabled, ButtonStyle.Slim, content = content) Button(onClick, ButtonType.Outline(color), modifier, enabled, ButtonStyle.Slim, content = content)
} }
@ -257,7 +258,7 @@ fun BorderlessButtonWithIcon(
text: String, text: String,
@DrawableRes iconRes: Int, @DrawableRes iconRes: Int,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
style: TextStyle = baseBold, style: TextStyle = LocalType.current.base.bold(),
color: Color = LocalColors.current.text, color: Color = LocalColors.current.text,
onClick: () -> Unit onClick: () -> Unit
) { ) {
@ -292,7 +293,7 @@ val MutableInteractionSource.releases
@Preview @Preview
@Composable @Composable
private fun VariousButtons( private fun VariousButtons(
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
) { ) {
PreviewTheme(colors) { PreviewTheme(colors) {
FlowRow( FlowRow(

View File

@ -1,21 +1,20 @@
package org.thoughtcrime.securesms.ui.components package org.thoughtcrime.securesms.ui.components
import android.annotation.SuppressLint import android.annotation.SuppressLint
import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material.LocalMinimumInteractiveComponentEnforcement import androidx.compose.material3.LocalMinimumInteractiveComponentEnforcement
import androidx.compose.material.LocalTextStyle import androidx.compose.material3.LocalTextStyle
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import org.thoughtcrime.securesms.ui.baseBold import org.thoughtcrime.securesms.ui.theme.LocalType
import org.thoughtcrime.securesms.ui.extraSmall import org.thoughtcrime.securesms.ui.theme.bold
import org.thoughtcrime.securesms.ui.extraSmallBold
interface ButtonStyle { interface ButtonStyle {
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterial3Api::class)
@SuppressLint("ComposableNaming") @SuppressLint("ComposableNaming")
@Composable fun applyButtonConstraints(content: @Composable () -> Unit) { @Composable fun applyButtonConstraints(content: @Composable () -> Unit) {
CompositionLocalProvider( CompositionLocalProvider(
@ -27,26 +26,34 @@ interface ButtonStyle {
@SuppressLint("ComposableNaming") @SuppressLint("ComposableNaming")
@Composable fun applyTextConstraints(content: @Composable () -> Unit) { @Composable fun applyTextConstraints(content: @Composable () -> Unit) {
CompositionLocalProvider( CompositionLocalProvider(
LocalTextStyle provides textStyle, LocalTextStyle provides textStyle(),
content = content content = content
) )
} }
val textStyle: TextStyle @Composable
fun textStyle() : TextStyle
val minHeight: Dp val minHeight: Dp
object Large: ButtonStyle { object Large: ButtonStyle {
override val textStyle = baseBold.copy(textAlign = TextAlign.Center) @Composable
override fun textStyle() = LocalType.current.base.bold()
.copy(textAlign = TextAlign.Center)
override val minHeight = 41.dp override val minHeight = 41.dp
} }
object Slim: ButtonStyle { object Slim: ButtonStyle {
override val textStyle = extraSmallBold.copy(textAlign = TextAlign.Center) @Composable
override fun textStyle() = LocalType.current.extraSmall.bold()
.copy(textAlign = TextAlign.Center)
override val minHeight = 29.dp override val minHeight = 29.dp
} }
object Borderless: ButtonStyle { object Borderless: ButtonStyle {
override val textStyle = extraSmall.copy(textAlign = TextAlign.Center) @Composable
override fun textStyle() = LocalType.current.extraSmall
.copy(textAlign = TextAlign.Center)
override val minHeight = 37.dp override val minHeight = 37.dp
} }
} }

View File

@ -2,13 +2,13 @@ package org.thoughtcrime.securesms.ui.components
import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.material.ButtonColors import androidx.compose.material3.ButtonColors
import androidx.compose.material.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalDimensions
private val disabledBorder @Composable get() = BorderStroke( private val disabledBorder @Composable get() = BorderStroke(
width = LocalDimensions.current.borderStroke, width = LocalDimensions.current.borderStroke,
@ -35,9 +35,9 @@ interface ButtonType {
@Composable @Composable
override fun buttonColors() = ButtonDefaults.buttonColors( override fun buttonColors() = ButtonDefaults.buttonColors(
contentColor = contentColor, contentColor = contentColor,
backgroundColor = Color.Unspecified, containerColor = Color.Transparent,
disabledContentColor = LocalColors.current.disabled, disabledContentColor = LocalColors.current.disabled,
disabledBackgroundColor = Color.Unspecified disabledContainerColor = Color.Transparent
) )
} }
@ -47,9 +47,9 @@ interface ButtonType {
@Composable @Composable
override fun buttonColors() = ButtonDefaults.buttonColors( override fun buttonColors() = ButtonDefaults.buttonColors(
contentColor = LocalColors.current.background, contentColor = LocalColors.current.background,
backgroundColor = LocalColors.current.text, containerColor = LocalColors.current.text,
disabledContentColor = LocalColors.current.disabled, disabledContentColor = LocalColors.current.disabled,
disabledBackgroundColor = Color.Unspecified disabledContainerColor = Color.Transparent
) )
} }
@ -59,9 +59,9 @@ interface ButtonType {
@Composable @Composable
override fun buttonColors() = ButtonDefaults.buttonColors( override fun buttonColors() = ButtonDefaults.buttonColors(
contentColor = LocalColors.current.primaryButtonFillText, contentColor = LocalColors.current.primaryButtonFillText,
backgroundColor = LocalColors.current.primaryButtonFill, containerColor = LocalColors.current.primaryButtonFill,
disabledContentColor = LocalColors.current.disabled, disabledContentColor = LocalColors.current.disabled,
disabledBackgroundColor = Color.Unspecified disabledContainerColor = Color.Transparent
) )
} }
@ -73,8 +73,9 @@ interface ButtonType {
@Composable @Composable
override fun buttonColors() = ButtonDefaults.outlinedButtonColors( override fun buttonColors() = ButtonDefaults.outlinedButtonColors(
contentColor = color, contentColor = color,
backgroundColor = Color.Transparent, containerColor = Color.Transparent,
disabledContentColor = LocalColors.current.disabled disabledContentColor = LocalColors.current.disabled,
disabledContainerColor = Color.Transparent
) )
} }
} }

View File

@ -1,7 +1,7 @@
package org.thoughtcrime.securesms.ui.components package org.thoughtcrime.securesms.ui.components
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material.LocalContentColor import androidx.compose.material3.LocalContentColor
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@ -9,7 +9,7 @@ import androidx.compose.ui.unit.dp
@Composable @Composable
fun CircularProgressIndicator(color: Color = LocalContentColor.current) { fun CircularProgressIndicator(color: Color = LocalContentColor.current) {
androidx.compose.material.CircularProgressIndicator( androidx.compose.material3.CircularProgressIndicator(
modifier = Modifier.size(40.dp), modifier = Modifier.size(40.dp),
color = color, color = color,
strokeWidth = 2.dp strokeWidth = 2.dp
@ -18,7 +18,7 @@ fun CircularProgressIndicator(color: Color = LocalContentColor.current) {
@Composable @Composable
fun SmallCircularProgressIndicator(color: Color = LocalContentColor.current) { fun SmallCircularProgressIndicator(color: Color = LocalContentColor.current) {
androidx.compose.material.CircularProgressIndicator( androidx.compose.material3.CircularProgressIndicator(
modifier = Modifier.size(20.dp), modifier = Modifier.size(20.dp),
color = color, color = color,
strokeWidth = 2.dp strokeWidth = 2.dp

View File

@ -22,11 +22,11 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material.Snackbar import androidx.compose.material3.Snackbar
import androidx.compose.material.SnackbarHost import androidx.compose.material3.SnackbarHost
import androidx.compose.material.Text import androidx.compose.material3.SnackbarHostState
import androidx.compose.material.rememberScaffoldState import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@ -56,10 +56,9 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import network.loki.messenger.R import network.loki.messenger.R
import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.base import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalType
import org.thoughtcrime.securesms.ui.xl
import java.util.concurrent.Executors import java.util.concurrent.Executors
private const val TAG = "NewMessageFragment" private const val TAG = "NewMessageFragment"
@ -78,7 +77,6 @@ fun MaybeScanQrCode(
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(LocalColors.current.background)
) { ) {
LocalSoftwareKeyboardController.current?.hide() LocalSoftwareKeyboardController.current?.hide()
@ -94,10 +92,10 @@ fun MaybeScanQrCode(
) { ) {
Text( Text(
stringResource(R.string.activity_link_camera_permission_permanently_denied_configure_in_settings), stringResource(R.string.activity_link_camera_permission_permanently_denied_configure_in_settings),
style = base, style = LocalType.current.base,
textAlign = TextAlign.Center textAlign = TextAlign.Center
) )
Spacer(modifier = Modifier.size(LocalDimensions.current.itemSpacing)) Spacer(modifier = Modifier.size(LocalDimensions.current.spacing))
OutlineButton( OutlineButton(
stringResource(R.string.sessionSettings), stringResource(R.string.sessionSettings),
modifier = Modifier.align(Alignment.CenterHorizontally), modifier = Modifier.align(Alignment.CenterHorizontally),
@ -107,14 +105,14 @@ fun MaybeScanQrCode(
} else { } else {
Column( Column(
modifier = Modifier modifier = Modifier
.background(color = LocalColors.current.backgroundSecondary)
.fillMaxSize() .fillMaxSize()
.padding(LocalDimensions.current.largeMargin), .padding(LocalDimensions.current.xlargeSpacing),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
Text(stringResource(R.string.fragment_scan_qr_code_camera_access_explanation), style = xl, textAlign = TextAlign.Center) Text(stringResource(R.string.fragment_scan_qr_code_camera_access_explanation),
Spacer(modifier = Modifier.height(LocalDimensions.current.itemSpacing)) style = LocalType.current.xl, textAlign = TextAlign.Center)
Spacer(modifier = Modifier.height(LocalDimensions.current.spacing))
PrimaryOutlineButton( PrimaryOutlineButton(
stringResource(R.string.cameraGrantAccess), stringResource(R.string.cameraGrantAccess),
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@ -158,13 +156,12 @@ fun ScanQrCode(errors: Flow<String>, onScan: (String) -> Unit) {
} }
} }
val scaffoldState = rememberScaffoldState() val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
errors.collect { error -> errors.collect { error ->
scaffoldState.snackbarHostState snackbarHostState
.takeIf { it.currentSnackbarData == null } .takeIf { it.currentSnackbarData == null }
?.run { ?.run {
scope.launch { scope.launch {
@ -175,22 +172,21 @@ fun ScanQrCode(errors: Flow<String>, onScan: (String) -> Unit) {
// Don't use debounce() because many QR scans can come through each second, // Don't use debounce() because many QR scans can come through each second,
// and each scan could restart the timer which could mean no scan gets // and each scan could restart the timer which could mean no scan gets
// through until the user stops scanning; quite perplexing. // through until the user stops scanning; quite perplexing.
showSnackbar(message = error) snackbarHostState.showSnackbar(message = error)
} }
} }
} }
} }
Scaffold( Scaffold(
scaffoldState = scaffoldState,
snackbarHost = { snackbarHost = {
SnackbarHost( SnackbarHost(
hostState = scaffoldState.snackbarHostState, hostState = snackbarHostState,
modifier = Modifier.padding(LocalDimensions.current.smallItemSpacing) modifier = Modifier.padding(LocalDimensions.current.smallSpacing)
) { data -> ) { data ->
Snackbar( Snackbar(
snackbarData = data, snackbarData = data,
modifier = Modifier.padding(LocalDimensions.current.smallItemSpacing) modifier = Modifier.padding(LocalDimensions.current.smallSpacing)
) )
} }
} }
@ -204,7 +200,7 @@ fun ScanQrCode(errors: Flow<String>, onScan: (String) -> Unit) {
Box( Box(
Modifier Modifier
.aspectRatio(1f) .aspectRatio(1f)
.padding(LocalDimensions.current.itemSpacing) .padding(LocalDimensions.current.spacing)
.clip(shape = RoundedCornerShape(26.dp)) .clip(shape = RoundedCornerShape(26.dp))
.background(Color(0x33ffffff)) .background(Color(0x33ffffff))
.align(Alignment.Center) .align(Alignment.Center)

View File

@ -10,8 +10,8 @@ import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material.Card import androidx.compose.material3.Icon
import androidx.compose.material.Icon import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -32,15 +32,15 @@ import androidx.compose.ui.unit.dp
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.util.QRCodeUtilities import org.thoughtcrime.securesms.util.QRCodeUtilities
@Composable @Composable
fun QrImage( fun QrImage(
string: String?, string: String?,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
contentPadding: Dp = LocalDimensions.current.smallItemSpacing, contentPadding: Dp = LocalDimensions.current.smallSpacing,
icon: Int = R.drawable.session_shield icon: Int = R.drawable.session_shield
) { ) {
var bitmap: Bitmap? by remember { var bitmap: Bitmap? by remember {
@ -56,10 +56,9 @@ fun QrImage(
} }
} }
Card( Box(
backgroundColor = LocalColors.current.qrCodeBackground, modifier = modifier.background(color = LocalColors.current.qrCodeBackground,
elevation = 0.dp, shape = MaterialTheme.shapes.small)
modifier = modifier
) { Content(bitmap, icon, Modifier.padding(contentPadding), backgroundColor = LocalColors.current.qrCodeBackground) } ) { Content(bitmap, icon, Modifier.padding(contentPadding), backgroundColor = LocalColors.current.qrCodeBackground) }
} }

View File

@ -1,40 +1,58 @@
package org.thoughtcrime.securesms.ui.components package org.thoughtcrime.securesms.ui.components
import android.content.ContentProvider
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.scaleIn import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut import androidx.compose.animation.scaleOut
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.TextButton import androidx.compose.material3.LocalContentColor
import androidx.compose.material.ripple.rememberRipple import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.Role
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import org.thoughtcrime.securesms.ui.LocalDimensions import network.loki.messenger.libsession_util.util.ExpiryMode
import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.conversation.disappearingmessages.ExpiryType
import org.thoughtcrime.securesms.ui.color.transparentButtonColors import org.thoughtcrime.securesms.ui.GetString
import org.thoughtcrime.securesms.ui.RadioOption
import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.theme.LocalType
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.theme.sessionTypography
import org.thoughtcrime.securesms.ui.theme.textEnabled
import org.thoughtcrime.securesms.ui.theme.transparentButtonColors
import kotlin.time.Duration.Companion.days
@Composable @Composable
fun RadioButton( fun RadioButton(
onClick: () -> Unit = {}, onClick: () -> Unit = {},
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
checked: Boolean = false, selected: Boolean = false,
enabled: Boolean = true,
contentPadding: PaddingValues = PaddingValues(), contentPadding: PaddingValues = PaddingValues(),
content: @Composable RowScope.() -> Unit = {} content: @Composable RowScope.() -> Unit = {}
) { ) {
@ -42,20 +60,22 @@ fun RadioButton(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.selectable( .selectable(
selected = checked, selected = selected,
enabled = true, enabled = true,
role = Role.RadioButton, role = Role.RadioButton,
onClick = onClick onClick = onClick
), ),
enabled = enabled,
colors = transparentButtonColors(), colors = transparentButtonColors(),
onClick = onClick, onClick = onClick,
shape = RectangleShape, shape = RectangleShape,
contentPadding = contentPadding contentPadding = contentPadding
) { ) {
content() content()
Spacer(modifier = Modifier.width(20.dp)) Spacer(modifier = Modifier.width(20.dp))
RadioButtonIndicator( RadioButtonIndicator(
checked = checked, selected = selected && enabled, // disabled radio shouldn't be selected
modifier = Modifier modifier = Modifier
.size(22.dp) .size(22.dp)
.align(Alignment.CenterVertically) .align(Alignment.CenterVertically)
@ -65,12 +85,12 @@ fun RadioButton(
@Composable @Composable
private fun RadioButtonIndicator( private fun RadioButtonIndicator(
checked: Boolean, selected: Boolean,
modifier: Modifier modifier: Modifier
) { ) {
Box(modifier = modifier) { Box(modifier = modifier) {
AnimatedVisibility( AnimatedVisibility(
checked, selected,
modifier = Modifier modifier = Modifier
.padding(2.5.dp) .padding(2.5.dp)
.clip(CircleShape), .clip(CircleShape),
@ -91,9 +111,93 @@ private fun RadioButtonIndicator(
.aspectRatio(1f) .aspectRatio(1f)
.border( .border(
width = LocalDimensions.current.borderStroke, width = LocalDimensions.current.borderStroke,
color = LocalColors.current.text, color = LocalContentColor.current,
shape = CircleShape shape = CircleShape
) )
) {} ) {}
} }
} }
@Composable
fun <T> TitledRadioButton(
modifier: Modifier = Modifier,
option: RadioOption<T>,
onClick: () -> Unit
) {
RadioButton(
modifier = modifier.heightIn(min = 60.dp)
.contentDescription(option.contentDescription),
onClick = onClick,
selected = option.selected,
enabled = option.enabled,
contentPadding = PaddingValues(horizontal = LocalDimensions.current.spacing),
content = {
Column(
modifier = Modifier
.weight(1f)
.align(Alignment.CenterVertically)
) {
Column {
Text(
text = option.title(),
style = LocalType.current.large
)
option.subtitle?.let {
Text(
text = it(),
style = LocalType.current.extraSmall
)
}
}
}
}
)
}
@Preview
@Composable
fun PreviewTextRadioButton() {
PreviewTheme {
TitledRadioButton(
option = RadioOption<ExpiryMode>(
value = ExpiryType.AFTER_SEND.mode(7.days),
title = GetString(7.days),
subtitle = GetString("This is a subtitle"),
enabled = true,
selected = true
)
) {}
}
}
@Preview
@Composable
fun PreviewDisabledTextRadioButton() {
PreviewTheme {
TitledRadioButton(
option = RadioOption<ExpiryMode>(
value = ExpiryType.AFTER_SEND.mode(7.days),
title = GetString(7.days),
subtitle = GetString("This is a subtitle"),
enabled = false,
selected = true
)
) {}
}
}
@Preview
@Composable
fun PreviewDeselectedTextRadioButton() {
PreviewTheme {
TitledRadioButton(
option = RadioOption<ExpiryMode>(
value = ExpiryType.AFTER_SEND.mode(7.days),
title = GetString(7.days),
subtitle = GetString("This is a subtitle"),
enabled = true,
selected = false
)
) {}
}
}

View File

@ -1,15 +1,15 @@
package org.thoughtcrime.securesms.ui.components package org.thoughtcrime.securesms.ui.components
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.Tab import androidx.compose.material3.HorizontalDivider
import androidx.compose.material.TabRow import androidx.compose.material3.Tab
import androidx.compose.material.TabRowDefaults import androidx.compose.material3.TabRow
import androidx.compose.material.TabRowDefaults.tabIndicatorOffset import androidx.compose.material3.TabRowDefaults
import androidx.compose.material.Text import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -19,13 +19,12 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.theme.LocalType
import org.thoughtcrime.securesms.ui.color.Colors import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
import org.thoughtcrime.securesms.ui.color.divider import org.thoughtcrime.securesms.ui.theme.ThemeColors
import org.thoughtcrime.securesms.ui.h8
private val TITLES = listOf(R.string.sessionRecoveryPassword, R.string.qrScan) private val TITLES = listOf(R.string.sessionRecoveryPassword, R.string.qrScan)
@ -33,32 +32,30 @@ private val TITLES = listOf(R.string.sessionRecoveryPassword, R.string.qrScan)
@Composable @Composable
fun SessionTabRow(pagerState: PagerState, titles: List<Int>) { fun SessionTabRow(pagerState: PagerState, titles: List<Int>) {
TabRow( TabRow(
backgroundColor = Color.Unspecified, containerColor = Color.Unspecified,
selectedTabIndex = pagerState.currentPage, selectedTabIndex = pagerState.currentPage,
contentColor = LocalColors.current.text, contentColor = LocalColors.current.text,
indicator = { tabPositions -> indicator = { tabPositions ->
TabRowDefaults.Indicator( TabRowDefaults.SecondaryIndicator(
Modifier.tabIndicatorOffset(tabPositions[pagerState.currentPage]), Modifier.tabIndicatorOffset(tabPositions[pagerState.currentPage]),
color = LocalColors.current.primary, color = LocalColors.current.primary,
height = LocalDimensions.current.indicatorHeight height = LocalDimensions.current.indicatorHeight
) )
}, },
divider = { TabRowDefaults.Divider(color = LocalColors.current.divider) }, divider = { HorizontalDivider(color = LocalColors.current.borders) }
modifier = Modifier
.height(48.dp)
.background(color = Color.Unspecified)
) { ) {
val animationScope = rememberCoroutineScope() val animationScope = rememberCoroutineScope()
titles.forEachIndexed { i, it -> titles.forEachIndexed { i, it ->
Tab( Tab(
i == pagerState.currentPage, modifier = Modifier.heightIn(min = 48.dp),
selected = i == pagerState.currentPage,
onClick = { animationScope.launch { pagerState.animateScrollToPage(i) } }, onClick = { animationScope.launch { pagerState.animateScrollToPage(i) } },
selectedContentColor = LocalColors.current.text, selectedContentColor = LocalColors.current.text,
unselectedContentColor = LocalColors.current.text, unselectedContentColor = LocalColors.current.text,
) { ) {
Text( Text(
stringResource(id = it), text = stringResource(id = it),
style = h8 style = LocalType.current.h8
) )
} }
} }
@ -69,7 +66,7 @@ fun SessionTabRow(pagerState: PagerState, titles: List<Int>) {
@androidx.compose.ui.tooling.preview.Preview @androidx.compose.ui.tooling.preview.Preview
@Composable @Composable
fun PreviewSessionTabRow( fun PreviewSessionTabRow(
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
) { ) {
PreviewTheme(colors) { PreviewTheme(colors) {
val pagerState = rememberPagerState { TITLES.size } val pagerState = rememberPagerState { TITLES.size }

View File

@ -17,9 +17,9 @@ import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.appendInlineContent import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.material.Icon import androidx.compose.material3.Icon
import androidx.compose.material.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -37,15 +37,15 @@ import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.ui.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.base import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.baseBold import org.thoughtcrime.securesms.ui.theme.borders
import org.thoughtcrime.securesms.ui.color.LocalColors import org.thoughtcrime.securesms.ui.theme.text
import org.thoughtcrime.securesms.ui.color.borders import org.thoughtcrime.securesms.ui.theme.textSecondary
import org.thoughtcrime.securesms.ui.color.text
import org.thoughtcrime.securesms.ui.color.textSecondary
import org.thoughtcrime.securesms.ui.contentDescription import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.theme.LocalType
import org.thoughtcrime.securesms.ui.theme.bold
@Preview @Preview
@Composable @Composable
@ -85,7 +85,7 @@ fun SessionOutlinedTextField(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
contentDescription: String? = null, contentDescription: String? = null,
onChange: (String) -> Unit = {}, onChange: (String) -> Unit = {},
textStyle: TextStyle = base, textStyle: TextStyle = LocalType.current.base,
placeholder: String = "", placeholder: String = "",
onContinue: () -> Unit = {}, onContinue: () -> Unit = {},
error: String? = null, error: String? = null,
@ -106,7 +106,7 @@ fun SessionOutlinedTextField(
if (text.isEmpty()) { if (text.isEmpty()) {
Text( Text(
text = placeholder, text = placeholder,
style = base, style = LocalType.current.base,
color = LocalColors.current.textSecondary(isTextErrorColor), color = LocalColors.current.textSecondary(isTextErrorColor),
modifier = Modifier.wrapContentSize() modifier = Modifier.wrapContentSize()
.align(Alignment.CenterStart) .align(Alignment.CenterStart)
@ -130,13 +130,13 @@ fun SessionOutlinedTextField(
) )
} }
error?.let { error?.let {
Spacer(modifier = Modifier.height(LocalDimensions.current.xsItemSpacing)) Spacer(modifier = Modifier.height(LocalDimensions.current.xsSpacing))
Text( Text(
it, it,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
.contentDescription(R.string.AccessibilityId_error_message), .contentDescription(R.string.AccessibilityId_error_message),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
style = baseBold, style = LocalType.current.base.bold(),
color = LocalColors.current.danger color = LocalColors.current.danger
) )
} }
@ -148,7 +148,7 @@ fun AnnotatedTextWithIcon(
text: String, text: String,
@DrawableRes iconRes: Int, @DrawableRes iconRes: Int,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
style: TextStyle = base, style: TextStyle = LocalType.current.base,
color: Color = Color.Unspecified, color: Color = Color.Unspecified,
iconSize: TextUnit = 12.sp iconSize: TextUnit = 12.sp
) { ) {

View File

@ -1,4 +1,4 @@
package org.thoughtcrime.securesms.ui.color package org.thoughtcrime.securesms.ui.theme
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color

View File

@ -0,0 +1,24 @@
package org.thoughtcrime.securesms.ui.theme
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
val LocalDimensions = staticCompositionLocalOf { Dimensions() }
data class Dimensions(
val xxxsSpacing: Dp = 4.dp,
val xxsSpacing: Dp = 8.dp,
val xsSpacing: Dp = 12.dp,
val smallSpacing: Dp = 16.dp,
val spacing: Dp = 24.dp,
val mediumSpacing: Dp = 36.dp,
val xlargeSpacing: Dp = 64.dp,
val dividerIndent: Dp = 60.dp,
val appBarHeight: Dp = 64.dp,
val minLargeItemButtonHeight: Dp = 60.dp,
val indicatorHeight: Dp = 4.dp,
val borderStroke: Dp = 1.dp
)

View File

@ -0,0 +1,145 @@
package org.thoughtcrime.securesms.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.runtime.Composable
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
fun TextStyle.bold() = TextStyle.Default.copy(
fontWeight = FontWeight.Bold
)
fun TextStyle.monospace() = TextStyle.Default.copy(
fontFamily = FontFamily.Monospace
)
val sessionTypography = SessionTypography()
data class SessionTypography(
// Body
val xl: TextStyle = TextStyle(
fontSize = 18.sp,
lineHeight = 21.6.sp,
fontWeight = FontWeight.Normal
),
val large: TextStyle = TextStyle(
fontSize = 16.sp,
lineHeight = 19.2.sp,
fontWeight = FontWeight.Normal
),
val base: TextStyle = TextStyle(
fontSize = 14.sp,
lineHeight = 16.8.sp,
fontWeight = FontWeight.Normal
),
val small: TextStyle = TextStyle(
fontSize = 12.sp,
lineHeight = 14.4.sp,
fontWeight = FontWeight.Normal
),
val extraSmall: TextStyle = TextStyle(
fontSize = 11.sp,
lineHeight = 13.2.sp,
fontWeight = FontWeight.Normal
),
val fine: TextStyle = TextStyle(
fontSize = 9.sp,
lineHeight = 10.8.sp,
fontWeight = FontWeight.Normal
),
// Headings
val h1: TextStyle = TextStyle(
fontSize = 36.sp,
lineHeight = 43.2.sp,
fontWeight = FontWeight.Bold
),
val h2: TextStyle = TextStyle(
fontSize = 32.sp,
lineHeight = 38.4.sp,
fontWeight = FontWeight.Bold
),
val h3: TextStyle = TextStyle(
fontSize = 29.sp,
lineHeight = 34.8.sp,
fontWeight = FontWeight.Bold
),
val h4: TextStyle = TextStyle(
fontSize = 26.sp,
lineHeight = 31.2.sp,
fontWeight = FontWeight.Bold
),
val h5: TextStyle = TextStyle(
fontSize = 23.sp,
lineHeight = 27.6.sp,
fontWeight = FontWeight.Bold
),
val h6: TextStyle = TextStyle(
fontSize = 20.sp,
lineHeight = 24.sp,
fontWeight = FontWeight.Bold
),
val h7: TextStyle = TextStyle(
fontSize = 18.sp,
lineHeight = 21.6.sp,
fontWeight = FontWeight.Bold
),
val h8: TextStyle = TextStyle(
fontSize = 16.sp,
lineHeight = 19.2.sp,
fontWeight = FontWeight.Bold
),
val h9: TextStyle = TextStyle(
fontSize = 14.sp,
lineHeight = 16.8.sp,
fontWeight = FontWeight.Bold
)
) {
// An opinionated override of Material's defaults
@Composable
fun asMaterialTypography() = Typography(
// Display
displayLarge = h1,
displayMedium = h1,
displaySmall = h1,
// Headline
headlineLarge = h2,
headlineMedium = h3,
headlineSmall = h4,
// Title
titleLarge = h5,
titleMedium = h6,
titleSmall = h7,
// Body
bodyLarge = large,
bodyMedium = base,
bodySmall = small,
// Label
labelLarge = extraSmall,
labelMedium = fine,
labelSmall = fine
)
}

View File

@ -0,0 +1,10 @@
package org.thoughtcrime.securesms.ui.theme
/**
* This class holds two instances of [ThemeColors], [light] representing the [ThemeColors] to use when the system is in a
* light theme, and [dark] representing the [ThemeColors] to use when the system is in a dark theme.
*/
data class ThemeColorSet(
val light: ThemeColors,
val dark: ThemeColors
)

View File

@ -0,0 +1,206 @@
package org.thoughtcrime.securesms.ui.theme
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.text.selection.TextSelectionColors
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
interface ThemeColors {
// properties to override for each theme
val isLight: Boolean
val primary: Color
val danger: Color
val disabled: Color
val background: Color
val backgroundSecondary: Color
val text: Color
val textSecondary: Color
val borders: Color
val textBubbleSent: Color
val backgroundBubbleReceived: Color
val textBubbleReceived: Color
val qrCodeContent: Color
val qrCodeBackground: Color
val primaryButtonFill: Color
val primaryButtonFillText: Color
}
// extra functions and properties that work for all themes
val ThemeColors.textSelectionColors
get() = TextSelectionColors(
handleColor = primary,
backgroundColor = primary.copy(alpha = 0.5f)
)
fun ThemeColors.text(isError: Boolean): Color = if (isError) danger else text
fun ThemeColors.textSecondary(isError: Boolean): Color = if (isError) danger else textSecondary
fun ThemeColors.textEnabled(enabled: Boolean) = if (enabled) text else disabled
fun ThemeColors.borders(isError: Boolean): Color = if (isError) danger else borders
fun ThemeColors.toMaterialColors() = if (isLight) {
lightColorScheme(
primary = background,
secondary = backgroundSecondary,
tertiary = backgroundSecondary,
onPrimary = text,
onSecondary = text,
onTertiary = text,
background = background,
surface = background,
surfaceVariant = background,
onBackground = text,
onSurface = text,
scrim = blackAlpha40,
outline = text,
outlineVariant = text
)
} else {
darkColorScheme(
primary = background,
secondary = backgroundSecondary,
tertiary = backgroundSecondary,
onPrimary = text,
onSecondary = text,
onTertiary = text,
background = background,
surface = background,
surfaceVariant = background,
onBackground = text,
onSurface = text,
scrim = blackAlpha40,
outline = text,
outlineVariant = text
)
}
@Composable
fun transparentButtonColors() = ButtonDefaults.buttonColors(
containerColor = Color.Transparent,
disabledContainerColor = Color.Transparent,
disabledContentColor = LocalColors.current.disabled
)
@Composable
fun dangerButtonColors() = ButtonDefaults.buttonColors(
containerColor = Color.Transparent,
contentColor = LocalColors.current.danger
)
// Our themes
data class ClassicDark(override val primary: Color = primaryGreen) : ThemeColors {
override val isLight = false
override val danger = dangerDark
override val disabled = disabledDark
override val background = classicDark0
override val backgroundSecondary = classicDark1
override val text = classicDark6
override val textSecondary = classicDark5
override val borders = classicDark3
override val textBubbleSent = Color.Black
override val backgroundBubbleReceived = classicDark2
override val textBubbleReceived = Color.White
override val qrCodeContent = background
override val qrCodeBackground = text
override val primaryButtonFill = primary
override val primaryButtonFillText = Color.Black
}
data class ClassicLight(override val primary: Color = primaryGreen) : ThemeColors {
override val isLight = true
override val danger = dangerLight
override val disabled = disabledLight
override val background = classicLight6
override val backgroundSecondary = classicLight5
override val text = classicLight0
override val textSecondary = classicLight1
override val borders = classicLight3
override val textBubbleSent = text
override val backgroundBubbleReceived = classicLight4
override val textBubbleReceived = classicLight4
override val qrCodeContent = text
override val qrCodeBackground = backgroundSecondary
override val primaryButtonFill = text
override val primaryButtonFillText = Color.White
}
data class OceanDark(override val primary: Color = primaryBlue) : ThemeColors {
override val isLight = false
override val danger = dangerDark
override val disabled = disabledDark
override val background = oceanDark2
override val backgroundSecondary = oceanDark1
override val text = oceanDark7
override val textSecondary = oceanDark5
override val borders = oceanDark4
override val textBubbleSent = Color.Black
override val backgroundBubbleReceived = oceanDark4
override val textBubbleReceived = oceanDark4
override val qrCodeContent = background
override val qrCodeBackground = text
override val primaryButtonFill = primary
override val primaryButtonFillText = Color.Black
}
data class OceanLight(override val primary: Color = primaryBlue) : ThemeColors {
override val isLight = true
override val danger = dangerLight
override val disabled = disabledLight
override val background = oceanLight7
override val backgroundSecondary = oceanLight6
override val text = oceanLight1
override val textSecondary = oceanLight2
override val borders = oceanLight3
override val textBubbleSent = text
override val backgroundBubbleReceived = oceanLight4
override val textBubbleReceived = oceanLight1
override val qrCodeContent = text
override val qrCodeBackground = backgroundSecondary
override val primaryButtonFill = text
override val primaryButtonFillText = Color.White
}
@Preview
@Composable
fun PreviewThemeColors(
@PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
) {
PreviewTheme(colors) { ThemeColors() }
}
@Composable
private fun ThemeColors() {
Column {
Box(Modifier.background(LocalColors.current.primary)) {
Text("primary", style = LocalType.current.base)
}
Box(Modifier.background(LocalColors.current.background)) {
Text("background", style = LocalType.current.base)
}
Box(Modifier.background(LocalColors.current.backgroundSecondary)) {
Text("backgroundSecondary", style = LocalType.current.base)
}
Box(Modifier.background(LocalColors.current.text)) {
Text("text", style = LocalType.current.base)
}
Box(Modifier.background(LocalColors.current.textSecondary)) {
Text("textSecondary", style = LocalType.current.base)
}
Box(Modifier.background(LocalColors.current.danger)) {
Text("danger", style = LocalType.current.base)
}
Box(Modifier.background(LocalColors.current.borders)) {
Text("border", style = LocalType.current.base)
}
}
}

View File

@ -0,0 +1,65 @@
package org.thoughtcrime.securesms.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.TextSecurePreferences.Companion.BLUE_ACCENT
import org.session.libsession.utilities.TextSecurePreferences.Companion.ORANGE_ACCENT
import org.session.libsession.utilities.TextSecurePreferences.Companion.PINK_ACCENT
import org.session.libsession.utilities.TextSecurePreferences.Companion.PURPLE_ACCENT
import org.session.libsession.utilities.TextSecurePreferences.Companion.RED_ACCENT
import org.session.libsession.utilities.TextSecurePreferences.Companion.YELLOW_ACCENT
/**
* Returns the compose theme based on saved preferences
* Some behaviour is hardcoded to cater for legacy usage of people with themes already set
* But future themes will be picked and set directly from the "Appearance" screen
*/
@Composable
fun TextSecurePreferences.getComposeTheme(): ThemeColors {
val selectedTheme = getThemeStyle()
// get the chosen primary color from the preferences
val selectedPrimary = primaryColor()
// create a theme set with the appropriate primary
val colorSet = when(selectedTheme){
TextSecurePreferences.OCEAN_DARK,
TextSecurePreferences.OCEAN_LIGHT -> ThemeColorSet(
light = OceanLight(selectedPrimary),
dark = OceanDark(selectedPrimary)
)
else -> ThemeColorSet(
light = ClassicLight(selectedPrimary),
dark = ClassicDark(selectedPrimary)
)
}
// deliver the right set from the light/dark mode chosen
val theme = when{
getFollowSystemSettings() -> if(isSystemInDarkTheme()) colorSet.dark else colorSet.light
selectedTheme == TextSecurePreferences.CLASSIC_LIGHT ||
selectedTheme == TextSecurePreferences.OCEAN_LIGHT -> colorSet.light
else -> colorSet.dark
}
return theme
}
fun TextSecurePreferences.primaryColor(): Color = when(getSelectedAccentColor()) {
BLUE_ACCENT -> primaryBlue
PURPLE_ACCENT -> primaryPurple
PINK_ACCENT -> primaryPink
RED_ACCENT -> primaryRed
ORANGE_ACCENT -> primaryOrange
YELLOW_ACCENT -> primaryYellow
else -> primaryGreen
}

View File

@ -1,28 +1,27 @@
package org.thoughtcrime.securesms.ui package org.thoughtcrime.securesms.ui.theme
import android.content.Context
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.selection.LocalTextSelectionColors import androidx.compose.foundation.text.selection.LocalTextSelectionColors
import androidx.compose.material.LocalContentColor import androidx.compose.material3.LocalContentColor
import androidx.compose.material.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material.Shapes import androidx.compose.material3.Shapes
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.thoughtcrime.securesms.ui.color.ClassicDark import org.session.libsession.utilities.prefs
import org.thoughtcrime.securesms.ui.color.ClassicLight
import org.thoughtcrime.securesms.ui.color.Colors // Globally accessible composition local objects
import org.thoughtcrime.securesms.ui.color.LocalColors val LocalColors = compositionLocalOf <ThemeColors> { ClassicDark() }
import org.thoughtcrime.securesms.ui.color.OceanDark val LocalType = compositionLocalOf { sessionTypography }
import org.thoughtcrime.securesms.ui.color.OceanLight
import org.thoughtcrime.securesms.ui.color.colors var selectedTheme: ThemeColors? = null
import org.thoughtcrime.securesms.ui.color.textSelectionColors
/** /**
* Apply a Material2 compose theme based on user selections in SharedPreferences. * Apply a Material2 compose theme based on user selections in SharedPreferences.
@ -31,24 +30,32 @@ import org.thoughtcrime.securesms.ui.color.textSelectionColors
fun SessionMaterialTheme( fun SessionMaterialTheme(
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
SessionMaterialTheme(LocalContext.current.colors()) { content() } // set the theme data if it hasn't been done yet
if(selectedTheme == null) {
// Some values can be set from the preferences, and if not should fallback to a default value
val context = LocalContext.current
selectedTheme = context.prefs.getComposeTheme()
}
SessionMaterialTheme(colors = selectedTheme ?: ClassicDark()) { content() }
} }
/** /**
* Apply a given [Colors], and our typography and shapes as a Material 2 Compose Theme. * Apply a given [ThemeColors], and our typography and shapes as a Material 2 Compose Theme.
**/ **/
@Composable @Composable
fun SessionMaterialTheme( fun SessionMaterialTheme(
colors: Colors, colors: ThemeColors,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
MaterialTheme( MaterialTheme(
colors = colors.toMaterialColors(), colorScheme = colors.toMaterialColors(),
typography = sessionTypography, typography = sessionTypography.asMaterialTypography(),
shapes = sessionShapes, shapes = sessionShapes,
) { ) {
CompositionLocalProvider( CompositionLocalProvider(
LocalColors provides colors, LocalColors provides colors,
LocalType provides sessionTypography,
LocalContentColor provides colors.text, LocalContentColor provides colors.text,
LocalTextSelectionColors provides colors.textSelectionColors, LocalTextSelectionColors provides colors.textSelectionColors,
) { ) {
@ -57,24 +64,6 @@ fun SessionMaterialTheme(
} }
} }
private fun Colors.toMaterialColors() = androidx.compose.material.Colors(
primary = background,
primaryVariant = backgroundSecondary,
secondary = background,
secondaryVariant = background,
background = background,
surface = background,
error = danger,
onPrimary = text,
onSecondary = text,
onBackground = text,
onSurface = text,
onError = text,
isLight = isLight
)
@Composable private fun Context.colors() = TextSecurePreferences(this).colors()
val pillShape = RoundedCornerShape(percent = 50) val pillShape = RoundedCornerShape(percent = 50)
val buttonShape = pillShape val buttonShape = pillShape
@ -88,7 +77,7 @@ val sessionShapes = Shapes(
*/ */
@Composable @Composable
fun PreviewTheme( fun PreviewTheme(
colors: Colors = LocalColors.current, colors: ThemeColors = LocalColors.current,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
SessionMaterialTheme(colors) { SessionMaterialTheme(colors) {
@ -98,6 +87,7 @@ fun PreviewTheme(
} }
} }
class SessionColorsParameterProvider : PreviewParameterProvider<Colors> { // used for previews
class SessionColorsParameterProvider : PreviewParameterProvider<ThemeColors> {
override val values = sequenceOf(ClassicDark(), ClassicLight(), OceanDark(), OceanLight()) override val values = sequenceOf(ClassicDark(), ClassicLight(), OceanDark(), OceanLight())
} }

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="?android:textColorTertiary"/> <item android:state_enabled="false" android:color="?android:textColorTertiary"/>
<item android:color="@color/destructive"/> <item android:color="?danger"/>
</selector> </selector>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/destructive" android:state_selected="true"/> <item android:color="?danger" android:state_selected="true"/>
<item android:color="@color/call_action_button" android:state_selected="false"/> <item android:color="@color/call_action_button" android:state_selected="false"/>
</selector> </selector>

View File

@ -3,9 +3,9 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">
<solid android:color="@color/destructive" /> <solid android:color="?danger" />
<corners android:radius="@dimen/dialog_button_corner_radius" /> <corners android:radius="@dimen/dialog_button_corner_radius" />
<stroke android:width="@dimen/border_thickness" android:color="@color/destructive" /> <stroke android:width="@dimen/border_thickness" android:color="?danger" />
</shape> </shape>

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android" <ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/button_destructive"> android:color="@color/button_danger">
<item> <item>
<shape android:shape="rectangle"> <shape android:shape="rectangle">
<solid android:color="?colorPrimary"/> <solid android:color="?colorPrimary"/>
<corners android:radius="@dimen/medium_button_corner_radius" /> <corners android:radius="@dimen/medium_button_corner_radius" />
<stroke <stroke
android:color="@color/button_destructive" android:color="@color/button_danger"
android:width="@dimen/border_thickness" /> android:width="@dimen/border_thickness" />
</shape> </shape>
</item> </item>

View File

@ -1,11 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="25dp"
android:height="26dp"
android:viewportWidth="25"
android:viewportHeight="26">
<path
android:pathData="M19.907,7.674H19.907H4.54H4.54C4.317,7.674 4.095,7.719 3.888,7.806L3.888,7.806C3.681,7.893 3.491,8.023 3.334,8.189C3.176,8.355 3.054,8.554 2.978,8.775L3.922,9.097L2.978,8.775C2.903,8.996 2.877,9.231 2.904,9.465L2.904,9.465L2.904,9.469L4.555,23.412C4.555,23.413 4.555,23.413 4.555,23.414C4.603,23.823 4.807,24.189 5.111,24.447C5.415,24.705 5.798,24.84 6.187,24.84H6.188H18.26H18.26C18.649,24.84 19.032,24.705 19.336,24.447C19.64,24.189 19.844,23.823 19.892,23.414C19.892,23.413 19.892,23.413 19.892,23.412L21.543,9.469L21.544,9.465C21.57,9.231 21.544,8.996 21.469,8.775L21.469,8.775C21.393,8.554 21.271,8.355 21.113,8.189C20.956,8.023 20.766,7.893 20.559,7.806L20.17,8.728L20.559,7.806C20.352,7.719 20.13,7.674 19.907,7.674ZM21.412,1.84H3.031C2.045,1.84 1.149,2.609 1.149,3.674V5.828C1.149,6.893 2.045,7.662 3.031,7.662H21.412C22.398,7.662 23.294,6.893 23.294,5.828V3.674C23.294,2.609 22.398,1.84 21.412,1.84Z"
android:strokeWidth="2"
android:fillColor="#FF3A3A"
android:strokeColor="?backgroundSecondary"/>
</vector>

View File

@ -3,7 +3,7 @@
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24" android:viewportHeight="24"
android:tint="@color/destructive"> android:tint="?danger">
<path <path
android:fillColor="@android:color/white" android:fillColor="@android:color/white"
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/> android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="rectangle" xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?conversation_menu_background_color" />
<corners android:radius="12dp" />
</shape>

View File

@ -39,7 +39,7 @@
/> />
<TextView <TextView
style="@style/Widget.Session.Button.Common.DestructiveOutline" style="@style/Widget.Session.Button.Common.DangerOutline"
android:paddingHorizontal="@dimen/large_spacing" android:paddingHorizontal="@dimen/large_spacing"
android:paddingVertical="@dimen/small_spacing" android:paddingVertical="@dimen/small_spacing"
android:text="@string/ConversationActivity_unblock" android:text="@string/ConversationActivity_unblock"

View File

@ -228,7 +228,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/toolbar" app:layout_constraintTop_toBottomOf="@+id/toolbar"
android:background="@color/destructive" android:background="?danger"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible"> tools:visibility="visible">
@ -300,7 +300,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:contentDescription="@string/AccessibilityId_block_message_request_button" android:contentDescription="@string/AccessibilityId_block_message_request_button"
android:textColor="@color/destructive" android:textColor="?danger"
android:paddingHorizontal="@dimen/massive_spacing" android:paddingHorizontal="@dimen/massive_spacing"
android:paddingVertical="@dimen/small_spacing" android:paddingVertical="@dimen/small_spacing"
android:textSize="@dimen/text_size" android:textSize="@dimen/text_size"
@ -334,7 +334,7 @@
<Button <Button
android:id="@+id/declineMessageRequestButton" android:id="@+id/declineMessageRequestButton"
style="@style/Widget.Session.Button.Common.DestructiveOutline" style="@style/Widget.Session.Button.Common.DangerOutline"
android:contentDescription="@string/AccessibilityId_decline_message_request_button" android:contentDescription="@string/AccessibilityId_decline_message_request_button"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="@dimen/medium_button_height" android:layout_height="@dimen/medium_button_height"

View File

@ -51,7 +51,7 @@
<Button <Button
android:contentDescription="@string/AccessibilityId_clear_all_message_requests" android:contentDescription="@string/AccessibilityId_clear_all_message_requests"
android:id="@+id/clearAllMessageRequestsButton" android:id="@+id/clearAllMessageRequestsButton"
style="@style/Widget.Session.Button.Common.DestructiveOutline" style="@style/Widget.Session.Button.Common.DangerOutline"
android:layout_width="196dp" android:layout_width="196dp"
android:layout_height="@dimen/medium_button_height" android:layout_height="@dimen/medium_button_height"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"

View File

@ -175,7 +175,7 @@
android:src="@drawable/ic_baseline_call_end_24" android:src="@drawable/ic_baseline_call_end_24"
android:padding="@dimen/medium_spacing" android:padding="@dimen/medium_spacing"
android:foregroundTint="@color/call_action_foreground" android:foregroundTint="@color/call_action_foreground"
android:backgroundTint="@color/destructive" android:backgroundTint="?danger"
android:layout_width="@dimen/large_button_height" android:layout_width="@dimen/large_button_height"
android:layout_height="@dimen/large_button_height" android:layout_height="@dimen/large_button_height"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
@ -265,7 +265,7 @@
android:src="@drawable/ic_baseline_call_end_24" android:src="@drawable/ic_baseline_call_end_24"
android:padding="@dimen/medium_spacing" android:padding="@dimen/medium_spacing"
android:foregroundTint="@color/call_action_foreground" android:foregroundTint="@color/call_action_foreground"
android:backgroundTint="@color/destructive" android:backgroundTint="?danger"
android:layout_width="@dimen/large_button_height" android:layout_width="@dimen/large_button_height"
android:layout_height="@dimen/large_button_height" android:layout_height="@dimen/large_button_height"
android:layout_marginBottom="@dimen/very_large_spacing" android:layout_marginBottom="@dimen/very_large_spacing"

View File

@ -9,7 +9,7 @@
android:layout_marginHorizontal="@dimen/large_spacing" android:layout_marginHorizontal="@dimen/large_spacing"
android:layout_marginVertical="@dimen/medium_spacing" android:layout_marginVertical="@dimen/medium_spacing"
android:text="@string/blocked_contacts_title" android:text="@string/blocked_contacts_title"
android:textColor="@color/destructive" android:textColor="?danger"
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold" android:textStyle="bold"
/> />

View File

@ -44,7 +44,7 @@
android:orientation="horizontal"> android:orientation="horizontal">
<Button <Button
style="@style/Widget.Session.Button.Dialog.DestructiveText" style="@style/Widget.Session.Button.Dialog.DangerText"
android:id="@+id/clearAllDataButton" android:id="@+id/clearAllDataButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="@dimen/dialog_button_height" android:layout_height="@dimen/dialog_button_height"

View File

@ -43,7 +43,7 @@
android:text="@string/cancel" /> android:text="@string/cancel" />
<Button <Button
style="@style/Widget.Session.Button.Dialog.DestructiveText" style="@style/Widget.Session.Button.Dialog.DangerText"
android:id="@+id/sendSeedButton" android:id="@+id/sendSeedButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="@dimen/dialog_button_height" android:layout_height="@dimen/dialog_button_height"

View File

@ -62,7 +62,7 @@
<TextView <TextView
style="@style/Widget.Session.Button.Common.ProminentFilled" style="@style/Widget.Session.Button.Common.ProminentFilled"
android:backgroundTint="@color/destructive" android:backgroundTint="?danger"
android:layout_marginHorizontal="@dimen/small_spacing" android:layout_marginHorizontal="@dimen/small_spacing"
android:id="@+id/declineButton" android:id="@+id/declineButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -5,7 +5,7 @@
android:id="@+id/rootLayout" android:id="@+id/rootLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?conversation_menu_background_color"> android:background="@drawable/sheet_rounded_bg">
<TextView <TextView
android:id="@+id/titleText" android:id="@+id/titleText"

View File

@ -14,14 +14,14 @@
android:contentDescription="@string/AccessibilityId_delete_just_for_me" android:contentDescription="@string/AccessibilityId_delete_just_for_me"
style="@style/BottomSheetActionItem" style="@style/BottomSheetActionItem"
android:text="@string/delete_message_for_me" android:text="@string/delete_message_for_me"
android:textColor="@color/destructive"/> android:textColor="?danger"/>
<TextView <TextView
android:id="@+id/deleteForEveryoneTextView" android:id="@+id/deleteForEveryoneTextView"
android:contentDescription="@string/delete_message_for_everyone" android:contentDescription="@string/delete_message_for_everyone"
style="@style/BottomSheetActionItem" style="@style/BottomSheetActionItem"
android:text="@string/delete_message_for_everyone" android:text="@string/delete_message_for_everyone"
android:textColor="@color/destructive"/> android:textColor="?danger"/>
<TextView <TextView
android:id="@+id/cancelTextView" android:id="@+id/cancelTextView"

View File

@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?conversation_menu_background_color"> android:background="@drawable/sheet_rounded_bg">
<TextView <TextView
android:id="@+id/titleText" android:id="@+id/titleText"

View File

@ -46,7 +46,7 @@
/> />
<TextView <TextView
android:textColor="@color/destructive" android:textColor="?danger"
android:id="@+id/cancelButton" android:id="@+id/cancelButton"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -4,7 +4,7 @@
android:id="@+id/rootLayout" android:id="@+id/rootLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?conversation_menu_background_color"> android:background="@drawable/sheet_rounded_bg">
<TextView <TextView
android:id="@+id/titleText" android:id="@+id/titleText"

View File

@ -13,13 +13,13 @@
android:layout_width="13dp" android:layout_width="13dp"
android:layout_height="13dp" android:layout_height="13dp"
android:src="@drawable/ic_baseline_block_24" android:src="@drawable/ic_baseline_block_24"
app:tint="@color/destructive" /> app:tint="?danger" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textStyle="bold" android:textStyle="bold"
android:textColor="@color/destructive" android:textColor="?danger"
android:textSize="16sp" android:textSize="16sp"
android:text="@string/recipient_preferences__block" android:text="@string/recipient_preferences__block"
/> />

View File

@ -31,7 +31,7 @@
android:layout_height="wrap_content"/> android:layout_height="wrap_content"/>
<TextView <TextView
android:text="@string/message_requests_clear_all" android:text="@string/message_requests_clear_all"
android:textColor="@color/destructive" android:textColor="?danger"
android:visibility="gone" android:visibility="gone"
android:id="@+id/header_view_clear_all" android:id="@+id/header_view_clear_all"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -62,6 +62,7 @@
android:textColor="?message_received_text_color" android:textColor="?message_received_text_color"
android:textAlignment="center" android:textAlignment="center"
android:layout_gravity="center" android:layout_gravity="center"
android:gravity="center"
tools:text="You missed a call" tools:text="You missed a call"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"

View File

@ -35,7 +35,7 @@
android:layout_width="16dp" android:layout_width="16dp"
android:layout_height="16dp" android:layout_height="16dp"
android:background="@drawable/circle_tintable" android:background="@drawable/circle_tintable"
android:backgroundTint="@color/destructive" /> android:backgroundTint="?danger" />
<TextView <TextView
android:id="@+id/recordingViewDurationTextView" android:id="@+id/recordingViewDurationTextView"
@ -121,7 +121,7 @@
android:alpha="0.5" android:alpha="0.5"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:background="@drawable/circle_tintable" android:background="@drawable/circle_tintable"
android:backgroundTint="@color/destructive" /> android:backgroundTint="?danger" />
</RelativeLayout> </RelativeLayout>
@ -136,7 +136,7 @@
android:layout_marginEnd="-8dp" android:layout_marginEnd="-8dp"
android:layout_marginBottom="0dp" android:layout_marginBottom="0dp"
android:background="@drawable/circle_tintable" android:background="@drawable/circle_tintable"
android:backgroundTint="@color/destructive" > android:backgroundTint="?danger" >
<ImageView <ImageView
android:id="@+id/recordButtonOverlayImageView" android:id="@+id/recordButtonOverlayImageView"

View File

@ -32,6 +32,7 @@
<attr name="onLightCell" format="reference|color"/> <attr name="onLightCell" format="reference|color"/>
<attr name="accentColor" format="reference|color"/> <attr name="accentColor" format="reference|color"/>
<attr name="danger" format="reference|color"/>
<attr name="backgroundSecondary" format="reference|color"/> <attr name="backgroundSecondary" format="reference|color"/>
<attr name="prominentButtonColor" format="reference|color"/> <attr name="prominentButtonColor" format="reference|color"/>
<attr name="elementBorderColor" format="reference|color"/> <attr name="elementBorderColor" format="reference|color"/>

View File

@ -1,6 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?> <?xml version='1.0' encoding='UTF-8'?>
<resources> <resources>
<color name="destructive">#FF453A</color>
<color name="unimportant">#D8D8D8</color> <color name="unimportant">#D8D8D8</color>
<color name="profile_picture_background">#353535</color> <color name="profile_picture_background">#353535</color>
<color name="action_bar_background">#161616</color> <color name="action_bar_background">#161616</color>

View File

@ -108,9 +108,9 @@
<item name="android:textColor">?android:textColorPrimary</item> <item name="android:textColor">?android:textColorPrimary</item>
</style> </style>
<style name="Widget.Session.Button.Common.DestructiveOutline"> <style name="Widget.Session.Button.Common.DangerOutline">
<item name="android:background">@drawable/destructive_outline_button_medium_background</item> <item name="android:background">@drawable/danger_outline_button_medium_background</item>
<item name="android:textColor">@color/button_destructive</item> <item name="android:textColor">@color/button_danger</item>
</style> </style>
<style name="Widget.Session.Button.Dialog" parent=""> <style name="Widget.Session.Button.Dialog" parent="">
@ -125,9 +125,9 @@
<item name="android:background">@drawable/unimportant_dialog_text_button_background</item> <item name="android:background">@drawable/unimportant_dialog_text_button_background</item>
</style> </style>
<style name="Widget.Session.Button.Dialog.DestructiveText"> <style name="Widget.Session.Button.Dialog.DangerText">
<item name="android:background">@drawable/destructive_dialog_text_button_background</item> <item name="android:background">@drawable/danger_dialog_text_button_background</item>
<item name="android:textColor">@color/destructive</item> <item name="android:textColor">?danger</item>
</style> </style>
<style name="Widget.Session.EditText.Compose" parent="@style/Signal.Text.Body"> <style name="Widget.Session.EditText.Compose" parent="@style/Signal.Text.Body">

View File

@ -58,7 +58,7 @@
<item name="prominentButtonColor">?colorAccent</item> <item name="prominentButtonColor">?colorAccent</item>
<item name="attachment_document_icon_small">@drawable/ic_document_small_dark</item> <item name="attachment_document_icon_small">@drawable/ic_document_small_dark</item>
<item name="attachment_document_icon_large">@drawable/ic_document_large_dark</item> <item name="attachment_document_icon_large">@drawable/ic_document_large_dark</item>
<item name="colorError">@color/destructive</item> <item name="colorError">?danger</item>
</style> </style>
<!-- This should be the default theme for the application. --> <!-- This should be the default theme for the application. -->
@ -318,6 +318,7 @@
<item name="backgroundSecondary">@color/classic_dark_1</item> <item name="backgroundSecondary">@color/classic_dark_1</item>
<item name="colorControlNormal">?android:textColorPrimary</item> <item name="colorControlNormal">?android:textColorPrimary</item>
<item name="colorControlActivated">?colorAccent</item> <item name="colorControlActivated">?colorAccent</item>
<item name="danger">@color/danger_dark</item>
<item name="android:textColorPrimary">@color/classic_dark_6</item> <item name="android:textColorPrimary">@color/classic_dark_6</item>
<item name="android:textColorSecondary">?android:textColorPrimary</item> <item name="android:textColorSecondary">?android:textColorPrimary</item>
<item name="android:textColorTertiary">@color/classic_dark_5</item> <item name="android:textColorTertiary">@color/classic_dark_5</item>
@ -399,6 +400,7 @@
<item name="colorPrimaryDark">@color/classic_light_6</item> <item name="colorPrimaryDark">@color/classic_light_6</item>
<item name="colorControlNormal">?android:textColorPrimary</item> <item name="colorControlNormal">?android:textColorPrimary</item>
<item name="colorControlActivated">?colorAccent</item> <item name="colorControlActivated">?colorAccent</item>
<item name="danger">@color/danger_light</item>
<item name="android:textColorPrimary">@color/classic_light_0</item> <item name="android:textColorPrimary">@color/classic_light_0</item>
<item name="android:textColorSecondary">@color/classic_light_1</item> <item name="android:textColorSecondary">@color/classic_light_1</item>
<item name="android:textColorTertiary">@color/classic_light_1</item> <item name="android:textColorTertiary">@color/classic_light_1</item>
@ -488,6 +490,7 @@
<item name="backgroundSecondary">@color/ocean_dark_1</item> <item name="backgroundSecondary">@color/ocean_dark_1</item>
<item name="colorControlNormal">@color/ocean_dark_7</item> <item name="colorControlNormal">@color/ocean_dark_7</item>
<item name="colorControlActivated">?colorAccent</item> <item name="colorControlActivated">?colorAccent</item>
<item name="danger">@color/danger_dark</item>
<item name="android:textColorPrimary">@color/ocean_dark_7</item> <item name="android:textColorPrimary">@color/ocean_dark_7</item>
<item name="android:textColorSecondary">@color/ocean_dark_5</item> <item name="android:textColorSecondary">@color/ocean_dark_5</item>
<item name="android:textColorTertiary">@color/ocean_dark_5</item> <item name="android:textColorTertiary">@color/ocean_dark_5</item>
@ -574,6 +577,7 @@
<item name="backgroundSecondary">@color/ocean_light_6</item> <item name="backgroundSecondary">@color/ocean_light_6</item>
<item name="colorControlNormal">@color/ocean_light_1</item> <item name="colorControlNormal">@color/ocean_light_1</item>
<item name="colorControlActivated">?colorAccent</item> <item name="colorControlActivated">?colorAccent</item>
<item name="danger">@color/danger_light</item>
<item name="android:textColorPrimary">@color/ocean_light_1</item> <item name="android:textColorPrimary">@color/ocean_light_1</item>
<item name="android:textColorSecondary">@color/ocean_light_2</item> <item name="android:textColorSecondary">@color/ocean_light_2</item>
<item name="android:textColorTertiary">@color/ocean_light_2</item> <item name="android:textColorTertiary">@color/ocean_light_2</item>
@ -686,6 +690,7 @@
<item name="actionBarStyle">@style/Widget.Session.ActionBar</item> <item name="actionBarStyle">@style/Widget.Session.ActionBar</item>
<item name="prominentButtonColor">?colorAccent</item> <item name="prominentButtonColor">?colorAccent</item>
<item name="elementBorderColor">@color/classic_dark_3</item> <item name="elementBorderColor">@color/classic_dark_3</item>
<item name="danger">@color/danger_dark</item>
<!-- Home screen --> <!-- Home screen -->
<item name="searchBackgroundColor">#1B1B1B</item> <item name="searchBackgroundColor">#1B1B1B</item>

View File

@ -25,7 +25,7 @@ import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.guava.Optional import org.session.libsignal.utilities.guava.Optional
import org.thoughtcrime.securesms.MainCoroutineRule import org.thoughtcrime.securesms.MainCoroutineRule
import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.ExpiryRadioOption import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.ExpiryRadioOption
import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.OptionsCard import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.OptionsCardData
import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.UiState import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.UiState
import org.thoughtcrime.securesms.database.GroupDatabase import org.thoughtcrime.securesms.database.GroupDatabase
import org.thoughtcrime.securesms.database.Storage import org.thoughtcrime.securesms.database.Storage
@ -87,7 +87,7 @@ class DisappearingMessagesViewModelTest {
viewModel.uiState.value viewModel.uiState.value
).isEqualTo( ).isEqualTo(
UiState( UiState(
OptionsCard( OptionsCardData(
R.string.activity_disappearing_messages_timer, R.string.activity_disappearing_messages_timer,
typeOption(ExpiryMode.NONE, selected = true), typeOption(ExpiryMode.NONE, selected = true),
timeOption(ExpiryType.AFTER_SEND, 12.hours), timeOption(ExpiryType.AFTER_SEND, 12.hours),
@ -126,7 +126,7 @@ class DisappearingMessagesViewModelTest {
viewModel.uiState.value viewModel.uiState.value
).isEqualTo( ).isEqualTo(
UiState( UiState(
OptionsCard( OptionsCardData(
R.string.activity_disappearing_messages_timer, R.string.activity_disappearing_messages_timer,
typeOption(ExpiryMode.NONE, selected = false), typeOption(ExpiryMode.NONE, selected = false),
timeOption(ExpiryType.LEGACY, 12.hours), timeOption(ExpiryType.LEGACY, 12.hours),
@ -165,7 +165,7 @@ class DisappearingMessagesViewModelTest {
viewModel.uiState.value viewModel.uiState.value
).isEqualTo( ).isEqualTo(
UiState( UiState(
OptionsCard( OptionsCardData(
R.string.activity_disappearing_messages_timer, R.string.activity_disappearing_messages_timer,
typeOption(ExpiryMode.NONE, selected = true), typeOption(ExpiryMode.NONE, selected = true),
timeOption(ExpiryType.AFTER_SEND, 12.hours), timeOption(ExpiryType.AFTER_SEND, 12.hours),
@ -205,7 +205,7 @@ class DisappearingMessagesViewModelTest {
viewModel.uiState.value viewModel.uiState.value
).isEqualTo( ).isEqualTo(
UiState( UiState(
OptionsCard( OptionsCardData(
R.string.activity_disappearing_messages_timer, R.string.activity_disappearing_messages_timer,
typeOption(ExpiryMode.NONE, enabled = false, selected = true), typeOption(ExpiryMode.NONE, enabled = false, selected = true),
timeOption(ExpiryType.AFTER_SEND, 12.hours, enabled = false), timeOption(ExpiryType.AFTER_SEND, 12.hours, enabled = false),
@ -247,7 +247,7 @@ class DisappearingMessagesViewModelTest {
viewModel.uiState.value viewModel.uiState.value
).isEqualTo( ).isEqualTo(
UiState( UiState(
OptionsCard( OptionsCardData(
R.string.activity_disappearing_messages_delete_type, R.string.activity_disappearing_messages_delete_type,
typeOption(ExpiryMode.NONE, selected = true), typeOption(ExpiryMode.NONE, selected = true),
typeOption(12.hours, ExpiryType.AFTER_READ), typeOption(12.hours, ExpiryType.AFTER_READ),
@ -286,13 +286,13 @@ class DisappearingMessagesViewModelTest {
viewModel.uiState.value viewModel.uiState.value
).isEqualTo( ).isEqualTo(
UiState( UiState(
OptionsCard( OptionsCardData(
R.string.activity_disappearing_messages_delete_type, R.string.activity_disappearing_messages_delete_type,
typeOption(ExpiryMode.NONE), typeOption(ExpiryMode.NONE),
typeOption(time, ExpiryType.AFTER_READ), typeOption(time, ExpiryType.AFTER_READ),
typeOption(time, ExpiryType.AFTER_SEND, selected = true) typeOption(time, ExpiryType.AFTER_SEND, selected = true)
), ),
OptionsCard( OptionsCardData(
R.string.activity_disappearing_messages_timer, R.string.activity_disappearing_messages_timer,
timeOption(ExpiryType.AFTER_SEND, 12.hours, selected = true), timeOption(ExpiryType.AFTER_SEND, 12.hours, selected = true),
timeOption(ExpiryType.AFTER_SEND, 1.days), timeOption(ExpiryType.AFTER_SEND, 1.days),
@ -332,14 +332,14 @@ class DisappearingMessagesViewModelTest {
viewModel.uiState.value viewModel.uiState.value
).isEqualTo( ).isEqualTo(
UiState( UiState(
OptionsCard( OptionsCardData(
R.string.activity_disappearing_messages_delete_type, R.string.activity_disappearing_messages_delete_type,
typeOption(ExpiryMode.NONE), typeOption(ExpiryMode.NONE),
typeOption(time, ExpiryType.LEGACY, selected = true), typeOption(time, ExpiryType.LEGACY, selected = true),
typeOption(12.hours, ExpiryType.AFTER_READ, enabled = false), typeOption(12.hours, ExpiryType.AFTER_READ, enabled = false),
typeOption(1.days, ExpiryType.AFTER_SEND, enabled = false) typeOption(1.days, ExpiryType.AFTER_SEND, enabled = false)
), ),
OptionsCard( OptionsCardData(
R.string.activity_disappearing_messages_timer, R.string.activity_disappearing_messages_timer,
timeOption(ExpiryType.LEGACY, 12.hours, selected = true), timeOption(ExpiryType.LEGACY, 12.hours, selected = true),
timeOption(ExpiryType.LEGACY, 1.days), timeOption(ExpiryType.LEGACY, 1.days),
@ -379,13 +379,13 @@ class DisappearingMessagesViewModelTest {
viewModel.uiState.value viewModel.uiState.value
).isEqualTo( ).isEqualTo(
UiState( UiState(
OptionsCard( OptionsCardData(
R.string.activity_disappearing_messages_delete_type, R.string.activity_disappearing_messages_delete_type,
typeOption(ExpiryMode.NONE), typeOption(ExpiryMode.NONE),
typeOption(12.hours, ExpiryType.AFTER_READ), typeOption(12.hours, ExpiryType.AFTER_READ),
typeOption(time, ExpiryType.AFTER_SEND, selected = true) typeOption(time, ExpiryType.AFTER_SEND, selected = true)
), ),
OptionsCard( OptionsCardData(
R.string.activity_disappearing_messages_timer, R.string.activity_disappearing_messages_timer,
timeOption(ExpiryType.AFTER_SEND, 12.hours), timeOption(ExpiryType.AFTER_SEND, 12.hours),
timeOption(ExpiryType.AFTER_SEND, 1.days, selected = true), timeOption(ExpiryType.AFTER_SEND, 1.days, selected = true),
@ -426,13 +426,13 @@ class DisappearingMessagesViewModelTest {
viewModel.uiState.value viewModel.uiState.value
).isEqualTo( ).isEqualTo(
UiState( UiState(
OptionsCard( OptionsCardData(
R.string.activity_disappearing_messages_delete_type, R.string.activity_disappearing_messages_delete_type,
typeOption(ExpiryMode.NONE), typeOption(ExpiryMode.NONE),
typeOption(1.days, ExpiryType.AFTER_READ, selected = true), typeOption(1.days, ExpiryType.AFTER_READ, selected = true),
typeOption(time, ExpiryType.AFTER_SEND) typeOption(time, ExpiryType.AFTER_SEND)
), ),
OptionsCard( OptionsCardData(
R.string.activity_disappearing_messages_timer, R.string.activity_disappearing_messages_timer,
timeOption(ExpiryType.AFTER_READ, 5.minutes), timeOption(ExpiryType.AFTER_READ, 5.minutes),
timeOption(ExpiryType.AFTER_READ, 1.hours), timeOption(ExpiryType.AFTER_READ, 1.hours),
@ -479,13 +479,13 @@ class DisappearingMessagesViewModelTest {
viewModel.uiState.value viewModel.uiState.value
).isEqualTo( ).isEqualTo(
UiState( UiState(
OptionsCard( OptionsCardData(
R.string.activity_disappearing_messages_delete_type, R.string.activity_disappearing_messages_delete_type,
typeOption(ExpiryMode.NONE), typeOption(ExpiryMode.NONE),
typeOption(12.hours, ExpiryType.AFTER_READ), typeOption(12.hours, ExpiryType.AFTER_READ),
typeOption(1.days, ExpiryType.AFTER_SEND, selected = true) typeOption(1.days, ExpiryType.AFTER_SEND, selected = true)
), ),
OptionsCard( OptionsCardData(
R.string.activity_disappearing_messages_timer, R.string.activity_disappearing_messages_timer,
timeOption(ExpiryType.AFTER_SEND, 12.hours), timeOption(ExpiryType.AFTER_SEND, 12.hours),
timeOption(ExpiryType.AFTER_SEND, 1.days, selected = true), timeOption(ExpiryType.AFTER_SEND, 1.days, selected = true),

View File

@ -1,6 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?> <?xml version='1.0' encoding='UTF-8'?>
<resources> <resources>
<color name="destructive">#FF453A</color>
<color name="unimportant">#D8D8D8</color> <color name="unimportant">#D8D8D8</color>
<color name="profile_picture_background">#353535</color> <color name="profile_picture_background">#353535</color>
<color name="cell_background">#1B1B1B</color> <color name="cell_background">#1B1B1B</color>