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