mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-28 12:35:17 +00:00
Merge branch 'dev' into just-prefs
This commit is contained in:
commit
a196a98f00
@ -381,7 +381,7 @@ dependencies {
|
||||
implementation "androidx.compose.ui:ui-tooling:$composeVersion"
|
||||
implementation "androidx.compose.runtime:runtime-livedata:$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"
|
||||
debugImplementation "androidx.compose.ui:ui-test-manifest:$composeVersion"
|
||||
|
||||
|
@ -114,14 +114,14 @@ class SessionDialogBuilder(val context: Context) {
|
||||
options,
|
||||
) { dialog, it -> onSelect(it); dialog.dismiss() }
|
||||
|
||||
fun destructiveButton(
|
||||
fun dangerButton(
|
||||
@StringRes text: Int,
|
||||
@StringRes contentDescription: Int = text,
|
||||
listener: () -> Unit = {}
|
||||
) = button(
|
||||
text,
|
||||
contentDescription,
|
||||
R.style.Widget_Session_Button_Dialog_DestructiveText,
|
||||
R.style.Widget_Session_Button_Dialog_DangerText,
|
||||
) { listener() }
|
||||
|
||||
fun okButton(listener: (() -> Unit) = {}) = button(android.R.string.ok) { listener() }
|
||||
|
@ -17,6 +17,7 @@ import org.session.libsession.messaging.contacts.Contact
|
||||
import org.session.libsession.utilities.Address
|
||||
import org.session.libsession.utilities.GroupUtil
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsession.utilities.prefs
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.session.libsignal.utilities.Log
|
||||
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 glide: GlideRequests = GlideApp.with(this)
|
||||
private val prefs = TextSecurePreferences(context)
|
||||
private val prefs = context.prefs
|
||||
private val userPublicKey = prefs.getLocalNumber()
|
||||
var publicKey: String? = null
|
||||
var displayName: String? = null
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 }
|
||||
}
|
@ -2,7 +2,7 @@ package org.thoughtcrime.securesms.components.menu
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.ColorInt
|
||||
|
||||
/**
|
||||
* Represents an action to be rendered
|
||||
@ -13,5 +13,5 @@ data class ActionItem(
|
||||
val action: Runnable,
|
||||
val contentDescription: Int? = null,
|
||||
val subtitle: ((Context) -> CharSequence?)? = null,
|
||||
@ColorRes val color: Int? = null,
|
||||
@ColorInt val color: Int? = null,
|
||||
)
|
||||
|
@ -78,7 +78,7 @@ class ContextMenuList(recyclerView: RecyclerView, onItemClick: () -> Unit) {
|
||||
|
||||
override fun bind(model: DisplayItem) {
|
||||
val item = model.item
|
||||
val color = item.color?.let { ContextCompat.getColor(context, it) }
|
||||
val color = item.color
|
||||
|
||||
if (item.iconRes > 0) {
|
||||
val typedValue = TypedValue()
|
||||
|
@ -14,7 +14,6 @@ import org.session.libsession.utilities.ExpirationUtil
|
||||
import org.session.libsession.utilities.SSKEnvironment.MessageExpirationManagerProtocol
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsession.utilities.getExpirationTypeDisplayValue
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
import org.thoughtcrime.securesms.showSessionDialog
|
||||
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
||||
@ -57,7 +56,7 @@ class DisappearingMessages @Inject constructor(
|
||||
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,
|
||||
contentDescription = if (message.expiresIn == 0L) R.string.AccessibilityId_confirm else R.string.AccessibilityId_set_button
|
||||
) {
|
||||
|
@ -13,8 +13,8 @@ import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
fun State.toUiState() = UiState(
|
||||
cards = listOfNotNull(
|
||||
typeOptions()?.let { ExpiryOptionsCard(GetString(R.string.activity_disappearing_messages_delete_type), it) },
|
||||
timeOptions()?.let { ExpiryOptionsCard(GetString(R.string.activity_disappearing_messages_timer), it) }
|
||||
typeOptions()?.let { ExpiryOptionsCardData(GetString(R.string.activity_disappearing_messages_delete_type), it) },
|
||||
timeOptions()?.let { ExpiryOptionsCardData(GetString(R.string.activity_disappearing_messages_timer), it) }
|
||||
),
|
||||
showGroupFooter = isGroup && isNewConfigEnabled,
|
||||
showSetButton = isSelfAdmin
|
||||
|
@ -3,11 +3,13 @@ package org.thoughtcrime.securesms.conversation.disappearingmessages.ui
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@ -18,15 +20,15 @@ import androidx.compose.ui.unit.dp
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||
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.OptionsCard
|
||||
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.contentDescription
|
||||
import org.thoughtcrime.securesms.ui.extraSmall
|
||||
import org.thoughtcrime.securesms.ui.fadingEdges
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalType
|
||||
|
||||
typealias ExpiryCallbacks = Callbacks<ExpiryMode>
|
||||
typealias ExpiryRadioOption = RadioOption<ExpiryMode>
|
||||
@ -39,26 +41,32 @@ fun DisappearingMessages(
|
||||
) {
|
||||
val scrollState = rememberScrollState()
|
||||
|
||||
Column(modifier = modifier.padding(horizontal = LocalDimensions.current.margin)) {
|
||||
Column(modifier = modifier.padding(horizontal = LocalDimensions.current.spacing)) {
|
||||
Box(modifier = Modifier.weight(1f)) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 20.dp)
|
||||
.padding(vertical = LocalDimensions.current.spacing)
|
||||
.verticalScroll(scrollState)
|
||||
.fadingEdges(scrollState),
|
||||
verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing)
|
||||
) {
|
||||
state.cards.forEach {
|
||||
OptionsCard(it, callbacks)
|
||||
state.cards.forEachIndexed { index, option ->
|
||||
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(
|
||||
text = stringResource(R.string.activity_disappearing_messages_group_footer),
|
||||
style = extraSmall,
|
||||
style = LocalType.current.extraSmall,
|
||||
fontWeight = FontWeight(400),
|
||||
color = LocalColors.current.textSecondary,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = LocalDimensions.current.xsSpacing)
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -68,7 +76,7 @@ fun DisappearingMessages(
|
||||
modifier = Modifier
|
||||
.contentDescription(R.string.AccessibilityId_set_button)
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.padding(bottom = 20.dp),
|
||||
.padding(bottom = LocalDimensions.current.spacing),
|
||||
onClick = callbacks::onSetClick
|
||||
)
|
||||
}
|
||||
|
@ -10,9 +10,9 @@ import androidx.compose.ui.unit.dp
|
||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||
import org.thoughtcrime.securesms.conversation.disappearingmessages.ExpiryType
|
||||
import org.thoughtcrime.securesms.conversation.disappearingmessages.State
|
||||
import org.thoughtcrime.securesms.ui.PreviewTheme
|
||||
import org.thoughtcrime.securesms.ui.color.Colors
|
||||
import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider
|
||||
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
|
||||
import org.thoughtcrime.securesms.ui.theme.ThemeColors
|
||||
import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
|
||||
|
||||
@Preview(widthDp = 450, heightDp = 700)
|
||||
@Composable
|
||||
@ -51,7 +51,7 @@ class StatePreviewParameterProvider : PreviewParameterProvider<State> {
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewThemes(
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
|
||||
) {
|
||||
PreviewTheme(colors) {
|
||||
DisappearingMessages(
|
||||
|
@ -5,15 +5,15 @@ import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||
import org.thoughtcrime.securesms.ui.GetString
|
||||
import org.thoughtcrime.securesms.ui.RadioOption
|
||||
|
||||
typealias ExpiryOptionsCard = OptionsCard<ExpiryMode>
|
||||
typealias ExpiryOptionsCardData = OptionsCardData<ExpiryMode>
|
||||
|
||||
data class UiState(
|
||||
val cards: List<ExpiryOptionsCard> = emptyList(),
|
||||
val cards: List<ExpiryOptionsCardData> = emptyList(),
|
||||
val showGroupFooter: Boolean = false,
|
||||
val showSetButton: Boolean = true
|
||||
) {
|
||||
constructor(
|
||||
vararg cards: ExpiryOptionsCard,
|
||||
vararg cards: ExpiryOptionsCardData,
|
||||
showGroupFooter: Boolean = false,
|
||||
showSetButton: Boolean = true,
|
||||
): this(
|
||||
@ -23,7 +23,7 @@ data class UiState(
|
||||
)
|
||||
}
|
||||
|
||||
data class OptionsCard<T>(
|
||||
data class OptionsCardData<T>(
|
||||
val title: GetString,
|
||||
val options: List<RadioOption<T>>
|
||||
) {
|
||||
|
@ -7,8 +7,9 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
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.ui.Divider
|
||||
import org.thoughtcrime.securesms.ui.ItemButton
|
||||
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.theme.LocalDimensions
|
||||
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.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.components.AppBar
|
||||
import org.thoughtcrime.securesms.ui.components.QrImage
|
||||
import org.thoughtcrime.securesms.ui.contentDescription
|
||||
import org.thoughtcrime.securesms.ui.small
|
||||
import org.thoughtcrime.securesms.ui.xl
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalType
|
||||
|
||||
@Composable
|
||||
internal fun StartConversationScreen(
|
||||
accountId: String,
|
||||
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)
|
||||
Surface(
|
||||
modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection()),
|
||||
@ -74,18 +77,18 @@ internal fun StartConversationScreen(
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = LocalDimensions.current.margin)
|
||||
.padding(top = LocalDimensions.current.itemSpacing)
|
||||
.padding(bottom = LocalDimensions.current.margin)
|
||||
.padding(horizontal = LocalDimensions.current.spacing)
|
||||
.padding(top = LocalDimensions.current.spacing)
|
||||
.padding(bottom = LocalDimensions.current.spacing)
|
||||
) {
|
||||
Text(stringResource(R.string.accountIdYours), style = xl)
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.xxxsItemSpacing))
|
||||
Text(stringResource(R.string.accountIdYours), style = LocalType.current.xl)
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.xxsSpacing))
|
||||
Text(
|
||||
text = stringResource(R.string.qrYoursDescription),
|
||||
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(
|
||||
string = accountId,
|
||||
Modifier.contentDescription(R.string.AccessibilityId_qr_code),
|
||||
@ -100,7 +103,7 @@ internal fun StartConversationScreen(
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PreviewStartConversationScreen(
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
|
||||
) {
|
||||
PreviewTheme(colors) {
|
||||
StartConversationScreen(
|
||||
|
@ -8,23 +8,23 @@ import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
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.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.ui.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.PreviewTheme
|
||||
import org.thoughtcrime.securesms.ui.base
|
||||
import org.thoughtcrime.securesms.ui.color.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.components.AppBar
|
||||
import org.thoughtcrime.securesms.ui.components.SlimOutlineButton
|
||||
import org.thoughtcrime.securesms.ui.components.SlimOutlineCopyButton
|
||||
import org.thoughtcrime.securesms.ui.components.border
|
||||
import org.thoughtcrime.securesms.ui.contentDescription
|
||||
import org.thoughtcrime.securesms.ui.small
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalType
|
||||
|
||||
@Composable
|
||||
internal fun InviteFriend(
|
||||
@ -34,10 +34,14 @@ internal fun InviteFriend(
|
||||
copyPublicKey: () -> 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)
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = LocalDimensions.current.itemSpacing),
|
||||
modifier = Modifier.padding(horizontal = LocalDimensions.current.spacing)
|
||||
.padding(top = LocalDimensions.current.spacing),
|
||||
) {
|
||||
Text(
|
||||
accountId,
|
||||
@ -45,24 +49,24 @@ internal fun InviteFriend(
|
||||
.contentDescription(R.string.AccessibilityId_account_id)
|
||||
.fillMaxWidth()
|
||||
.border()
|
||||
.padding(LocalDimensions.current.smallMargin),
|
||||
.padding(LocalDimensions.current.spacing),
|
||||
textAlign = TextAlign.Center,
|
||||
style = base
|
||||
style = LocalType.current.base
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.xsItemSpacing))
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.xsSpacing))
|
||||
|
||||
Text(
|
||||
stringResource(R.string.invite_your_friend_to_chat_with_you_on_session_by_sharing_your_account_id_with_them),
|
||||
textAlign = TextAlign.Center,
|
||||
style = small,
|
||||
style = LocalType.current.small,
|
||||
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(
|
||||
stringResource(R.string.share),
|
||||
modifier = Modifier
|
||||
|
@ -3,17 +3,19 @@ package org.thoughtcrime.securesms.conversation.start.newmessage
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
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.ui.Alignment
|
||||
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.emptyFlow
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.onboarding.ui.ContinuePrimaryOutlineButton
|
||||
import org.thoughtcrime.securesms.ui.LoadingArcOr
|
||||
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.theme.LocalDimensions
|
||||
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.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.components.AppBar
|
||||
import org.thoughtcrime.securesms.ui.components.BorderlessButtonWithIcon
|
||||
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.SessionTabRow
|
||||
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)
|
||||
|
||||
@ -52,7 +55,10 @@ internal fun NewMessage(
|
||||
) {
|
||||
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)
|
||||
SessionTabRow(pagerState, TITLES)
|
||||
HorizontalPager(pagerState) {
|
||||
@ -70,7 +76,6 @@ private fun EnterAccountId(
|
||||
callbacks: Callbacks,
|
||||
onHelp: () -> Unit = {}
|
||||
) {
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@ -78,14 +83,13 @@ private fun EnterAccountId(
|
||||
.imePadding()
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = LocalDimensions.current.xxsMargin, vertical = LocalDimensions.current.xsMargin),
|
||||
modifier = Modifier.padding(vertical = LocalDimensions.current.spacing),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.xsMargin)
|
||||
) {
|
||||
SessionOutlinedTextField(
|
||||
text = state.newMessageIdOrOns,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = LocalDimensions.current.smallMargin),
|
||||
.padding(horizontal = LocalDimensions.current.spacing),
|
||||
contentDescription = "Session id input box",
|
||||
placeholder = stringResource(R.string.accountIdOrOnsEnter),
|
||||
onChange = callbacks::onChange,
|
||||
@ -94,31 +98,36 @@ private fun EnterAccountId(
|
||||
isTextErrorColor = state.isTextErrorColor
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.xxxsSpacing))
|
||||
|
||||
BorderlessButtonWithIcon(
|
||||
text = stringResource(R.string.messageNewDescription),
|
||||
modifier = Modifier
|
||||
.contentDescription(R.string.AccessibilityId_help_desk_link)
|
||||
.padding(horizontal = LocalDimensions.current.margin)
|
||||
.padding(horizontal = LocalDimensions.current.mediumSpacing)
|
||||
.fillMaxWidth(),
|
||||
style = small,
|
||||
style = LocalType.current.small,
|
||||
color = LocalColors.current.textSecondary,
|
||||
iconRes = R.drawable.ic_circle_question_mark,
|
||||
onClick = onHelp
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedVisibility(state.isNextButtonVisible) {
|
||||
PrimaryOutlineButton(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.padding(horizontal = LocalDimensions.current.largeMargin)
|
||||
.fillMaxWidth()
|
||||
.contentDescription(R.string.next),
|
||||
onClick = callbacks::onContinue
|
||||
) {
|
||||
LoadingArcOr(state.loading) {
|
||||
Text(stringResource(R.string.next))
|
||||
}
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing))
|
||||
Spacer(Modifier.weight(2f))
|
||||
|
||||
PrimaryOutlineButton(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.padding(horizontal = LocalDimensions.current.xlargeSpacing)
|
||||
.padding(bottom = LocalDimensions.current.smallSpacing)
|
||||
.fillMaxWidth()
|
||||
.contentDescription(R.string.next),
|
||||
enabled = state.isNextButtonEnabled,
|
||||
onClick = callbacks::onContinue
|
||||
) {
|
||||
LoadingArcOr(state.loading) {
|
||||
Text(stringResource(R.string.next))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -127,7 +136,7 @@ private fun EnterAccountId(
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PreviewNewMessage(
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
|
||||
) {
|
||||
PreviewTheme(colors) {
|
||||
NewMessage(State("z"))
|
||||
|
@ -6,7 +6,6 @@ import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.TimeoutCancellationException
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@ -22,8 +21,6 @@ import org.session.libsignal.utilities.timeout
|
||||
import org.thoughtcrime.securesms.ui.GetString
|
||||
import java.util.concurrent.TimeoutException
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@HiltViewModel
|
||||
internal class NewMessageViewModel @Inject constructor(
|
||||
@ -112,7 +109,7 @@ internal data class State(
|
||||
val error: GetString? = null,
|
||||
val loading: Boolean = false
|
||||
) {
|
||||
val isNextButtonVisible: Boolean get() = newMessageIdOrOns.isNotBlank()
|
||||
val isNextButtonEnabled: Boolean get() = newMessageIdOrOns.isNotBlank()
|
||||
}
|
||||
|
||||
internal data class Success(val publicKey: String)
|
||||
|
@ -1120,7 +1120,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
showSessionDialog {
|
||||
title(R.string.RecipientPreferenceActivity_block_this_contact_question)
|
||||
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()
|
||||
if (deleteThread) {
|
||||
viewModel.deleteThread()
|
||||
@ -1163,7 +1163,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
showSessionDialog {
|
||||
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)
|
||||
destructiveButton(
|
||||
dangerButton(
|
||||
R.string.ConversationActivity_unblock,
|
||||
R.string.AccessibilityId_block_confirm
|
||||
) { viewModel.unblock() }
|
||||
|
@ -546,7 +546,8 @@ class ConversationReactionOverlay : FrameLayout {
|
||||
}
|
||||
// Delete message
|
||||
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
|
||||
if (userCanBanSelectedUsers(context, message, openGroup, userPublicKey, blindedPublicKey)) {
|
||||
|
@ -27,9 +27,9 @@ import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
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.GetString
|
||||
import org.thoughtcrime.securesms.ui.HorizontalPagerIndicator
|
||||
import org.thoughtcrime.securesms.ui.ItemButton
|
||||
import org.thoughtcrime.securesms.ui.LargeItemButton
|
||||
import org.thoughtcrime.securesms.ui.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.PreviewTheme
|
||||
import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
|
||||
import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
|
||||
import org.thoughtcrime.securesms.ui.TitledText
|
||||
import org.thoughtcrime.securesms.ui.base
|
||||
import org.thoughtcrime.securesms.ui.baseBold
|
||||
import org.thoughtcrime.securesms.ui.baseMonospace
|
||||
import org.thoughtcrime.securesms.ui.color.Colors
|
||||
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.theme.ThemeColors
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.theme.blackAlpha40
|
||||
import org.thoughtcrime.securesms.ui.theme.dangerButtonColors
|
||||
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
|
||||
|
||||
@AndroidEntryPoint
|
||||
@ -152,12 +151,12 @@ fun MessageDetails(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(vertical = LocalDimensions.current.smallItemSpacing),
|
||||
verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing)
|
||||
.padding(vertical = LocalDimensions.current.smallSpacing),
|
||||
verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing)
|
||||
) {
|
||||
state.record?.let { message ->
|
||||
AndroidView(
|
||||
modifier = Modifier.padding(horizontal = LocalDimensions.current.margin),
|
||||
modifier = Modifier.padding(horizontal = LocalDimensions.current.spacing),
|
||||
factory = {
|
||||
ViewVisibleMessageContentBinding.inflate(LayoutInflater.from(it)).mainContainerConstraint.apply {
|
||||
bind(
|
||||
@ -193,7 +192,7 @@ fun CellMetadata(
|
||||
state.apply {
|
||||
if (listOfNotNull(sent, received, error, senderInfo).isEmpty()) return
|
||||
CellWithPaddingAndMargin {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing)) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing)) {
|
||||
TitledText(sent)
|
||||
TitledText(received)
|
||||
TitledErrorText(error)
|
||||
@ -237,7 +236,7 @@ fun CellButtons(
|
||||
LargeItemButton(
|
||||
R.string.delete,
|
||||
R.drawable.ic_message_details__trash,
|
||||
colors = destructiveButtonColors(),
|
||||
colors = dangerButtonColors(),
|
||||
onClick = onDelete
|
||||
)
|
||||
}
|
||||
@ -251,7 +250,7 @@ fun Carousel(attachments: List<Attachment>, onClick: (Int) -> Unit) {
|
||||
|
||||
val pagerState = rememberPagerState { attachments.size }
|
||||
|
||||
Column(verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing)) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing)) {
|
||||
Row {
|
||||
CarouselPrevButton(pagerState)
|
||||
Box(modifier = Modifier.weight(1f)) {
|
||||
@ -260,7 +259,7 @@ fun Carousel(attachments: List<Attachment>, onClick: (Int) -> Unit) {
|
||||
ExpandButton(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(LocalDimensions.current.xxsItemSpacing)
|
||||
.padding(LocalDimensions.current.xxsSpacing)
|
||||
) { onClick(pagerState.currentPage) }
|
||||
}
|
||||
CarouselNextButton(pagerState)
|
||||
@ -313,7 +312,7 @@ fun ExpandButton(modifier: Modifier = Modifier, onClick: () -> Unit) {
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewMessageDetails(
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
|
||||
) {
|
||||
PreviewTheme(colors) {
|
||||
MessageDetails(
|
||||
@ -340,8 +339,8 @@ fun FileDetails(fileDetails: List<TitledText>) {
|
||||
|
||||
Cell {
|
||||
FlowRow(
|
||||
modifier = Modifier.padding(horizontal = LocalDimensions.current.xsItemSpacing, vertical = LocalDimensions.current.itemSpacing),
|
||||
verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing)
|
||||
modifier = Modifier.padding(horizontal = LocalDimensions.current.xsSpacing, vertical = LocalDimensions.current.spacing),
|
||||
verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing)
|
||||
) {
|
||||
fileDetails.forEach {
|
||||
BoxWithConstraints {
|
||||
@ -349,7 +348,7 @@ fun FileDetails(fileDetails: List<TitledText>) {
|
||||
it,
|
||||
modifier = Modifier
|
||||
.widthIn(min = maxWidth.div(2))
|
||||
.padding(horizontal = LocalDimensions.current.xsItemSpacing)
|
||||
.padding(horizontal = LocalDimensions.current.xsSpacing)
|
||||
.width(IntrinsicSize.Max)
|
||||
)
|
||||
}
|
||||
@ -362,7 +361,7 @@ fun FileDetails(fileDetails: List<TitledText>) {
|
||||
fun TitledErrorText(titledText: TitledText?) {
|
||||
TitledText(
|
||||
titledText,
|
||||
style = base,
|
||||
style = LocalType.current.base,
|
||||
color = LocalColors.current.danger
|
||||
)
|
||||
}
|
||||
@ -371,7 +370,7 @@ fun TitledErrorText(titledText: TitledText?) {
|
||||
fun TitledMonospaceText(titledText: TitledText?) {
|
||||
TitledText(
|
||||
titledText,
|
||||
style = baseMonospace
|
||||
style = LocalType.current.base.monospace()
|
||||
)
|
||||
}
|
||||
|
||||
@ -379,7 +378,7 @@ fun TitledMonospaceText(titledText: TitledText?) {
|
||||
fun TitledText(
|
||||
titledText: TitledText?,
|
||||
modifier: Modifier = Modifier,
|
||||
style: TextStyle = base,
|
||||
style: TextStyle = LocalType.current.base,
|
||||
color: Color = Color.Unspecified
|
||||
) {
|
||||
titledText?.apply {
|
||||
@ -396,8 +395,8 @@ fun TitledText(
|
||||
|
||||
@Composable
|
||||
fun TitledView(title: GetString, modifier: Modifier = Modifier, content: @Composable () -> Unit) {
|
||||
Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.xxxsItemSpacing)) {
|
||||
Text(title.string(), style = baseBold)
|
||||
Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.xxxsSpacing)) {
|
||||
Text(title.string(), style = LocalType.current.base.bold())
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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.sending_receiving.attachments.DatabaseAttachment
|
||||
import org.session.libsession.utilities.Address
|
||||
import org.session.libsession.utilities.ThemeUtil.getThemedColor
|
||||
import org.session.libsession.utilities.ViewUtil
|
||||
import org.session.libsession.utilities.getColorFromAttr
|
||||
import org.session.libsession.utilities.modifyLayoutParams
|
||||
@ -382,7 +383,7 @@ class VisibleMessageView : FrameLayout {
|
||||
private fun getMessageStatusInfo(message: MessageRecord): MessageStatusInfo = when {
|
||||
message.isFailed ->
|
||||
MessageStatusInfo(R.drawable.ic_delivery_status_failed,
|
||||
resources.getColor(R.color.destructive, context.theme),
|
||||
getThemedColor(context, R.attr.danger),
|
||||
R.string.delivery_status_failed
|
||||
)
|
||||
message.isSyncFailed ->
|
||||
|
@ -15,6 +15,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.databinding.ViewConversationBinding
|
||||
import org.session.libsession.utilities.ThemeUtil
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities.highlightMentions
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.NOTIFY_TYPE_ALL
|
||||
@ -67,7 +68,7 @@ class ConversationView : LinearLayout {
|
||||
}
|
||||
val unreadCount = thread.unreadCount
|
||||
if (thread.recipient.isBlocked) {
|
||||
binding.accentView.setBackgroundResource(R.color.destructive)
|
||||
binding.accentView.setBackgroundColor(ThemeUtil.getThemedColor(context, R.attr.danger))
|
||||
binding.accentView.visibility = View.VISIBLE
|
||||
} else {
|
||||
val accentColor = context.getAccentColor()
|
||||
@ -120,7 +121,7 @@ class ConversationView : LinearLayout {
|
||||
!thread.isOutgoing -> binding.statusIndicatorImageView.visibility = View.GONE
|
||||
thread.isFailed -> {
|
||||
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)
|
||||
}
|
||||
thread.isPending -> binding.statusIndicatorImageView.setImageResource(R.drawable.ic_circle_dot_dot_dot)
|
||||
|
@ -3,8 +3,8 @@ package org.thoughtcrime.securesms.home
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
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.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.ui.Divider
|
||||
import org.thoughtcrime.securesms.ui.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.PreviewTheme
|
||||
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.h4
|
||||
import org.thoughtcrime.securesms.ui.h8
|
||||
import org.thoughtcrime.securesms.ui.small
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
|
||||
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.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalType
|
||||
|
||||
@Composable
|
||||
internal fun EmptyView(newAccount: Boolean) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = LocalDimensions.current.homeEmptyViewMargin)
|
||||
.padding(horizontal = 50.dp)
|
||||
) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Icon(
|
||||
@ -42,27 +40,27 @@ internal fun EmptyView(newAccount: Boolean) {
|
||||
if (newAccount) {
|
||||
Text(
|
||||
stringResource(R.string.onboardingAccountCreated),
|
||||
style = h4,
|
||||
style = LocalType.current.h4,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Text(
|
||||
stringResource(R.string.welcome_to_session),
|
||||
style = base,
|
||||
style = LocalType.current.base,
|
||||
color = LocalColors.current.primary,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
|
||||
Divider(modifier = Modifier.padding(vertical = LocalDimensions.current.xsMargin))
|
||||
Divider(modifier = Modifier.padding(vertical = LocalDimensions.current.smallSpacing))
|
||||
|
||||
Text(
|
||||
stringResource(R.string.conversationsNone),
|
||||
style = h8,
|
||||
style = LocalType.current.h8,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(bottom = LocalDimensions.current.xsItemSpacing))
|
||||
modifier = Modifier.padding(bottom = LocalDimensions.current.xsSpacing))
|
||||
Text(
|
||||
stringResource(R.string.onboardingHitThePlusButton),
|
||||
style = small,
|
||||
style = LocalType.current.small,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(2f))
|
||||
@ -72,7 +70,7 @@ internal fun EmptyView(newAccount: Boolean) {
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewEmptyView(
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
|
||||
) {
|
||||
PreviewTheme(colors) {
|
||||
EmptyView(newAccount = false)
|
||||
@ -82,7 +80,7 @@ fun PreviewEmptyView(
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewEmptyViewNew(
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
|
||||
) {
|
||||
PreviewTheme(colors) {
|
||||
EmptyView(newAccount = true)
|
||||
|
@ -10,7 +10,7 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredWidth
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
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.PreviewParameter
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.ui.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.PreviewTheme
|
||||
import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
|
||||
import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
|
||||
import org.thoughtcrime.securesms.ui.SessionShieldIcon
|
||||
import org.thoughtcrime.securesms.ui.color.Colors
|
||||
import org.thoughtcrime.securesms.ui.color.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.theme.ThemeColors
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.components.SlimPrimaryOutlineButton
|
||||
import org.thoughtcrime.securesms.ui.contentDescription
|
||||
import org.thoughtcrime.securesms.ui.h8
|
||||
import org.thoughtcrime.securesms.ui.small
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalType
|
||||
|
||||
@Composable
|
||||
internal fun SeedReminder(startRecoveryPasswordActivity: () -> Unit) {
|
||||
@ -43,25 +42,25 @@ internal fun SeedReminder(startRecoveryPasswordActivity: () -> Unit) {
|
||||
Modifier
|
||||
.background(LocalColors.current.backgroundSecondary)
|
||||
.padding(
|
||||
horizontal = LocalDimensions.current.smallMargin,
|
||||
vertical = LocalDimensions.current.xsMargin
|
||||
horizontal = LocalDimensions.current.spacing,
|
||||
vertical = LocalDimensions.current.smallSpacing
|
||||
)
|
||||
) {
|
||||
Column(Modifier.weight(1f)) {
|
||||
Row {
|
||||
Text(
|
||||
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()
|
||||
}
|
||||
Text(
|
||||
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(
|
||||
text = stringResource(R.string.continue_2),
|
||||
modifier = Modifier
|
||||
@ -76,7 +75,7 @@ internal fun SeedReminder(startRecoveryPasswordActivity: () -> Unit) {
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PreviewSeedReminder(
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
|
||||
) {
|
||||
PreviewTheme(colors) {
|
||||
SeedReminder {}
|
||||
|
@ -10,6 +10,7 @@ import android.view.ViewGroup
|
||||
import android.widget.PopupMenu
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.utilities.ThemeUtil
|
||||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||
@ -60,8 +61,9 @@ class MessageRequestsAdapter(
|
||||
for (i in 0 until popupMenu.menu.size()) {
|
||||
val item = popupMenu.menu.getItem(i)
|
||||
val s = SpannableString(item.title)
|
||||
s.setSpan(ForegroundColorSpan(context.getColor(R.color.destructive)), 0, s.length, 0)
|
||||
item.iconTintList = ColorStateList.valueOf(context.getColor(R.color.destructive))
|
||||
val danger = ThemeUtil.getThemedColor(context, R.attr.danger)
|
||||
s.setSpan(ForegroundColorSpan(danger), 0, s.length, 0)
|
||||
item.iconTintList = ColorStateList.valueOf(danger)
|
||||
item.title = s
|
||||
}
|
||||
popupMenu.setForceShowIcon(true)
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
180
app/src/main/java/org/thoughtcrime/securesms/mms/Slide.kt
Normal file
180
app/src/main/java/org/thoughtcrime/securesms/mms/Slide.kt
Normal 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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.ui.AlertDialog
|
||||
import org.thoughtcrime.securesms.ui.DialogButtonModel
|
||||
import org.thoughtcrime.securesms.ui.GetString
|
||||
import org.thoughtcrime.securesms.ui.color.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalColors
|
||||
|
||||
@Composable
|
||||
fun OnboardingBackPressAlertDialog(
|
||||
|
@ -5,6 +5,7 @@ import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
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.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
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.DialogButtonModel
|
||||
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.PrimaryFillButton
|
||||
import org.thoughtcrime.securesms.ui.components.PrimaryOutlineButton
|
||||
import org.thoughtcrime.securesms.ui.contentDescription
|
||||
import org.thoughtcrime.securesms.ui.h4
|
||||
import org.thoughtcrime.securesms.ui.large
|
||||
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.SessionColorsParameterProvider
|
||||
import org.thoughtcrime.securesms.ui.theme.ThemeColors
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PreviewLandingScreen(
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
|
||||
) {
|
||||
PreviewTheme(colors) {
|
||||
LandingScreen({}, {}, {}, {})
|
||||
@ -80,6 +78,7 @@ internal fun LandingScreen(
|
||||
onDismissRequest = { isUrlDialogVisible = false },
|
||||
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),
|
||||
@ -107,24 +106,24 @@ internal fun LandingScreen(
|
||||
Column {
|
||||
Column(modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(horizontal = LocalDimensions.current.onboardingMargin)
|
||||
.padding(horizontal = LocalDimensions.current.mediumSpacing)
|
||||
) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Text(
|
||||
stringResource(R.string.onboardingBubblePrivacyInYourPocket),
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
style = h4,
|
||||
style = LocalType.current.h4,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.itemSpacing))
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.spacing))
|
||||
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
modifier = Modifier
|
||||
.heightIn(min = LocalDimensions.current.minScrollableViewHeight)
|
||||
.heightIn(min = 200.dp)
|
||||
.fillMaxWidth()
|
||||
.weight(3f),
|
||||
verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing)
|
||||
verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing)
|
||||
) {
|
||||
items(
|
||||
MESSAGES.take(count),
|
||||
@ -140,7 +139,7 @@ internal fun LandingScreen(
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.padding(horizontal = LocalDimensions.current.largeMargin)) {
|
||||
Column(modifier = Modifier.padding(horizontal = LocalDimensions.current.xlargeSpacing)) {
|
||||
PrimaryFillButton(
|
||||
text = stringResource(R.string.onboardingAccountCreate),
|
||||
modifier = Modifier
|
||||
@ -149,7 +148,7 @@ internal fun LandingScreen(
|
||||
.contentDescription(R.string.AccessibilityId_create_account_button),
|
||||
onClick = createAccount
|
||||
)
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.smallItemSpacing))
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing))
|
||||
PrimaryOutlineButton(
|
||||
stringResource(R.string.onboardingAccountExists),
|
||||
modifier = Modifier
|
||||
@ -166,7 +165,7 @@ internal fun LandingScreen(
|
||||
.contentDescription(R.string.AccessibilityId_open_url),
|
||||
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()) {
|
||||
MessageText(
|
||||
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,
|
||||
modifier = Modifier.align(if (isOutgoing) Alignment.TopEnd else Alignment.TopStart)
|
||||
)
|
||||
@ -209,19 +208,17 @@ private fun MessageText(
|
||||
modifier: Modifier = Modifier,
|
||||
textColor: Color = Color.Unspecified
|
||||
) {
|
||||
Card(
|
||||
modifier = modifier.fillMaxWidth(0.666f),
|
||||
shape = MaterialTheme.shapes.small,
|
||||
backgroundColor = color,
|
||||
elevation = 0.dp
|
||||
Box(
|
||||
modifier = modifier.fillMaxWidth(0.666f)
|
||||
.background(color = color, shape = MaterialTheme.shapes.small)
|
||||
) {
|
||||
Text(
|
||||
text,
|
||||
style = large,
|
||||
style = LocalType.current.large,
|
||||
color = textColor,
|
||||
modifier = Modifier.padding(
|
||||
horizontal = LocalDimensions.current.smallItemSpacing,
|
||||
vertical = LocalDimensions.current.xsItemSpacing
|
||||
horizontal = LocalDimensions.current.smallSpacing,
|
||||
vertical = LocalDimensions.current.xsSpacing
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -13,8 +13,8 @@ import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@ -24,13 +24,12 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.onboarding.ui.ContinuePrimaryOutlineButton
|
||||
import org.thoughtcrime.securesms.ui.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.PreviewTheme
|
||||
import org.thoughtcrime.securesms.ui.base
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
|
||||
import org.thoughtcrime.securesms.ui.components.MaybeScanQrCode
|
||||
import org.thoughtcrime.securesms.ui.components.SessionOutlinedTextField
|
||||
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)
|
||||
|
||||
@ -75,29 +74,29 @@ private fun RecoveryPassword(state: State, onChange: (String) -> Unit = {}, onCo
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
Spacer(Modifier.weight(1f))
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.smallItemSpacing))
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing))
|
||||
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = LocalDimensions.current.largeMargin)
|
||||
modifier = Modifier.padding(horizontal = LocalDimensions.current.mediumSpacing)
|
||||
) {
|
||||
Row {
|
||||
Text(
|
||||
text = stringResource(R.string.sessionRecoveryPassword),
|
||||
style = h4
|
||||
style = LocalType.current.h4
|
||||
)
|
||||
Spacer(Modifier.width(LocalDimensions.current.xxsItemSpacing))
|
||||
Spacer(Modifier.width(LocalDimensions.current.xxsSpacing))
|
||||
Icon(
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
painter = painterResource(id = R.drawable.ic_shield_outline),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.height(LocalDimensions.current.smallItemSpacing))
|
||||
Spacer(Modifier.height(LocalDimensions.current.smallSpacing))
|
||||
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),
|
||||
style = base
|
||||
style = LocalType.current.base
|
||||
)
|
||||
Spacer(Modifier.height(LocalDimensions.current.itemSpacing))
|
||||
Spacer(Modifier.height(LocalDimensions.current.spacing))
|
||||
SessionOutlinedTextField(
|
||||
text = state.recoveryPhrase,
|
||||
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))
|
||||
|
||||
ContinuePrimaryOutlineButton(modifier = Modifier.align(Alignment.CenterHorizontally), onContinue)
|
||||
|
@ -3,17 +3,16 @@ package org.thoughtcrime.securesms.onboarding.loading
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
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.base
|
||||
import org.thoughtcrime.securesms.ui.contentDescription
|
||||
import org.thoughtcrime.securesms.ui.h7
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalType
|
||||
|
||||
@Composable
|
||||
internal fun LoadingScreen(progress: Float) {
|
||||
@ -25,12 +24,12 @@ internal fun LoadingScreen(progress: Float) {
|
||||
)
|
||||
Text(
|
||||
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(
|
||||
stringResource(R.string.loadAccountProgressMessage),
|
||||
style = base
|
||||
style = LocalType.current.base
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(2f))
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
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.messagenotifications.MessageNotificationsViewModel.UiState
|
||||
import org.thoughtcrime.securesms.onboarding.ui.ContinuePrimaryOutlineButton
|
||||
import org.thoughtcrime.securesms.ui.AlertDialog
|
||||
import org.thoughtcrime.securesms.ui.DialogButtonModel
|
||||
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.base
|
||||
import org.thoughtcrime.securesms.ui.color.Colors
|
||||
import org.thoughtcrime.securesms.ui.color.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
|
||||
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.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.components.CircularProgressIndicator
|
||||
import org.thoughtcrime.securesms.ui.components.RadioButton
|
||||
import org.thoughtcrime.securesms.ui.contentDescription
|
||||
import org.thoughtcrime.securesms.ui.h4
|
||||
import org.thoughtcrime.securesms.ui.h8
|
||||
import org.thoughtcrime.securesms.ui.h9
|
||||
import org.thoughtcrime.securesms.ui.small
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalType
|
||||
|
||||
@Composable
|
||||
internal fun MessageNotificationsScreen(
|
||||
@ -63,11 +56,11 @@ internal fun MessageNotificationsScreen(
|
||||
Column {
|
||||
Spacer(Modifier.weight(1f))
|
||||
|
||||
Column(modifier = Modifier.padding(horizontal = LocalDimensions.current.onboardingMargin)) {
|
||||
Text(stringResource(R.string.notificationsMessage), style = h4)
|
||||
Spacer(Modifier.height(LocalDimensions.current.xsMargin))
|
||||
Text(stringResource(R.string.onboardingMessageNotificationExplaination), style = base)
|
||||
Spacer(Modifier.height(LocalDimensions.current.itemSpacing))
|
||||
Column(modifier = Modifier.padding(horizontal = LocalDimensions.current.mediumSpacing)) {
|
||||
Text(stringResource(R.string.notificationsMessage), style = LocalType.current.h4)
|
||||
Spacer(Modifier.height(LocalDimensions.current.smallSpacing))
|
||||
Text(stringResource(R.string.onboardingMessageNotificationExplaination), style = LocalType.current.base)
|
||||
Spacer(Modifier.height(LocalDimensions.current.spacing))
|
||||
}
|
||||
|
||||
NotificationRadioButton(
|
||||
@ -107,8 +100,8 @@ private fun NotificationRadioButton(
|
||||
RadioButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
checked = checked,
|
||||
contentPadding = PaddingValues(horizontal = LocalDimensions.current.margin, vertical = 7.dp)
|
||||
selected = checked,
|
||||
contentPadding = PaddingValues(horizontal = LocalDimensions.current.mediumSpacing, vertical = 7.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@ -120,17 +113,18 @@ private fun NotificationRadioButton(
|
||||
),
|
||||
) {
|
||||
Column(modifier = Modifier
|
||||
.padding(horizontal = 15.dp)
|
||||
.padding(top = 10.dp, bottom = 11.dp)) {
|
||||
Text(stringResource(title), style = h8)
|
||||
.padding(horizontal = LocalDimensions.current.smallSpacing,
|
||||
vertical = LocalDimensions.current.xsSpacing)
|
||||
) {
|
||||
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 {
|
||||
Text(
|
||||
stringResource(it),
|
||||
modifier = Modifier.padding(top = 6.dp),
|
||||
modifier = Modifier.padding(top = LocalDimensions.current.xxsSpacing),
|
||||
color = LocalColors.current.primary,
|
||||
style = h9
|
||||
style = LocalType.current.h9
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -141,7 +135,7 @@ private fun NotificationRadioButton(
|
||||
@Preview
|
||||
@Composable
|
||||
private fun MessageNotificationsScreenPreview(
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
|
||||
) {
|
||||
PreviewTheme(colors) {
|
||||
MessageNotificationsScreen()
|
||||
|
@ -8,7 +8,7 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@ -17,11 +17,10 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.onboarding.OnboardingBackPressAlertDialog
|
||||
import org.thoughtcrime.securesms.onboarding.ui.ContinuePrimaryOutlineButton
|
||||
import org.thoughtcrime.securesms.ui.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.PreviewTheme
|
||||
import org.thoughtcrime.securesms.ui.base
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
|
||||
import org.thoughtcrime.securesms.ui.components.SessionOutlinedTextField
|
||||
import org.thoughtcrime.securesms.ui.h4
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalType
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
@ -52,18 +51,18 @@ internal fun PickDisplayName(
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
Spacer(Modifier.weight(1f))
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.smallItemSpacing))
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing))
|
||||
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = LocalDimensions.current.largeMargin)
|
||||
modifier = Modifier.padding(horizontal = LocalDimensions.current.mediumSpacing)
|
||||
) {
|
||||
Text(stringResource(state.title), style = h4)
|
||||
Spacer(Modifier.height(LocalDimensions.current.smallItemSpacing))
|
||||
Text(stringResource(state.title), style = LocalType.current.h4)
|
||||
Spacer(Modifier.height(LocalDimensions.current.smallSpacing))
|
||||
Text(
|
||||
stringResource(state.description),
|
||||
style = base,
|
||||
modifier = Modifier.padding(bottom = LocalDimensions.current.xsItemSpacing))
|
||||
Spacer(Modifier.height(LocalDimensions.current.itemSpacing))
|
||||
style = LocalType.current.base,
|
||||
modifier = Modifier.padding(bottom = LocalDimensions.current.xsSpacing))
|
||||
Spacer(Modifier.height(LocalDimensions.current.spacing))
|
||||
SessionOutlinedTextField(
|
||||
text = state.displayName,
|
||||
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))
|
||||
|
||||
ContinuePrimaryOutlineButton(modifier = Modifier.align(Alignment.CenterHorizontally), onContinue)
|
||||
|
@ -6,7 +6,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
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.contentDescription
|
||||
|
||||
@ -17,8 +17,8 @@ fun ContinuePrimaryOutlineButton(modifier: Modifier, onContinue: () -> Unit) {
|
||||
modifier = modifier
|
||||
.contentDescription(R.string.AccessibilityId_continue)
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = LocalDimensions.current.largeMargin)
|
||||
.padding(bottom = LocalDimensions.current.xxsMargin),
|
||||
.padding(horizontal = LocalDimensions.current.xlargeSpacing)
|
||||
.padding(bottom = LocalDimensions.current.smallSpacing),
|
||||
onClick = onContinue,
|
||||
)
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
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.conversation.v2.ConversationActivityV2
|
||||
import org.thoughtcrime.securesms.database.threadDatabase
|
||||
import org.thoughtcrime.securesms.ui.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.color.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.components.MaybeScanQrCode
|
||||
import org.thoughtcrime.securesms.ui.components.QrImage
|
||||
import org.thoughtcrime.securesms.ui.components.SessionTabRow
|
||||
import org.thoughtcrime.securesms.ui.contentDescription
|
||||
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
|
||||
|
||||
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) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(LocalColors.current.backgroundSecondary)
|
||||
.padding(horizontal = LocalDimensions.current.margin)
|
||||
.background(LocalColors.current.background)
|
||||
.padding(horizontal = LocalDimensions.current.mediumSpacing)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
QrImage(
|
||||
string = string,
|
||||
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),
|
||||
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),
|
||||
color = LocalColors.current.textSecondary,
|
||||
textAlign = TextAlign.Center,
|
||||
style = small
|
||||
style = LocalType.current.small
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -77,8 +77,8 @@ import org.thoughtcrime.securesms.ui.Cell
|
||||
import org.thoughtcrime.securesms.ui.Divider
|
||||
import org.thoughtcrime.securesms.ui.LargeItemButton
|
||||
import org.thoughtcrime.securesms.ui.LargeItemButtonWithDrawable
|
||||
import org.thoughtcrime.securesms.ui.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.color.destructiveButtonColors
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.theme.dangerButtonColors
|
||||
import org.thoughtcrime.securesms.ui.components.PrimaryOutlineButton
|
||||
import org.thoughtcrime.securesms.ui.components.PrimaryOutlineCopyButton
|
||||
import org.thoughtcrime.securesms.ui.contentDescription
|
||||
@ -448,9 +448,9 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
||||
Column {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = LocalDimensions.current.smallMargin)
|
||||
.padding(top = LocalDimensions.current.xxxsMargin),
|
||||
horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing),
|
||||
.padding(horizontal = LocalDimensions.current.spacing)
|
||||
.padding(top = LocalDimensions.current.xxsSpacing),
|
||||
horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing),
|
||||
) {
|
||||
PrimaryOutlineButton(
|
||||
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)
|
||||
|
||||
@ -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>() }
|
||||
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") }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -509,4 +509,4 @@ private fun LocalBroadcastManager.hasPaths(): Flow<Boolean> = callbackFlow {
|
||||
registerReceiver(receiver, IntentFilter("pathsBuilt"))
|
||||
|
||||
awaitClose { unregisterReceiver(receiver) }
|
||||
}.onStart { emit(Unit) }.map { OnionRequestAPI.paths.isNotEmpty() }
|
||||
}.onStart { emit(Unit) }.map { OnionRequestAPI.paths.isNotEmpty() }
|
@ -6,6 +6,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
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 javax.inject.Inject
|
||||
@ -26,11 +27,17 @@ class AppearanceSettingsViewModel @Inject constructor(private val prefs: TextSec
|
||||
prefs.setThemeStyle(newThemeStyle)
|
||||
// update UI state
|
||||
_uiState.value = prefs.themeState()
|
||||
|
||||
// force compose to refresh its style reference
|
||||
selectedTheme = null
|
||||
}
|
||||
|
||||
fun setNewFollowSystemSettings(followSystemSettings: Boolean) {
|
||||
prefs.setFollowSystemSettings(followSystemSettings)
|
||||
_uiState.value = prefs.themeState()
|
||||
|
||||
// force compose to refresh its style reference
|
||||
selectedTheme = null
|
||||
}
|
||||
|
||||
}
|
@ -11,7 +11,7 @@ import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@ -26,20 +26,19 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin
|
||||
import org.thoughtcrime.securesms.ui.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.PreviewTheme
|
||||
import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
|
||||
import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
|
||||
import org.thoughtcrime.securesms.ui.SessionShieldIcon
|
||||
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.theme.ThemeColors
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.components.QrImage
|
||||
import org.thoughtcrime.securesms.ui.components.SlimOutlineButton
|
||||
import org.thoughtcrime.securesms.ui.components.SlimOutlineCopyButton
|
||||
import org.thoughtcrime.securesms.ui.components.border
|
||||
import org.thoughtcrime.securesms.ui.contentDescription
|
||||
import org.thoughtcrime.securesms.ui.extraSmallMonospace
|
||||
import org.thoughtcrime.securesms.ui.h8
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalType
|
||||
import org.thoughtcrime.securesms.ui.theme.monospace
|
||||
|
||||
@Composable
|
||||
internal fun RecoveryPasswordScreen(
|
||||
@ -49,11 +48,11 @@ internal fun RecoveryPasswordScreen(
|
||||
onHide:() -> Unit = {}
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.xsMargin),
|
||||
verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing),
|
||||
modifier = Modifier
|
||||
.contentDescription(R.string.AccessibilityId_recovery_password)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(bottom = LocalDimensions.current.xsMargin)
|
||||
.padding(bottom = LocalDimensions.current.smallSpacing)
|
||||
) {
|
||||
RecoveryPasswordCell(mnemonic, seed, copyMnemonic)
|
||||
HideRecoveryPasswordCell(onHide)
|
||||
@ -75,17 +74,17 @@ private fun RecoveryPasswordCell(
|
||||
Row {
|
||||
Text(
|
||||
stringResource(R.string.sessionRecoveryPassword),
|
||||
style = h8
|
||||
style = LocalType.current.h8
|
||||
)
|
||||
Spacer(Modifier.width(LocalDimensions.current.xxsItemSpacing))
|
||||
Spacer(Modifier.width(LocalDimensions.current.xxsSpacing))
|
||||
SessionShieldIcon()
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.xxxsMargin))
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.xxsSpacing))
|
||||
|
||||
Text(
|
||||
stringResource(R.string.recoveryPasswordDescription),
|
||||
style = base
|
||||
style = LocalType.current.base
|
||||
)
|
||||
|
||||
AnimatedVisibility(!showQr) {
|
||||
@ -99,7 +98,7 @@ private fun RecoveryPasswordCell(
|
||||
QrImage(
|
||||
seed,
|
||||
modifier = Modifier
|
||||
.padding(vertical = LocalDimensions.current.smallMargin)
|
||||
.padding(vertical = LocalDimensions.current.spacing)
|
||||
.contentDescription(R.string.AccessibilityId_qr_code),
|
||||
contentPadding = 10.dp,
|
||||
icon = R.drawable.session_shield
|
||||
@ -108,7 +107,7 @@ private fun RecoveryPasswordCell(
|
||||
|
||||
AnimatedVisibility(!showQr) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallItemSpacing),
|
||||
horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
SlimOutlineCopyButton(
|
||||
@ -138,11 +137,11 @@ private fun RecoveryPassword(mnemonic: String) {
|
||||
mnemonic,
|
||||
modifier = Modifier
|
||||
.contentDescription(R.string.AccessibilityId_recovery_password_container)
|
||||
.padding(vertical = LocalDimensions.current.smallMargin)
|
||||
.padding(vertical = LocalDimensions.current.spacing)
|
||||
.border()
|
||||
.padding(LocalDimensions.current.smallMargin),
|
||||
.padding(LocalDimensions.current.spacing),
|
||||
textAlign = TextAlign.Center,
|
||||
style = extraSmallMonospace,
|
||||
style = LocalType.current.extraSmall.monospace(),
|
||||
color = LocalColors.current.run { if (isLight) text else primary },
|
||||
)
|
||||
}
|
||||
@ -156,14 +155,14 @@ private fun HideRecoveryPasswordCell(onHide: () -> Unit = {}) {
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.recoveryPasswordHideRecoveryPassword),
|
||||
style = h8
|
||||
style = LocalType.current.h8
|
||||
)
|
||||
Text(
|
||||
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(
|
||||
text = stringResource(R.string.hide),
|
||||
modifier = Modifier
|
||||
@ -180,7 +179,7 @@ private fun HideRecoveryPasswordCell(onHide: () -> Unit = {}) {
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PreviewRecoveryPasswordScreen(
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
|
||||
) {
|
||||
PreviewTheme(colors) {
|
||||
RecoveryPasswordScreen(mnemonic = "voyage urban toyed maverick peculiar tuxedo penguin tree grass building listen speak withdraw terminal plane")
|
||||
|
@ -34,7 +34,7 @@ class RecoveryPasswordActivity : BaseActionBarActivity() {
|
||||
showSessionDialog {
|
||||
title(R.string.recoveryPasswordHidePermanently)
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -44,7 +44,7 @@ class RecoveryPasswordActivity : BaseActionBarActivity() {
|
||||
title(R.string.recoveryPasswordHidePermanently)
|
||||
text(R.string.recoveryPasswordHidePermanentlyDescription2)
|
||||
cancelButton()
|
||||
destructiveButton(
|
||||
dangerButton(
|
||||
R.string.yes,
|
||||
contentDescription = R.string.AccessibilityId_confirm_button
|
||||
) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.thoughtcrime.securesms.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextButton
|
||||
import androidx.compose.material3.BasicAlertDialog
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
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.takeOrElse
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
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(
|
||||
val text: GetString,
|
||||
@ -32,29 +42,35 @@ class DialogButtonModel(
|
||||
val onClick: () -> Unit = {},
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AlertDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
title: 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(
|
||||
onDismissRequest,
|
||||
shape = MaterialTheme.shapes.small,
|
||||
backgroundColor = LocalColors.current.backgroundSecondary,
|
||||
buttons = {
|
||||
Box {
|
||||
IconButton(
|
||||
onClick = onDismissRequest,
|
||||
modifier = Modifier.align(Alignment.TopEnd)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_dialog_x),
|
||||
tint = LocalColors.current.text,
|
||||
contentDescription = "back"
|
||||
)
|
||||
BasicAlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
content = {
|
||||
Box(
|
||||
modifier = Modifier.background(color = LocalColors.current.backgroundSecondary,
|
||||
shape = MaterialTheme.shapes.small)
|
||||
) {
|
||||
// only show the 'x' button is required
|
||||
if(showCloseButton) {
|
||||
IconButton(
|
||||
onClick = onDismissRequest,
|
||||
modifier = Modifier.align(Alignment.TopEnd)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_dialog_x),
|
||||
tint = LocalColors.current.text,
|
||||
contentDescription = "back"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
@ -62,23 +78,23 @@ fun AlertDialog(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = LocalDimensions.current.smallItemSpacing)
|
||||
.padding(horizontal = LocalDimensions.current.smallItemSpacing)
|
||||
.padding(top = LocalDimensions.current.smallSpacing)
|
||||
.padding(horizontal = LocalDimensions.current.smallSpacing)
|
||||
) {
|
||||
title?.let {
|
||||
Text(
|
||||
it,
|
||||
textAlign = TextAlign.Center,
|
||||
style = h7,
|
||||
modifier = Modifier.padding(bottom = LocalDimensions.current.xxsItemSpacing)
|
||||
style = LocalType.current.h7,
|
||||
modifier = Modifier.padding(bottom = LocalDimensions.current.xxsSpacing)
|
||||
)
|
||||
}
|
||||
text?.let {
|
||||
Text(
|
||||
it,
|
||||
textAlign = TextAlign.Center,
|
||||
style = large,
|
||||
modifier = Modifier.padding(bottom = LocalDimensions.current.xxsItemSpacing)
|
||||
style = LocalType.current.large,
|
||||
modifier = Modifier.padding(bottom = LocalDimensions.current.xxsSpacing)
|
||||
)
|
||||
}
|
||||
content()
|
||||
@ -116,12 +132,59 @@ fun DialogButton(text: String, modifier: Modifier, color: Color = Color.Unspecif
|
||||
Text(
|
||||
text,
|
||||
color = color.takeOrElse { LocalColors.current.text },
|
||||
style = largeBold,
|
||||
style = LocalType.current.large.bold(),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(
|
||||
top = LocalDimensions.current.smallItemSpacing,
|
||||
bottom = LocalDimensions.current.itemSpacing
|
||||
top = LocalDimensions.current.smallSpacing,
|
||||
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 = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -16,12 +16,8 @@ import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.pager.PagerState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.ContentAlpha
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.LocalContentAlpha
|
||||
import androidx.compose.material.LocalContentColor
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
@ -37,26 +33,27 @@ import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.launch
|
||||
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.sign
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun BoxScope.HorizontalPagerIndicator(pagerState: PagerState) {
|
||||
if (pagerState.pageCount >= 2) Card(
|
||||
shape = pillShape,
|
||||
backgroundColor = Color.Black.copy(alpha = 0.4f),
|
||||
if (pagerState.pageCount >= 2) Box(
|
||||
modifier = Modifier
|
||||
.background(color = blackAlpha40, shape = pillShape)
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(8.dp)
|
||||
.padding(LocalDimensions.current.xxsSpacing)
|
||||
) {
|
||||
Box(modifier = Modifier.padding(8.dp)) {
|
||||
Box(modifier = Modifier.padding(LocalDimensions.current.xxsSpacing)) {
|
||||
ClickableHorizontalPagerIndicator(
|
||||
pagerState = pagerState,
|
||||
pageCount = pagerState.pageCount,
|
||||
activeColor = Color.White,
|
||||
inactiveColor = LocalColors.current.textSecondary)
|
||||
pageCount = pagerState.pageCount
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -73,9 +70,9 @@ fun ClickableHorizontalPagerIndicator(
|
||||
pageCount: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
pageIndexMapping: (Int) -> Int = { it },
|
||||
activeColor: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current),
|
||||
inactiveColor: Color = activeColor.copy(ContentAlpha.disabled),
|
||||
indicatorWidth: Dp = 8.dp,
|
||||
activeColor: Color = Color.White,
|
||||
inactiveColor: Color = LocalColors.current.disabled,
|
||||
indicatorWidth: Dp = LocalDimensions.current.xxsSpacing,
|
||||
indicatorHeight: Dp = indicatorWidth,
|
||||
spacing: Dp = indicatorWidth,
|
||||
indicatorShape: Shape = CircleShape,
|
||||
@ -115,9 +112,9 @@ private fun HorizontalPagerIndicator(
|
||||
pageCount: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
pageIndexMapping: (Int) -> Int = { it },
|
||||
activeColor: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current),
|
||||
inactiveColor: Color = activeColor.copy(ContentAlpha.disabled),
|
||||
indicatorWidth: Dp = 8.dp,
|
||||
activeColor: Color = Color.White,
|
||||
inactiveColor: Color = LocalColors.current.disabled,
|
||||
indicatorWidth: Dp = LocalDimensions.current.xxsSpacing,
|
||||
indicatorHeight: Dp = indicatorWidth,
|
||||
spacing: Dp = indicatorWidth,
|
||||
indicatorShape: Shape = CircleShape,
|
||||
|
@ -7,13 +7,13 @@ import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.ScrollState
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
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.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
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.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material.ButtonColors
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.LocalContentColor
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.RadioButton
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextButton
|
||||
import androidx.compose.material3.ButtonColors
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
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.semantics
|
||||
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.viewinterop.AndroidView
|
||||
@ -61,14 +62,15 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.session.libsession.utilities.runIf
|
||||
import org.thoughtcrime.securesms.components.ProfilePictureView
|
||||
import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.OptionsCard
|
||||
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.conversation.disappearingmessages.ui.OptionsCardData
|
||||
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.roundToInt
|
||||
|
||||
@ -92,18 +94,25 @@ data class RadioOption<T>(
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun <T> OptionsCard(card: OptionsCard<T>, callbacks: Callbacks<T>) {
|
||||
Text(
|
||||
card.title(),
|
||||
style = base
|
||||
)
|
||||
CellNoMargin {
|
||||
LazyColumn(
|
||||
modifier = Modifier.heightIn(max = 5000.dp)
|
||||
) {
|
||||
itemsIndexed(card.options) { i, it ->
|
||||
if (i != 0) Divider()
|
||||
TitledRadioButton(it) { callbacks.setValue(it.value) }
|
||||
fun <T> OptionsCard(card: OptionsCardData<T>, callbacks: Callbacks<T>) {
|
||||
Column {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = LocalDimensions.current.smallSpacing),
|
||||
text = card.title(),
|
||||
style = LocalType.current.base,
|
||||
color = LocalColors.current.textSecondary
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.xsSpacing))
|
||||
|
||||
CellNoMargin {
|
||||
LazyColumn(
|
||||
modifier = Modifier.heightIn(max = 5000.dp)
|
||||
) {
|
||||
itemsIndexed(card.options) { i, it ->
|
||||
if (i != 0) Divider()
|
||||
TitledRadioButton(option = it) { callbacks.setValue(it.value) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -117,7 +126,10 @@ fun LargeItemButtonWithDrawable(
|
||||
colors: ButtonColors = transparentButtonColors(),
|
||||
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
|
||||
@ -125,7 +137,7 @@ fun ItemButtonWithDrawable(
|
||||
@StringRes textId: Int,
|
||||
@DrawableRes icon: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
textStyle: TextStyle = xl,
|
||||
textStyle: TextStyle = LocalType.current.xl,
|
||||
colors: ButtonColors = transparentButtonColors(),
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
@ -155,7 +167,10 @@ fun LargeItemButton(
|
||||
colors: ButtonColors = transparentButtonColors(),
|
||||
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,
|
||||
@DrawableRes icon: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
textStyle: TextStyle = xl,
|
||||
textStyle: TextStyle = LocalType.current.xl,
|
||||
colors: ButtonColors = transparentButtonColors(),
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
@ -196,7 +211,7 @@ fun ItemButton(
|
||||
text: String,
|
||||
icon: @Composable BoxScope.() -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
textStyle: TextStyle = xl,
|
||||
textStyle: TextStyle = LocalType.current.xl,
|
||||
colors: ButtonColors = transparentButtonColors(),
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
@ -208,27 +223,42 @@ fun ItemButton(
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(80.dp)
|
||||
.width(50.dp)
|
||||
.wrapContentHeight()
|
||||
.align(Alignment.CenterVertically)
|
||||
) {
|
||||
icon()
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.width(LocalDimensions.current.smallSpacing))
|
||||
|
||||
Text(
|
||||
text,
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = LocalDimensions.current.xsItemSpacing)
|
||||
.padding(vertical = LocalDimensions.current.xsSpacing)
|
||||
.align(Alignment.CenterVertically),
|
||||
style = textStyle
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PrewviewItemButton() {
|
||||
PreviewTheme {
|
||||
ItemButton(
|
||||
textId = R.string.activity_create_group_title,
|
||||
icon = R.drawable.ic_group,
|
||||
onClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Cell(
|
||||
padding: Dp = 0.dp,
|
||||
margin: Dp = LocalDimensions.current.margin,
|
||||
margin: Dp = LocalDimensions.current.spacing,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
CellWithPaddingAndMargin(padding, margin) { content() }
|
||||
@ -240,64 +270,22 @@ fun CellNoMargin(content: @Composable () -> Unit) {
|
||||
|
||||
@Composable
|
||||
fun CellWithPaddingAndMargin(
|
||||
padding: Dp = LocalDimensions.current.smallMargin,
|
||||
margin: Dp = LocalDimensions.current.margin,
|
||||
padding: Dp = LocalDimensions.current.spacing,
|
||||
margin: Dp = LocalDimensions.current.spacing,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
Card(
|
||||
backgroundColor = LocalColors.current.backgroundSecondary,
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
elevation = 0.dp,
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = margin)
|
||||
.background(color = LocalColors.current.backgroundSecondary,
|
||||
shape = MaterialTheme.shapes.small)
|
||||
.wrapContentHeight()
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = margin),
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
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
|
||||
fun Modifier.contentDescription(text: GetString?): Modifier {
|
||||
return text?.let {
|
||||
@ -357,10 +345,10 @@ fun Modifier.fadingEdges(
|
||||
|
||||
@Composable
|
||||
fun Divider(modifier: Modifier = Modifier, startIndent: Dp = 0.dp) {
|
||||
androidx.compose.material.Divider(
|
||||
modifier = modifier.padding(horizontal = LocalDimensions.current.xsMargin),
|
||||
color = LocalColors.current.divider,
|
||||
startIndent = startIndent
|
||||
HorizontalDivider(
|
||||
modifier = modifier.padding(horizontal = LocalDimensions.current.smallSpacing)
|
||||
.padding(start = startIndent),
|
||||
color = LocalColors.current.borders,
|
||||
)
|
||||
}
|
||||
|
||||
@ -392,7 +380,7 @@ fun ProgressArc(progress: Float, modifier: Modifier = Modifier) {
|
||||
"${text}%",
|
||||
color = Color.White,
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
style = h2
|
||||
style = LocalType.current.h2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
)
|
@ -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,
|
||||
)
|
@ -5,6 +5,7 @@ import android.content.Context
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.fragment.app.Fragment
|
||||
import org.thoughtcrime.securesms.ui.theme.SessionMaterialTheme
|
||||
|
||||
fun Activity.setComposeContent(content: @Composable () -> Unit) {
|
||||
ComposeView(this)
|
||||
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -5,9 +5,9 @@ import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
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.PreviewParameter
|
||||
import network.loki.messenger.R
|
||||
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.h4
|
||||
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
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun AppBarPreview(
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
|
||||
) {
|
||||
PreviewTheme(colors) {
|
||||
AppBar(title = "Title", {}, {})
|
||||
@ -42,7 +42,7 @@ fun AppBar(title: String, onClose: () -> Unit = {}, onBack: (() -> Unit)? = null
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Text(text = title, style = h4)
|
||||
Text(text = title, style = LocalType.current.h4)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Box(contentAlignment = Alignment.Center, modifier = Modifier.size(LocalDimensions.current.appBarHeight)) {
|
||||
IconButton(onClick = onClose) {
|
||||
|
@ -1,12 +1,12 @@
|
||||
package org.thoughtcrime.securesms.ui.components
|
||||
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import org.thoughtcrime.securesms.ui.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.color.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalColors
|
||||
|
||||
@Composable
|
||||
fun Modifier.border() = this.border(
|
||||
|
@ -16,8 +16,8 @@ import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.ButtonColors
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.ButtonColors
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@ -37,13 +37,14 @@ import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import network.loki.messenger.R
|
||||
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.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.Companion.seconds
|
||||
|
||||
@ -65,16 +66,16 @@ fun Button(
|
||||
content: @Composable RowScope.() -> Unit
|
||||
) {
|
||||
style.applyButtonConstraints {
|
||||
androidx.compose.material.Button(
|
||||
onClick,
|
||||
modifier.heightIn(min = style.minHeight),
|
||||
enabled,
|
||||
interactionSource,
|
||||
androidx.compose.material3.Button(
|
||||
onClick = onClick,
|
||||
modifier = modifier.heightIn(min = style.minHeight),
|
||||
enabled = enabled,
|
||||
interactionSource = interactionSource,
|
||||
elevation = null,
|
||||
shape,
|
||||
border,
|
||||
colors,
|
||||
contentPadding
|
||||
shape = shape,
|
||||
border = border,
|
||||
colors = colors,
|
||||
contentPadding = contentPadding
|
||||
) {
|
||||
// Button sets LocalTextStyle, so text style is applied inside to override that.
|
||||
style.applyTextConstraints {
|
||||
@ -129,11 +130,11 @@ fun Button(
|
||||
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)
|
||||
}
|
||||
|
||||
@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)
|
||||
}
|
||||
|
||||
@ -257,7 +258,7 @@ fun BorderlessButtonWithIcon(
|
||||
text: String,
|
||||
@DrawableRes iconRes: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
style: TextStyle = baseBold,
|
||||
style: TextStyle = LocalType.current.base.bold(),
|
||||
color: Color = LocalColors.current.text,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
@ -292,7 +293,7 @@ val MutableInteractionSource.releases
|
||||
@Preview
|
||||
@Composable
|
||||
private fun VariousButtons(
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
|
||||
) {
|
||||
PreviewTheme(colors) {
|
||||
FlowRow(
|
||||
|
@ -1,21 +1,20 @@
|
||||
package org.thoughtcrime.securesms.ui.components
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.LocalMinimumInteractiveComponentEnforcement
|
||||
import androidx.compose.material.LocalTextStyle
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.LocalMinimumInteractiveComponentEnforcement
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.thoughtcrime.securesms.ui.baseBold
|
||||
import org.thoughtcrime.securesms.ui.extraSmall
|
||||
import org.thoughtcrime.securesms.ui.extraSmallBold
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalType
|
||||
import org.thoughtcrime.securesms.ui.theme.bold
|
||||
|
||||
interface ButtonStyle {
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@SuppressLint("ComposableNaming")
|
||||
@Composable fun applyButtonConstraints(content: @Composable () -> Unit) {
|
||||
CompositionLocalProvider(
|
||||
@ -27,26 +26,34 @@ interface ButtonStyle {
|
||||
@SuppressLint("ComposableNaming")
|
||||
@Composable fun applyTextConstraints(content: @Composable () -> Unit) {
|
||||
CompositionLocalProvider(
|
||||
LocalTextStyle provides textStyle,
|
||||
LocalTextStyle provides textStyle(),
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
||||
val textStyle: TextStyle
|
||||
@Composable
|
||||
fun textStyle() : TextStyle
|
||||
|
||||
val minHeight: Dp
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,13 @@ package org.thoughtcrime.securesms.ui.components
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.material.ButtonColors
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material3.ButtonColors
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.thoughtcrime.securesms.ui.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.color.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
|
||||
|
||||
private val disabledBorder @Composable get() = BorderStroke(
|
||||
width = LocalDimensions.current.borderStroke,
|
||||
@ -35,9 +35,9 @@ interface ButtonType {
|
||||
@Composable
|
||||
override fun buttonColors() = ButtonDefaults.buttonColors(
|
||||
contentColor = contentColor,
|
||||
backgroundColor = Color.Unspecified,
|
||||
containerColor = Color.Transparent,
|
||||
disabledContentColor = LocalColors.current.disabled,
|
||||
disabledBackgroundColor = Color.Unspecified
|
||||
disabledContainerColor = Color.Transparent
|
||||
)
|
||||
}
|
||||
|
||||
@ -47,9 +47,9 @@ interface ButtonType {
|
||||
@Composable
|
||||
override fun buttonColors() = ButtonDefaults.buttonColors(
|
||||
contentColor = LocalColors.current.background,
|
||||
backgroundColor = LocalColors.current.text,
|
||||
containerColor = LocalColors.current.text,
|
||||
disabledContentColor = LocalColors.current.disabled,
|
||||
disabledBackgroundColor = Color.Unspecified
|
||||
disabledContainerColor = Color.Transparent
|
||||
)
|
||||
}
|
||||
|
||||
@ -59,9 +59,9 @@ interface ButtonType {
|
||||
@Composable
|
||||
override fun buttonColors() = ButtonDefaults.buttonColors(
|
||||
contentColor = LocalColors.current.primaryButtonFillText,
|
||||
backgroundColor = LocalColors.current.primaryButtonFill,
|
||||
containerColor = LocalColors.current.primaryButtonFill,
|
||||
disabledContentColor = LocalColors.current.disabled,
|
||||
disabledBackgroundColor = Color.Unspecified
|
||||
disabledContainerColor = Color.Transparent
|
||||
)
|
||||
}
|
||||
|
||||
@ -73,8 +73,9 @@ interface ButtonType {
|
||||
@Composable
|
||||
override fun buttonColors() = ButtonDefaults.outlinedButtonColors(
|
||||
contentColor = color,
|
||||
backgroundColor = Color.Transparent,
|
||||
disabledContentColor = LocalColors.current.disabled
|
||||
containerColor = Color.Transparent,
|
||||
disabledContentColor = LocalColors.current.disabled,
|
||||
disabledContainerColor = Color.Transparent
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package org.thoughtcrime.securesms.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.LocalContentColor
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@ -9,7 +9,7 @@ import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun CircularProgressIndicator(color: Color = LocalContentColor.current) {
|
||||
androidx.compose.material.CircularProgressIndicator(
|
||||
androidx.compose.material3.CircularProgressIndicator(
|
||||
modifier = Modifier.size(40.dp),
|
||||
color = color,
|
||||
strokeWidth = 2.dp
|
||||
@ -18,7 +18,7 @@ fun CircularProgressIndicator(color: Color = LocalContentColor.current) {
|
||||
|
||||
@Composable
|
||||
fun SmallCircularProgressIndicator(color: Color = LocalContentColor.current) {
|
||||
androidx.compose.material.CircularProgressIndicator(
|
||||
androidx.compose.material3.CircularProgressIndicator(
|
||||
modifier = Modifier.size(20.dp),
|
||||
color = color,
|
||||
strokeWidth = 2.dp
|
||||
|
@ -22,11 +22,11 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.Snackbar
|
||||
import androidx.compose.material.SnackbarHost
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.rememberScaffoldState
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Snackbar
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@ -56,10 +56,9 @@ import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.launch
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsignal.utilities.Log
|
||||
import org.thoughtcrime.securesms.ui.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.base
|
||||
import org.thoughtcrime.securesms.ui.color.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.xl
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalType
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
private const val TAG = "NewMessageFragment"
|
||||
@ -78,7 +77,6 @@ fun MaybeScanQrCode(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(LocalColors.current.background)
|
||||
) {
|
||||
LocalSoftwareKeyboardController.current?.hide()
|
||||
|
||||
@ -94,10 +92,10 @@ fun MaybeScanQrCode(
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.activity_link_camera_permission_permanently_denied_configure_in_settings),
|
||||
style = base,
|
||||
style = LocalType.current.base,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(modifier = Modifier.size(LocalDimensions.current.itemSpacing))
|
||||
Spacer(modifier = Modifier.size(LocalDimensions.current.spacing))
|
||||
OutlineButton(
|
||||
stringResource(R.string.sessionSettings),
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
@ -107,14 +105,14 @@ fun MaybeScanQrCode(
|
||||
} else {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(color = LocalColors.current.backgroundSecondary)
|
||||
.fillMaxSize()
|
||||
.padding(LocalDimensions.current.largeMargin),
|
||||
.padding(LocalDimensions.current.xlargeSpacing),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Text(stringResource(R.string.fragment_scan_qr_code_camera_access_explanation), style = xl, textAlign = TextAlign.Center)
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.itemSpacing))
|
||||
Text(stringResource(R.string.fragment_scan_qr_code_camera_access_explanation),
|
||||
style = LocalType.current.xl, textAlign = TextAlign.Center)
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.spacing))
|
||||
PrimaryOutlineButton(
|
||||
stringResource(R.string.cameraGrantAccess),
|
||||
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()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
errors.collect { error ->
|
||||
scaffoldState.snackbarHostState
|
||||
snackbarHostState
|
||||
.takeIf { it.currentSnackbarData == null }
|
||||
?.run {
|
||||
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,
|
||||
// and each scan could restart the timer which could mean no scan gets
|
||||
// through until the user stops scanning; quite perplexing.
|
||||
showSnackbar(message = error)
|
||||
snackbarHostState.showSnackbar(message = error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
scaffoldState = scaffoldState,
|
||||
snackbarHost = {
|
||||
SnackbarHost(
|
||||
hostState = scaffoldState.snackbarHostState,
|
||||
modifier = Modifier.padding(LocalDimensions.current.smallItemSpacing)
|
||||
hostState = snackbarHostState,
|
||||
modifier = Modifier.padding(LocalDimensions.current.smallSpacing)
|
||||
) { data ->
|
||||
Snackbar(
|
||||
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(
|
||||
Modifier
|
||||
.aspectRatio(1f)
|
||||
.padding(LocalDimensions.current.itemSpacing)
|
||||
.padding(LocalDimensions.current.spacing)
|
||||
.clip(shape = RoundedCornerShape(26.dp))
|
||||
.background(Color(0x33ffffff))
|
||||
.align(Alignment.Center)
|
||||
|
@ -10,8 +10,8 @@ import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@ -32,15 +32,15 @@ import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.ui.LocalDimensions
|
||||
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.util.QRCodeUtilities
|
||||
|
||||
@Composable
|
||||
fun QrImage(
|
||||
string: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
contentPadding: Dp = LocalDimensions.current.smallItemSpacing,
|
||||
contentPadding: Dp = LocalDimensions.current.smallSpacing,
|
||||
icon: Int = R.drawable.session_shield
|
||||
) {
|
||||
var bitmap: Bitmap? by remember {
|
||||
@ -56,10 +56,9 @@ fun QrImage(
|
||||
}
|
||||
}
|
||||
|
||||
Card(
|
||||
backgroundColor = LocalColors.current.qrCodeBackground,
|
||||
elevation = 0.dp,
|
||||
modifier = modifier
|
||||
Box(
|
||||
modifier = modifier.background(color = LocalColors.current.qrCodeBackground,
|
||||
shape = MaterialTheme.shapes.small)
|
||||
) { Content(bitmap, icon, Modifier.padding(contentPadding), backgroundColor = LocalColors.current.qrCodeBackground) }
|
||||
}
|
||||
|
||||
|
@ -1,40 +1,58 @@
|
||||
package org.thoughtcrime.securesms.ui.components
|
||||
|
||||
import android.content.ContentProvider
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.scaleIn
|
||||
import androidx.compose.animation.scaleOut
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.TextButton
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.thoughtcrime.securesms.ui.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.color.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.color.transparentButtonColors
|
||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||
import org.thoughtcrime.securesms.conversation.disappearingmessages.ExpiryType
|
||||
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
|
||||
fun RadioButton(
|
||||
onClick: () -> Unit = {},
|
||||
modifier: Modifier = Modifier,
|
||||
checked: Boolean = false,
|
||||
selected: Boolean = false,
|
||||
enabled: Boolean = true,
|
||||
contentPadding: PaddingValues = PaddingValues(),
|
||||
content: @Composable RowScope.() -> Unit = {}
|
||||
) {
|
||||
@ -42,20 +60,22 @@ fun RadioButton(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.selectable(
|
||||
selected = checked,
|
||||
selected = selected,
|
||||
enabled = true,
|
||||
role = Role.RadioButton,
|
||||
onClick = onClick
|
||||
),
|
||||
enabled = enabled,
|
||||
colors = transparentButtonColors(),
|
||||
onClick = onClick,
|
||||
shape = RectangleShape,
|
||||
contentPadding = contentPadding
|
||||
) {
|
||||
content()
|
||||
|
||||
Spacer(modifier = Modifier.width(20.dp))
|
||||
RadioButtonIndicator(
|
||||
checked = checked,
|
||||
selected = selected && enabled, // disabled radio shouldn't be selected
|
||||
modifier = Modifier
|
||||
.size(22.dp)
|
||||
.align(Alignment.CenterVertically)
|
||||
@ -65,12 +85,12 @@ fun RadioButton(
|
||||
|
||||
@Composable
|
||||
private fun RadioButtonIndicator(
|
||||
checked: Boolean,
|
||||
selected: Boolean,
|
||||
modifier: Modifier
|
||||
) {
|
||||
Box(modifier = modifier) {
|
||||
AnimatedVisibility(
|
||||
checked,
|
||||
selected,
|
||||
modifier = Modifier
|
||||
.padding(2.5.dp)
|
||||
.clip(CircleShape),
|
||||
@ -91,9 +111,93 @@ private fun RadioButtonIndicator(
|
||||
.aspectRatio(1f)
|
||||
.border(
|
||||
width = LocalDimensions.current.borderStroke,
|
||||
color = LocalColors.current.text,
|
||||
color = LocalContentColor.current,
|
||||
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
|
||||
)
|
||||
) {}
|
||||
}
|
||||
}
|
@ -1,15 +1,15 @@
|
||||
package org.thoughtcrime.securesms.ui.components
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.pager.PagerState
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.material.Tab
|
||||
import androidx.compose.material.TabRow
|
||||
import androidx.compose.material.TabRowDefaults
|
||||
import androidx.compose.material.TabRowDefaults.tabIndicatorOffset
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.TabRow
|
||||
import androidx.compose.material3.TabRowDefaults
|
||||
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
@ -19,13 +19,12 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.launch
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.ui.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.color.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.PreviewTheme
|
||||
import org.thoughtcrime.securesms.ui.color.Colors
|
||||
import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider
|
||||
import org.thoughtcrime.securesms.ui.color.divider
|
||||
import org.thoughtcrime.securesms.ui.h8
|
||||
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.SessionColorsParameterProvider
|
||||
import org.thoughtcrime.securesms.ui.theme.ThemeColors
|
||||
|
||||
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
|
||||
fun SessionTabRow(pagerState: PagerState, titles: List<Int>) {
|
||||
TabRow(
|
||||
backgroundColor = Color.Unspecified,
|
||||
containerColor = Color.Unspecified,
|
||||
selectedTabIndex = pagerState.currentPage,
|
||||
contentColor = LocalColors.current.text,
|
||||
indicator = { tabPositions ->
|
||||
TabRowDefaults.Indicator(
|
||||
TabRowDefaults.SecondaryIndicator(
|
||||
Modifier.tabIndicatorOffset(tabPositions[pagerState.currentPage]),
|
||||
color = LocalColors.current.primary,
|
||||
height = LocalDimensions.current.indicatorHeight
|
||||
)
|
||||
},
|
||||
divider = { TabRowDefaults.Divider(color = LocalColors.current.divider) },
|
||||
modifier = Modifier
|
||||
.height(48.dp)
|
||||
.background(color = Color.Unspecified)
|
||||
divider = { HorizontalDivider(color = LocalColors.current.borders) }
|
||||
) {
|
||||
val animationScope = rememberCoroutineScope()
|
||||
titles.forEachIndexed { i, it ->
|
||||
Tab(
|
||||
i == pagerState.currentPage,
|
||||
modifier = Modifier.heightIn(min = 48.dp),
|
||||
selected = i == pagerState.currentPage,
|
||||
onClick = { animationScope.launch { pagerState.animateScrollToPage(i) } },
|
||||
selectedContentColor = LocalColors.current.text,
|
||||
unselectedContentColor = LocalColors.current.text,
|
||||
) {
|
||||
Text(
|
||||
stringResource(id = it),
|
||||
style = h8
|
||||
text = stringResource(id = it),
|
||||
style = LocalType.current.h8
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -69,7 +66,7 @@ fun SessionTabRow(pagerState: PagerState, titles: List<Int>) {
|
||||
@androidx.compose.ui.tooling.preview.Preview
|
||||
@Composable
|
||||
fun PreviewSessionTabRow(
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors
|
||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
|
||||
) {
|
||||
PreviewTheme(colors) {
|
||||
val pagerState = rememberPagerState { TITLES.size }
|
||||
|
@ -17,9 +17,9 @@ import androidx.compose.foundation.text.InlineTextContent
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.text.appendInlineContent
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
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.sp
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.ui.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.PreviewTheme
|
||||
import org.thoughtcrime.securesms.ui.base
|
||||
import org.thoughtcrime.securesms.ui.baseBold
|
||||
import org.thoughtcrime.securesms.ui.color.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.color.borders
|
||||
import org.thoughtcrime.securesms.ui.color.text
|
||||
import org.thoughtcrime.securesms.ui.color.textSecondary
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.theme.borders
|
||||
import org.thoughtcrime.securesms.ui.theme.text
|
||||
import org.thoughtcrime.securesms.ui.theme.textSecondary
|
||||
import org.thoughtcrime.securesms.ui.contentDescription
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalType
|
||||
import org.thoughtcrime.securesms.ui.theme.bold
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
@ -85,7 +85,7 @@ fun SessionOutlinedTextField(
|
||||
modifier: Modifier = Modifier,
|
||||
contentDescription: String? = null,
|
||||
onChange: (String) -> Unit = {},
|
||||
textStyle: TextStyle = base,
|
||||
textStyle: TextStyle = LocalType.current.base,
|
||||
placeholder: String = "",
|
||||
onContinue: () -> Unit = {},
|
||||
error: String? = null,
|
||||
@ -106,7 +106,7 @@ fun SessionOutlinedTextField(
|
||||
if (text.isEmpty()) {
|
||||
Text(
|
||||
text = placeholder,
|
||||
style = base,
|
||||
style = LocalType.current.base,
|
||||
color = LocalColors.current.textSecondary(isTextErrorColor),
|
||||
modifier = Modifier.wrapContentSize()
|
||||
.align(Alignment.CenterStart)
|
||||
@ -130,13 +130,13 @@ fun SessionOutlinedTextField(
|
||||
)
|
||||
}
|
||||
error?.let {
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.xsItemSpacing))
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.xsSpacing))
|
||||
Text(
|
||||
it,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
.contentDescription(R.string.AccessibilityId_error_message),
|
||||
textAlign = TextAlign.Center,
|
||||
style = baseBold,
|
||||
style = LocalType.current.base.bold(),
|
||||
color = LocalColors.current.danger
|
||||
)
|
||||
}
|
||||
@ -148,7 +148,7 @@ fun AnnotatedTextWithIcon(
|
||||
text: String,
|
||||
@DrawableRes iconRes: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
style: TextStyle = base,
|
||||
style: TextStyle = LocalType.current.base,
|
||||
color: Color = Color.Unspecified,
|
||||
iconSize: TextUnit = 12.sp
|
||||
) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
package org.thoughtcrime.securesms.ui.color
|
||||
package org.thoughtcrime.securesms.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
@ -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
|
||||
)
|
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
)
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
@ -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.layout.Box
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
|
||||
import androidx.compose.material.LocalContentColor
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Shapes
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Shapes
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.thoughtcrime.securesms.ui.color.ClassicDark
|
||||
import org.thoughtcrime.securesms.ui.color.ClassicLight
|
||||
import org.thoughtcrime.securesms.ui.color.Colors
|
||||
import org.thoughtcrime.securesms.ui.color.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.color.OceanDark
|
||||
import org.thoughtcrime.securesms.ui.color.OceanLight
|
||||
import org.thoughtcrime.securesms.ui.color.colors
|
||||
import org.thoughtcrime.securesms.ui.color.textSelectionColors
|
||||
import org.session.libsession.utilities.prefs
|
||||
|
||||
// Globally accessible composition local objects
|
||||
val LocalColors = compositionLocalOf <ThemeColors> { ClassicDark() }
|
||||
val LocalType = compositionLocalOf { sessionTypography }
|
||||
|
||||
var selectedTheme: ThemeColors? = null
|
||||
|
||||
/**
|
||||
* Apply a Material2 compose theme based on user selections in SharedPreferences.
|
||||
@ -31,24 +30,32 @@ import org.thoughtcrime.securesms.ui.color.textSelectionColors
|
||||
fun SessionMaterialTheme(
|
||||
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
|
||||
fun SessionMaterialTheme(
|
||||
colors: Colors,
|
||||
colors: ThemeColors,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
MaterialTheme(
|
||||
colors = colors.toMaterialColors(),
|
||||
typography = sessionTypography,
|
||||
colorScheme = colors.toMaterialColors(),
|
||||
typography = sessionTypography.asMaterialTypography(),
|
||||
shapes = sessionShapes,
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalColors provides colors,
|
||||
LocalType provides sessionTypography,
|
||||
LocalContentColor provides colors.text,
|
||||
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 buttonShape = pillShape
|
||||
|
||||
@ -88,7 +77,7 @@ val sessionShapes = Shapes(
|
||||
*/
|
||||
@Composable
|
||||
fun PreviewTheme(
|
||||
colors: Colors = LocalColors.current,
|
||||
colors: ThemeColors = LocalColors.current,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
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())
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_enabled="false" android:color="?android:textColorTertiary"/>
|
||||
<item android:color="@color/destructive"/>
|
||||
<item android:color="?danger"/>
|
||||
</selector>
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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"/>
|
||||
</selector>
|
@ -3,9 +3,9 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="@color/destructive" />
|
||||
<solid android:color="?danger" />
|
||||
|
||||
<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>
|
@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/button_destructive">
|
||||
android:color="@color/button_danger">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="?colorPrimary"/>
|
||||
<corners android:radius="@dimen/medium_button_corner_radius" />
|
||||
<stroke
|
||||
android:color="@color/button_destructive"
|
||||
android:color="@color/button_danger"
|
||||
android:width="@dimen/border_thickness" />
|
||||
</shape>
|
||||
</item>
|
@ -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>
|
@ -3,7 +3,7 @@
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="@color/destructive">
|
||||
android:tint="?danger">
|
||||
<path
|
||||
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"/>
|
||||
|
5
app/src/main/res/drawable/sheet_rounded_bg.xml
Normal file
5
app/src/main/res/drawable/sheet_rounded_bg.xml
Normal 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>
|
@ -39,7 +39,7 @@
|
||||
/>
|
||||
|
||||
<TextView
|
||||
style="@style/Widget.Session.Button.Common.DestructiveOutline"
|
||||
style="@style/Widget.Session.Button.Common.DangerOutline"
|
||||
android:paddingHorizontal="@dimen/large_spacing"
|
||||
android:paddingVertical="@dimen/small_spacing"
|
||||
android:text="@string/ConversationActivity_unblock"
|
||||
|
@ -228,7 +228,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@+id/toolbar"
|
||||
android:background="@color/destructive"
|
||||
android:background="?danger"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
@ -300,7 +300,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:contentDescription="@string/AccessibilityId_block_message_request_button"
|
||||
android:textColor="@color/destructive"
|
||||
android:textColor="?danger"
|
||||
android:paddingHorizontal="@dimen/massive_spacing"
|
||||
android:paddingVertical="@dimen/small_spacing"
|
||||
android:textSize="@dimen/text_size"
|
||||
@ -334,7 +334,7 @@
|
||||
|
||||
<Button
|
||||
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:layout_width="0dp"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
|
@ -51,7 +51,7 @@
|
||||
<Button
|
||||
android:contentDescription="@string/AccessibilityId_clear_all_message_requests"
|
||||
android:id="@+id/clearAllMessageRequestsButton"
|
||||
style="@style/Widget.Session.Button.Common.DestructiveOutline"
|
||||
style="@style/Widget.Session.Button.Common.DangerOutline"
|
||||
android:layout_width="196dp"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_alignParentBottom="true"
|
||||
|
@ -175,7 +175,7 @@
|
||||
android:src="@drawable/ic_baseline_call_end_24"
|
||||
android:padding="@dimen/medium_spacing"
|
||||
android:foregroundTint="@color/call_action_foreground"
|
||||
android:backgroundTint="@color/destructive"
|
||||
android:backgroundTint="?danger"
|
||||
android:layout_width="@dimen/large_button_height"
|
||||
android:layout_height="@dimen/large_button_height"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
@ -265,7 +265,7 @@
|
||||
android:src="@drawable/ic_baseline_call_end_24"
|
||||
android:padding="@dimen/medium_spacing"
|
||||
android:foregroundTint="@color/call_action_foreground"
|
||||
android:backgroundTint="@color/destructive"
|
||||
android:backgroundTint="?danger"
|
||||
android:layout_width="@dimen/large_button_height"
|
||||
android:layout_height="@dimen/large_button_height"
|
||||
android:layout_marginBottom="@dimen/very_large_spacing"
|
||||
|
@ -9,7 +9,7 @@
|
||||
android:layout_marginHorizontal="@dimen/large_spacing"
|
||||
android:layout_marginVertical="@dimen/medium_spacing"
|
||||
android:text="@string/blocked_contacts_title"
|
||||
android:textColor="@color/destructive"
|
||||
android:textColor="?danger"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
/>
|
||||
|
@ -44,7 +44,7 @@
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Session.Button.Dialog.DestructiveText"
|
||||
style="@style/Widget.Session.Button.Dialog.DangerText"
|
||||
android:id="@+id/clearAllDataButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/dialog_button_height"
|
||||
|
@ -43,7 +43,7 @@
|
||||
android:text="@string/cancel" />
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Session.Button.Dialog.DestructiveText"
|
||||
style="@style/Widget.Session.Button.Dialog.DangerText"
|
||||
android:id="@+id/sendSeedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/dialog_button_height"
|
||||
|
@ -62,7 +62,7 @@
|
||||
|
||||
<TextView
|
||||
style="@style/Widget.Session.Button.Common.ProminentFilled"
|
||||
android:backgroundTint="@color/destructive"
|
||||
android:backgroundTint="?danger"
|
||||
android:layout_marginHorizontal="@dimen/small_spacing"
|
||||
android:id="@+id/declineButton"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -5,7 +5,7 @@
|
||||
android:id="@+id/rootLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?conversation_menu_background_color">
|
||||
android:background="@drawable/sheet_rounded_bg">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleText"
|
||||
|
@ -14,14 +14,14 @@
|
||||
android:contentDescription="@string/AccessibilityId_delete_just_for_me"
|
||||
style="@style/BottomSheetActionItem"
|
||||
android:text="@string/delete_message_for_me"
|
||||
android:textColor="@color/destructive"/>
|
||||
android:textColor="?danger"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/deleteForEveryoneTextView"
|
||||
android:contentDescription="@string/delete_message_for_everyone"
|
||||
style="@style/BottomSheetActionItem"
|
||||
android:text="@string/delete_message_for_everyone"
|
||||
android:textColor="@color/destructive"/>
|
||||
android:textColor="?danger"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cancelTextView"
|
||||
|
@ -3,7 +3,7 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?conversation_menu_background_color">
|
||||
android:background="@drawable/sheet_rounded_bg">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleText"
|
||||
|
@ -46,7 +46,7 @@
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:textColor="@color/destructive"
|
||||
android:textColor="?danger"
|
||||
android:id="@+id/cancelButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -4,7 +4,7 @@
|
||||
android:id="@+id/rootLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?conversation_menu_background_color">
|
||||
android:background="@drawable/sheet_rounded_bg">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleText"
|
||||
|
@ -13,13 +13,13 @@
|
||||
android:layout_width="13dp"
|
||||
android:layout_height="13dp"
|
||||
android:src="@drawable/ic_baseline_block_24"
|
||||
app:tint="@color/destructive" />
|
||||
app:tint="?danger" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/destructive"
|
||||
android:textColor="?danger"
|
||||
android:textSize="16sp"
|
||||
android:text="@string/recipient_preferences__block"
|
||||
/>
|
||||
|
@ -31,7 +31,7 @@
|
||||
android:layout_height="wrap_content"/>
|
||||
<TextView
|
||||
android:text="@string/message_requests_clear_all"
|
||||
android:textColor="@color/destructive"
|
||||
android:textColor="?danger"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/header_view_clear_all"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -62,6 +62,7 @@
|
||||
android:textColor="?message_received_text_color"
|
||||
android:textAlignment="center"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
tools:text="You missed a call"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
@ -35,7 +35,7 @@
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:background="@drawable/circle_tintable"
|
||||
android:backgroundTint="@color/destructive" />
|
||||
android:backgroundTint="?danger" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/recordingViewDurationTextView"
|
||||
@ -121,7 +121,7 @@
|
||||
android:alpha="0.5"
|
||||
android:layout_centerInParent="true"
|
||||
android:background="@drawable/circle_tintable"
|
||||
android:backgroundTint="@color/destructive" />
|
||||
android:backgroundTint="?danger" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
@ -136,7 +136,7 @@
|
||||
android:layout_marginEnd="-8dp"
|
||||
android:layout_marginBottom="0dp"
|
||||
android:background="@drawable/circle_tintable"
|
||||
android:backgroundTint="@color/destructive" >
|
||||
android:backgroundTint="?danger" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/recordButtonOverlayImageView"
|
||||
|
@ -32,6 +32,7 @@
|
||||
<attr name="onLightCell" format="reference|color"/>
|
||||
|
||||
<attr name="accentColor" format="reference|color"/>
|
||||
<attr name="danger" format="reference|color"/>
|
||||
<attr name="backgroundSecondary" format="reference|color"/>
|
||||
<attr name="prominentButtonColor" format="reference|color"/>
|
||||
<attr name="elementBorderColor" format="reference|color"/>
|
||||
|
@ -1,6 +1,5 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<color name="destructive">#FF453A</color>
|
||||
<color name="unimportant">#D8D8D8</color>
|
||||
<color name="profile_picture_background">#353535</color>
|
||||
<color name="action_bar_background">#161616</color>
|
||||
|
@ -108,9 +108,9 @@
|
||||
<item name="android:textColor">?android:textColorPrimary</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Session.Button.Common.DestructiveOutline">
|
||||
<item name="android:background">@drawable/destructive_outline_button_medium_background</item>
|
||||
<item name="android:textColor">@color/button_destructive</item>
|
||||
<style name="Widget.Session.Button.Common.DangerOutline">
|
||||
<item name="android:background">@drawable/danger_outline_button_medium_background</item>
|
||||
<item name="android:textColor">@color/button_danger</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Session.Button.Dialog" parent="">
|
||||
@ -125,9 +125,9 @@
|
||||
<item name="android:background">@drawable/unimportant_dialog_text_button_background</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Session.Button.Dialog.DestructiveText">
|
||||
<item name="android:background">@drawable/destructive_dialog_text_button_background</item>
|
||||
<item name="android:textColor">@color/destructive</item>
|
||||
<style name="Widget.Session.Button.Dialog.DangerText">
|
||||
<item name="android:background">@drawable/danger_dialog_text_button_background</item>
|
||||
<item name="android:textColor">?danger</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Session.EditText.Compose" parent="@style/Signal.Text.Body">
|
||||
|
@ -58,7 +58,7 @@
|
||||
<item name="prominentButtonColor">?colorAccent</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="colorError">@color/destructive</item>
|
||||
<item name="colorError">?danger</item>
|
||||
</style>
|
||||
|
||||
<!-- This should be the default theme for the application. -->
|
||||
@ -318,6 +318,7 @@
|
||||
<item name="backgroundSecondary">@color/classic_dark_1</item>
|
||||
<item name="colorControlNormal">?android:textColorPrimary</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:textColorSecondary">?android:textColorPrimary</item>
|
||||
<item name="android:textColorTertiary">@color/classic_dark_5</item>
|
||||
@ -399,6 +400,7 @@
|
||||
<item name="colorPrimaryDark">@color/classic_light_6</item>
|
||||
<item name="colorControlNormal">?android:textColorPrimary</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:textColorSecondary">@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="colorControlNormal">@color/ocean_dark_7</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:textColorSecondary">@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="colorControlNormal">@color/ocean_light_1</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:textColorSecondary">@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="prominentButtonColor">?colorAccent</item>
|
||||
<item name="elementBorderColor">@color/classic_dark_3</item>
|
||||
<item name="danger">@color/danger_dark</item>
|
||||
|
||||
<!-- Home screen -->
|
||||
<item name="searchBackgroundColor">#1B1B1B</item>
|
||||
|
@ -25,7 +25,7 @@ import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.session.libsignal.utilities.guava.Optional
|
||||
import org.thoughtcrime.securesms.MainCoroutineRule
|
||||
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.database.GroupDatabase
|
||||
import org.thoughtcrime.securesms.database.Storage
|
||||
@ -87,7 +87,7 @@ class DisappearingMessagesViewModelTest {
|
||||
viewModel.uiState.value
|
||||
).isEqualTo(
|
||||
UiState(
|
||||
OptionsCard(
|
||||
OptionsCardData(
|
||||
R.string.activity_disappearing_messages_timer,
|
||||
typeOption(ExpiryMode.NONE, selected = true),
|
||||
timeOption(ExpiryType.AFTER_SEND, 12.hours),
|
||||
@ -126,7 +126,7 @@ class DisappearingMessagesViewModelTest {
|
||||
viewModel.uiState.value
|
||||
).isEqualTo(
|
||||
UiState(
|
||||
OptionsCard(
|
||||
OptionsCardData(
|
||||
R.string.activity_disappearing_messages_timer,
|
||||
typeOption(ExpiryMode.NONE, selected = false),
|
||||
timeOption(ExpiryType.LEGACY, 12.hours),
|
||||
@ -165,7 +165,7 @@ class DisappearingMessagesViewModelTest {
|
||||
viewModel.uiState.value
|
||||
).isEqualTo(
|
||||
UiState(
|
||||
OptionsCard(
|
||||
OptionsCardData(
|
||||
R.string.activity_disappearing_messages_timer,
|
||||
typeOption(ExpiryMode.NONE, selected = true),
|
||||
timeOption(ExpiryType.AFTER_SEND, 12.hours),
|
||||
@ -205,7 +205,7 @@ class DisappearingMessagesViewModelTest {
|
||||
viewModel.uiState.value
|
||||
).isEqualTo(
|
||||
UiState(
|
||||
OptionsCard(
|
||||
OptionsCardData(
|
||||
R.string.activity_disappearing_messages_timer,
|
||||
typeOption(ExpiryMode.NONE, enabled = false, selected = true),
|
||||
timeOption(ExpiryType.AFTER_SEND, 12.hours, enabled = false),
|
||||
@ -247,7 +247,7 @@ class DisappearingMessagesViewModelTest {
|
||||
viewModel.uiState.value
|
||||
).isEqualTo(
|
||||
UiState(
|
||||
OptionsCard(
|
||||
OptionsCardData(
|
||||
R.string.activity_disappearing_messages_delete_type,
|
||||
typeOption(ExpiryMode.NONE, selected = true),
|
||||
typeOption(12.hours, ExpiryType.AFTER_READ),
|
||||
@ -286,13 +286,13 @@ class DisappearingMessagesViewModelTest {
|
||||
viewModel.uiState.value
|
||||
).isEqualTo(
|
||||
UiState(
|
||||
OptionsCard(
|
||||
OptionsCardData(
|
||||
R.string.activity_disappearing_messages_delete_type,
|
||||
typeOption(ExpiryMode.NONE),
|
||||
typeOption(time, ExpiryType.AFTER_READ),
|
||||
typeOption(time, ExpiryType.AFTER_SEND, selected = true)
|
||||
),
|
||||
OptionsCard(
|
||||
OptionsCardData(
|
||||
R.string.activity_disappearing_messages_timer,
|
||||
timeOption(ExpiryType.AFTER_SEND, 12.hours, selected = true),
|
||||
timeOption(ExpiryType.AFTER_SEND, 1.days),
|
||||
@ -332,14 +332,14 @@ class DisappearingMessagesViewModelTest {
|
||||
viewModel.uiState.value
|
||||
).isEqualTo(
|
||||
UiState(
|
||||
OptionsCard(
|
||||
OptionsCardData(
|
||||
R.string.activity_disappearing_messages_delete_type,
|
||||
typeOption(ExpiryMode.NONE),
|
||||
typeOption(time, ExpiryType.LEGACY, selected = true),
|
||||
typeOption(12.hours, ExpiryType.AFTER_READ, enabled = false),
|
||||
typeOption(1.days, ExpiryType.AFTER_SEND, enabled = false)
|
||||
),
|
||||
OptionsCard(
|
||||
OptionsCardData(
|
||||
R.string.activity_disappearing_messages_timer,
|
||||
timeOption(ExpiryType.LEGACY, 12.hours, selected = true),
|
||||
timeOption(ExpiryType.LEGACY, 1.days),
|
||||
@ -379,13 +379,13 @@ class DisappearingMessagesViewModelTest {
|
||||
viewModel.uiState.value
|
||||
).isEqualTo(
|
||||
UiState(
|
||||
OptionsCard(
|
||||
OptionsCardData(
|
||||
R.string.activity_disappearing_messages_delete_type,
|
||||
typeOption(ExpiryMode.NONE),
|
||||
typeOption(12.hours, ExpiryType.AFTER_READ),
|
||||
typeOption(time, ExpiryType.AFTER_SEND, selected = true)
|
||||
),
|
||||
OptionsCard(
|
||||
OptionsCardData(
|
||||
R.string.activity_disappearing_messages_timer,
|
||||
timeOption(ExpiryType.AFTER_SEND, 12.hours),
|
||||
timeOption(ExpiryType.AFTER_SEND, 1.days, selected = true),
|
||||
@ -426,13 +426,13 @@ class DisappearingMessagesViewModelTest {
|
||||
viewModel.uiState.value
|
||||
).isEqualTo(
|
||||
UiState(
|
||||
OptionsCard(
|
||||
OptionsCardData(
|
||||
R.string.activity_disappearing_messages_delete_type,
|
||||
typeOption(ExpiryMode.NONE),
|
||||
typeOption(1.days, ExpiryType.AFTER_READ, selected = true),
|
||||
typeOption(time, ExpiryType.AFTER_SEND)
|
||||
),
|
||||
OptionsCard(
|
||||
OptionsCardData(
|
||||
R.string.activity_disappearing_messages_timer,
|
||||
timeOption(ExpiryType.AFTER_READ, 5.minutes),
|
||||
timeOption(ExpiryType.AFTER_READ, 1.hours),
|
||||
@ -479,13 +479,13 @@ class DisappearingMessagesViewModelTest {
|
||||
viewModel.uiState.value
|
||||
).isEqualTo(
|
||||
UiState(
|
||||
OptionsCard(
|
||||
OptionsCardData(
|
||||
R.string.activity_disappearing_messages_delete_type,
|
||||
typeOption(ExpiryMode.NONE),
|
||||
typeOption(12.hours, ExpiryType.AFTER_READ),
|
||||
typeOption(1.days, ExpiryType.AFTER_SEND, selected = true)
|
||||
),
|
||||
OptionsCard(
|
||||
OptionsCardData(
|
||||
R.string.activity_disappearing_messages_timer,
|
||||
timeOption(ExpiryType.AFTER_SEND, 12.hours),
|
||||
timeOption(ExpiryType.AFTER_SEND, 1.days, selected = true),
|
||||
|
@ -1,6 +1,5 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<color name="destructive">#FF453A</color>
|
||||
<color name="unimportant">#D8D8D8</color>
|
||||
<color name="profile_picture_background">#353535</color>
|
||||
<color name="cell_background">#1B1B1B</color>
|
||||
|
Loading…
Reference in New Issue
Block a user