Improve themes, styling and animations

This commit is contained in:
Andrew 2024-05-01 15:18:00 +09:30
parent 89d93bc80d
commit ff64a8bc13
16 changed files with 428 additions and 368 deletions

View File

@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.conversation.disappearingmessages.ui
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@ -24,8 +25,8 @@ import org.thoughtcrime.securesms.ui.Callbacks
import org.thoughtcrime.securesms.ui.GetString
import org.thoughtcrime.securesms.ui.NoOpCallbacks
import org.thoughtcrime.securesms.ui.OptionsCard
import org.thoughtcrime.securesms.ui.OutlineButton
import org.thoughtcrime.securesms.ui.RadioOption
import org.thoughtcrime.securesms.ui.components.OutlineButton
import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.fadingEdges
@ -64,9 +65,9 @@ fun DisappearingMessages(
}
if (state.showSetButton) OutlineButton(
GetString(R.string.disappearing_messages_set_button_title),
textId = R.string.disappearing_messages_set_button_title,
modifier = Modifier
.contentDescription(GetString(R.string.AccessibilityId_set_button))
.contentDescription(R.string.AccessibilityId_set_button)
.align(Alignment.CenterHorizontally)
.padding(bottom = 20.dp),
onClick = callbacks::onSetClick

View File

@ -1,12 +1,10 @@
package org.thoughtcrime.securesms.dms
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
@ -17,13 +15,13 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
@ -41,14 +39,16 @@ import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.showOpenUrlDialog
import org.thoughtcrime.securesms.ui.AppTheme
import org.thoughtcrime.securesms.ui.BorderlessButtonSecondary
import org.thoughtcrime.securesms.ui.OutlineButton
import org.thoughtcrime.securesms.ui.LoadingArcOr
import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
import org.thoughtcrime.securesms.ui.components.AppBar
import org.thoughtcrime.securesms.ui.components.BorderlessButtonSecondary
import org.thoughtcrime.securesms.ui.components.MaybeScanQrCode
import org.thoughtcrime.securesms.ui.components.OutlineButton
import org.thoughtcrime.securesms.ui.components.SessionOutlinedTextField
import org.thoughtcrime.securesms.ui.components.SessionTabRow
import org.thoughtcrime.securesms.ui.contentDescription
@AndroidEntryPoint
class NewMessageFragment : Fragment() {
@ -157,12 +157,16 @@ fun EnterAccountId(
Spacer(modifier = Modifier.weight(1f))
OutlineButton(
text = stringResource(id = R.string.continue_2),
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(horizontal = 64.dp, vertical = 20.dp)
.width(200.dp),
loading = state.loading
) { callbacks.onContinue() }
.width(200.dp)
.contentDescription(R.string.continue_2),
onClick = { callbacks.onContinue() }
) {
LoadingArcOr(state.loading) {
Text(stringResource(R.string.continue_2))
}
}
}
}

View File

@ -98,11 +98,11 @@ import org.thoughtcrime.securesms.preferences.SettingsActivity
import org.thoughtcrime.securesms.showMuteDialog
import org.thoughtcrime.securesms.showSessionDialog
import org.thoughtcrime.securesms.ui.AppTheme
import org.thoughtcrime.securesms.ui.GetString
import org.thoughtcrime.securesms.ui.OutlineButton
import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.SessionShieldIcon
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
import org.thoughtcrime.securesms.ui.components.OutlineButton
import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.h8
import org.thoughtcrime.securesms.ui.small
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
@ -372,10 +372,11 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
}
Spacer(Modifier.width(12.dp))
OutlineButton(
stringResource(R.string.continue_2),
Modifier.align(Alignment.CenterVertically),
contentDescription = GetString(R.string.AccessibilityId_reveal_recovery_phrase_button)
) { start<RecoveryPasswordActivity>() }
textId = R.string.continue_2,
Modifier.align(Alignment.CenterVertically)
.contentDescription(R.string.AccessibilityId_reveal_recovery_phrase_button),
onClick = { start<RecoveryPasswordActivity>() }
)
}
}
}

View File

@ -32,15 +32,14 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.thoughtcrime.securesms.onboarding.pickname.startPickDisplayNameActivity
import org.thoughtcrime.securesms.service.KeyCachingService
import org.thoughtcrime.securesms.showOpenUrlDialog
import org.thoughtcrime.securesms.showSessionDialog
import org.thoughtcrime.securesms.ui.AppTheme
import org.thoughtcrime.securesms.ui.BorderlessButton
import org.thoughtcrime.securesms.ui.FilledButton
import org.thoughtcrime.securesms.ui.GetString
import org.thoughtcrime.securesms.ui.OutlineButton
import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
import org.thoughtcrime.securesms.ui.classicDarkColors
import org.thoughtcrime.securesms.ui.components.BorderlessButton
import org.thoughtcrime.securesms.ui.components.FilledButton
import org.thoughtcrime.securesms.ui.components.OutlineButton
import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.session_accent
import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo
@ -94,28 +93,30 @@ class LandingActivity : BaseActionBarActivity() {
OutlineButton(
text = stringResource(R.string.onboardingAccountCreate),
modifier = Modifier
.contentDescription(R.string.AccessibilityId_create_account_button)
.width(262.dp)
.align(Alignment.CenterHorizontally),
contentDescription = GetString(R.string.AccessibilityId_create_account_button)
) { startPickDisplayNameActivity() }
onClick = ::startPickDisplayNameActivity
)
Spacer(modifier = Modifier.height(14.dp))
FilledButton(
text = stringResource(R.string.onboardingAccountExists),
modifier = Modifier
.width(262.dp)
.align(Alignment.CenterHorizontally),
contentDescription = GetString(R.string.AccessibilityId_restore_account_button)
.align(Alignment.CenterHorizontally)
.contentDescription(R.string.AccessibilityId_restore_account_button)
) { startLinkDeviceActivity() }
Spacer(modifier = Modifier.height(8.dp))
BorderlessButton(
text = stringResource(R.string.onboardingTosPrivacy),
modifier = Modifier
.width(262.dp)
.align(Alignment.CenterHorizontally),
contentDescription = GetString(R.string.AccessibilityId_privacy_policy_link),
.align(Alignment.CenterHorizontally)
.contentDescription(R.string.AccessibilityId_privacy_policy_link),
fontSize = 11.sp,
lineHeight = 13.sp
) { openDialog() }
lineHeight = 13.sp,
onClick = ::openDialog
)
Spacer(modifier = Modifier.height(8.dp))
}
}
@ -136,32 +137,33 @@ class LandingActivity : BaseActionBarActivity() {
private fun open(url: String) {
Intent(Intent.ACTION_VIEW, Uri.parse(url)).let(::startActivity)
}
}
@Composable
private fun IncomingText(text: String) {
@Composable
private fun IncomingText(text: String) {
ChatText(
text,
color = classicDarkColors[2]
)
}
}
@Composable
private fun ColumnScope.OutgoingText(text: String) {
@Composable
private fun ColumnScope.OutgoingText(text: String) {
ChatText(
text,
color = session_accent,
textColor = MaterialTheme.colors.primary,
modifier = Modifier.align(Alignment.End)
)
}
}
@Composable
private fun ChatText(
@Composable
private fun ChatText(
text: String,
color: Color,
modifier: Modifier = Modifier,
textColor: Color = Color.Unspecified
) {
) {
Text(
text,
fontSize = 16.sp,
@ -175,5 +177,4 @@ class LandingActivity : BaseActionBarActivity() {
)
.padding(horizontal = 16.dp, vertical = 12.dp)
)
}
}

View File

@ -40,9 +40,9 @@ import network.loki.messenger.R
import org.session.libsession.utilities.TextSecurePreferences
import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.ui.AppTheme
import org.thoughtcrime.securesms.ui.OutlineButton
import org.thoughtcrime.securesms.ui.baseBold
import org.thoughtcrime.securesms.ui.components.MaybeScanQrCode
import org.thoughtcrime.securesms.ui.components.OutlineButton
import org.thoughtcrime.securesms.ui.components.SessionOutlinedTextField
import org.thoughtcrime.securesms.ui.components.SessionTabRow
import org.thoughtcrime.securesms.ui.contentDescription
@ -150,12 +150,13 @@ fun RecoveryPassword(state: LinkDeviceState, onChange: (String) -> Unit = {}, on
}
Spacer(Modifier.weight(2f))
OutlineButton(
text = stringResource(id = R.string.continue_2),
textId = R.string.continue_2,
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(horizontal = 64.dp, vertical = 20.dp)
.width(200.dp)
) { onContinue() }
.width(200.dp),
onClick = onContinue
)
}
}

View File

@ -38,10 +38,9 @@ import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.home.HomeActivity
import org.thoughtcrime.securesms.notifications.PushRegistry
import org.thoughtcrime.securesms.ui.AppTheme
import org.thoughtcrime.securesms.ui.GetString
import org.thoughtcrime.securesms.ui.OutlineButton
import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
import org.thoughtcrime.securesms.ui.components.OutlineButton
import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.h8
import org.thoughtcrime.securesms.ui.h9
@ -127,7 +126,7 @@ fun MessageNotificationsScreen(
)
Spacer(Modifier.weight(1f))
OutlineButton(
stringResource(R.string.continue_2),
textId = R.string.continue_2,
modifier = Modifier
.align(Alignment.CenterHorizontally)
.width(262.dp),

View File

@ -36,10 +36,10 @@ import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.onboarding.messagenotifications.startPNModeActivity
import org.thoughtcrime.securesms.ui.AppTheme
import org.thoughtcrime.securesms.ui.OutlineButton
import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.base
import org.thoughtcrime.securesms.ui.baseBold
import org.thoughtcrime.securesms.ui.components.OutlineButton
import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.outlinedTextFieldColors
import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo
@ -129,11 +129,12 @@ class PickDisplayNameActivity : BaseActionBarActivity() {
Spacer(modifier = Modifier.weight(2f))
OutlineButton(
stringResource(R.string.continue_2),
textId = R.string.continue_2,
modifier = Modifier
.align(Alignment.CenterHorizontally)
.width(262.dp)
) { onContinue() }
.width(262.dp),
onClick = onContinue,
)
}
}
}

View File

@ -1,8 +1,5 @@
package org.thoughtcrime.securesms.onboarding.recoverypassword
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import androidx.compose.animation.AnimatedVisibility
@ -13,15 +10,19 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
@ -35,15 +36,16 @@ import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.showSessionDialog
import org.thoughtcrime.securesms.ui.AppTheme
import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin
import org.thoughtcrime.securesms.ui.GetString
import org.thoughtcrime.securesms.ui.LocalExtraColors
import org.thoughtcrime.securesms.ui.OutlineButton
import org.thoughtcrime.securesms.ui.LocalButtonColor
import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.SessionShieldIcon
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
import org.thoughtcrime.securesms.ui.classicDarkColors
import org.thoughtcrime.securesms.ui.colorDestructive
import org.thoughtcrime.securesms.ui.components.OutlineButton
import org.thoughtcrime.securesms.ui.components.QrImageCard
import org.thoughtcrime.securesms.ui.components.SmallButtons
import org.thoughtcrime.securesms.ui.components.TemporaryStateButton
import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.h8
import org.thoughtcrime.securesms.ui.small
@ -114,15 +116,17 @@ fun RecoveryPassword(
.verticalScroll(rememberScrollState())
.padding(bottom = 16.dp)
) {
SmallButtons {
RecoveryPasswordCell(seed, copySeed)
HideRecoveryPasswordCell(onHide)
}
}
}
}
@Composable
fun RecoveryPasswordCell(seed: String, copySeed:() -> Unit = {}) {
val showQr = remember {
var showQr by remember {
mutableStateOf(false)
}
@ -136,7 +140,7 @@ fun RecoveryPasswordCell(seed: String, copySeed:() -> Unit = {}) {
Text(stringResource(R.string.recoveryPasswordDescription))
AnimatedVisibility(!showQr.value) {
AnimatedVisibility(!showQr) {
Text(
seed,
modifier = Modifier
@ -149,62 +153,70 @@ fun RecoveryPasswordCell(seed: String, copySeed:() -> Unit = {}) {
)
.padding(24.dp),
style = MaterialTheme.typography.small.copy(fontFamily = FontFamily.Monospace),
color = LocalExtraColors.current.prominentButtonColor,
color = MaterialTheme.colors.run { if (isLight) onSurface else secondary },
)
}
AnimatedVisibility(
showQr.value,
modifier = Modifier.align(Alignment.CenterHorizontally).padding(vertical = 24.dp)
showQr,
modifier = Modifier.align(Alignment.CenterHorizontally)
) {
QrImageCard(
seed,
modifier = Modifier.padding(vertical = 24.dp),
contentDescription = "QR code of your recovery password",
icon = R.drawable.session_shield
)
}
AnimatedVisibility(!showQr.value) {
AnimatedVisibility(!showQr) {
Row(horizontalArrangement = Arrangement.spacedBy(32.dp)) {
TemporaryStateButton { source, temporary ->
OutlineButton(
modifier = Modifier.weight(1f),
color = MaterialTheme.colors.onPrimary,
onClick = copySeed,
temporaryContent = { Text(stringResource(R.string.copied)) }
interactionSource = source,
onClick = { copySeed() },
) {
Text(stringResource(R.string.copy))
AnimatedVisibility(temporary) { Text(stringResource(R.string.copied)) }
AnimatedVisibility(!temporary) { Text(stringResource(R.string.copy)) }
}
OutlineButton(text = stringResource(R.string.qrView), modifier = Modifier.weight(1f), color = MaterialTheme.colors.onPrimary) { showQr.toggle() }
}
OutlineButton(textId = R.string.qrView, modifier = Modifier.weight(1f), onClick = { showQr = !showQr })
}
}
AnimatedVisibility(showQr.value, modifier = Modifier.align(Alignment.CenterHorizontally)) {
AnimatedVisibility(showQr, modifier = Modifier.align(Alignment.CenterHorizontally)) {
OutlineButton(
text = stringResource(R.string.recoveryPasswordView),
color = MaterialTheme.colors.onPrimary,
modifier = Modifier.align(Alignment.CenterHorizontally)
) { showQr.toggle() }
textId = R.string.recoveryPasswordView,
onClick = { showQr = !showQr }
)
}
}
}
}
private fun MutableState<Boolean>.toggle() { value = !value }
@Composable
fun HideRecoveryPasswordCell(onHide: () -> Unit = {}) {
CellWithPaddingAndMargin {
Row {
Column(Modifier.weight(1f)) {
Column(
Modifier.weight(1f)
) {
Text(text = stringResource(R.string.recoveryPasswordHideRecoveryPassword), style = MaterialTheme.typography.h8)
Text(text = stringResource(R.string.recoveryPasswordHideRecoveryPasswordDescription))
}
CompositionLocalProvider(
LocalButtonColor provides colorDestructive,
) {
OutlineButton(
stringResource(R.string.hide),
contentDescription = GetString(R.string.AccessibilityId_hide_recovery_password_button),
modifier = Modifier.align(Alignment.CenterVertically),
color = colorDestructive
) { onHide() }
textId = R.string.hide,
modifier = Modifier
.wrapContentWidth()
.align(Alignment.CenterVertically)
.contentDescription(R.string.AccessibilityId_hide_recovery_password_button),
onClick = onHide
)
}
}
}
}

View File

@ -19,12 +19,15 @@ import android.view.View
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.interaction.InteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
@ -84,19 +87,16 @@ import org.thoughtcrime.securesms.preferences.appearance.AppearanceSettingsActiv
import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints
import org.thoughtcrime.securesms.showSessionDialog
import org.thoughtcrime.securesms.ui.AppTheme
import org.thoughtcrime.securesms.ui.BorderlessButton
import org.thoughtcrime.securesms.ui.Cell
import org.thoughtcrime.securesms.ui.Divider
import org.thoughtcrime.securesms.ui.ItemButton
import org.thoughtcrime.securesms.ui.ItemButtonWithDrawable
import org.thoughtcrime.securesms.ui.OutlineButton
import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
import org.thoughtcrime.securesms.ui.components.OutlineButton
import org.thoughtcrime.securesms.ui.components.TemporaryStateButton
import org.thoughtcrime.securesms.ui.destructiveButtonColors
import org.thoughtcrime.securesms.util.BitmapDecodingException
import org.thoughtcrime.securesms.util.BitmapUtil
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
import org.thoughtcrime.securesms.util.disableClipping
import org.thoughtcrime.securesms.util.push
import org.thoughtcrime.securesms.util.show
import java.io.File
@ -429,20 +429,25 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
fun Buttons() {
Column {
Row(
modifier = Modifier.padding(horizontal = 24.dp),
modifier = Modifier.padding(horizontal = 24.dp).padding(top = 8.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
) {
OutlineButton(
modifier = Modifier.weight(1f),
onClick = { sharePublicKey() }
) { Text(stringResource(R.string.share)) }
onClick = ::sharePublicKey
) {
Text(stringResource(R.string.share))
}
TemporaryStateButton { source, temporary ->
OutlineButton(
modifier = Modifier.weight(1f),
interactionSource = source,
onClick = { copyPublicKey() },
temporaryContent = { Text(stringResource(R.string.copied)) }
) {
Text(stringResource(R.string.copy))
AnimatedVisibility(temporary) { Text(stringResource(R.string.copied)) }
AnimatedVisibility(!temporary) { Text(stringResource(R.string.copy)) }
}
}
}

View File

@ -5,7 +5,6 @@ import android.os.Parcelable
import android.util.SparseArray
import android.view.View
import androidx.activity.viewModels
import androidx.appcompat.widget.SwitchCompat
import androidx.core.view.children
import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
@ -31,8 +30,8 @@ class AppearanceSettingsActivity: PassphraseRequiredActionBarActivity(), View.On
var currentTheme: ThemeState? = null
private val accentColors
get() = mapOf(
private val accentColors by lazy {
mapOf(
binding.accentGreen to R.style.PrimaryGreen,
binding.accentBlue to R.style.PrimaryBlue,
binding.accentYellow to R.style.PrimaryYellow,
@ -41,9 +40,10 @@ class AppearanceSettingsActivity: PassphraseRequiredActionBarActivity(), View.On
binding.accentOrange to R.style.PrimaryOrange,
binding.accentRed to R.style.PrimaryRed
)
}
private val themeViews
get() = listOf(
private val themeViews by lazy {
listOf(
binding.themeOptionClassicDark,
binding.themeRadioClassicDark,
binding.themeOptionClassicLight,
@ -53,6 +53,7 @@ class AppearanceSettingsActivity: PassphraseRequiredActionBarActivity(), View.On
binding.themeOptionOceanLight,
binding.themeRadioOceanLight
)
}
override fun onClick(v: View?) {
v ?: return

View File

@ -1,14 +1,13 @@
package org.thoughtcrime.securesms.ui
import android.graphics.drawable.BitmapDrawable
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.Image
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.clickable
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
@ -27,7 +26,6 @@ import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Button
import androidx.compose.material.ButtonColors
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Card
@ -35,12 +33,14 @@ import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Colors
import androidx.compose.material.ContentAlpha
import androidx.compose.material.Icon
import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedButton
import androidx.compose.material.RadioButton
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -49,6 +49,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Size
@ -57,7 +58,6 @@ import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalContext
@ -71,10 +71,9 @@ import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.res.ResourcesCompat
import com.google.accompanist.drawablepainter.rememberDrawablePainter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import network.loki.messenger.R
import org.session.libsession.utilities.recipients.Recipient
@ -83,155 +82,6 @@ import org.thoughtcrime.securesms.components.ProfilePictureView
import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.OptionsCard
import kotlin.math.min
import kotlin.math.roundToInt
import kotlin.time.Duration.Companion.seconds
@Composable
fun OutlineButton(
text: String,
modifier: Modifier = Modifier,
contentDescription: GetString = GetString(text),
color: Color = LocalExtraColors.current.prominentButtonColor,
loading: Boolean = false,
onClick: () -> Unit
) {
OutlinedButton(
modifier = modifier.contentDescription(contentDescription),
onClick = onClick,
border = BorderStroke(1.dp, color),
shape = RoundedCornerShape(50), // = 50% percent
colors = ButtonDefaults.outlinedButtonColors(
contentColor = color,
backgroundColor = Color.Unspecified
)
) {
AnimatedVisibility(loading) {
CircularProgressIndicator(
modifier = Modifier.size(20.dp),
color = color,
strokeWidth = 2.dp
)
}
AnimatedVisibility(!loading) { Text(text = text) }
}
}
@Composable
fun OutlineButton(
modifier: Modifier = Modifier,
color: Color = LocalExtraColors.current.prominentButtonColor,
onClick: () -> Unit = {},
content: @Composable () -> Unit = {}
) {
OutlinedButton(
modifier = modifier,
onClick = onClick,
border = BorderStroke(1.dp, color),
shape = RoundedCornerShape(percent = 50),
colors = ButtonDefaults.outlinedButtonColors(
contentColor = color,
backgroundColor = Color.Unspecified
)
) {
content()
}
}
@Composable
fun OutlineButton(
temporaryContent: @Composable () -> Unit,
modifier: Modifier = Modifier,
color: Color = LocalExtraColors.current.prominentButtonColor,
onClick: () -> Unit = {},
content: @Composable () -> Unit = {}
) {
var clicked by remember { mutableStateOf(false) }
if (clicked) LaunchedEffectAsync {
delay(2.seconds)
clicked = false
}
OutlinedButton(
modifier = modifier,
onClick = {
onClick()
clicked = true
},
border = BorderStroke(1.dp, color),
shape = RoundedCornerShape(percent = 50),
colors = ButtonDefaults.outlinedButtonColors(
contentColor = color,
backgroundColor = Color.Unspecified
)
) {
AnimatedVisibility(clicked) {
temporaryContent()
}
AnimatedVisibility(!clicked) {
content()
}
}
}
@Composable
fun FilledButton(
text: String,
modifier: Modifier = Modifier,
contentDescription: GetString? = GetString(text),
onClick: () -> Unit) {
OutlinedButton(
modifier = modifier.size(108.dp, 34.dp),
onClick = onClick,
shape = RoundedCornerShape(50), // = 50% percent
colors = ButtonDefaults.outlinedButtonColors(
contentColor = MaterialTheme.colors.background,
backgroundColor = LocalExtraColors.current.prominentButtonColor
)
) {
Text(text = text)
}
}
@Composable
fun BorderlessButtonSecondary(
text: String,
onClick: () -> Unit
) {
BorderlessButton(
text,
contentColor = MaterialTheme.colors.onSurface.copy(ContentAlpha.medium),
onClick = onClick
)
}
@Composable
fun BorderlessButton(
text: String,
modifier: Modifier = Modifier,
contentDescription: GetString = GetString(text),
fontSize: TextUnit = TextUnit.Unspecified,
lineHeight: TextUnit = TextUnit.Unspecified,
contentColor: Color = MaterialTheme.colors.onBackground,
backgroundColor: Color = Color.Transparent,
onClick: () -> Unit
) {
TextButton(
onClick = onClick,
modifier = modifier.contentDescription(contentDescription),
shape = RoundedCornerShape(percent = 50),
colors = ButtonDefaults.outlinedButtonColors(
contentColor = contentColor,
backgroundColor = backgroundColor
)
) {
Text(
text = text,
textAlign = TextAlign.Center,
fontSize = fontSize,
lineHeight = lineHeight,
modifier = Modifier.padding(horizontal = 2.dp)
)
}
}
interface Callbacks<in T> {
fun onSetClick(): Any?
@ -367,8 +217,9 @@ fun CellWithPaddingAndMargin(
margin: Dp = 32.dp,
content: @Composable () -> Unit
) {
CompositionLocalProvider(LocalButtonColor provides MaterialTheme.colors.onPrimary) {
Card(
backgroundColor = MaterialTheme.colors.cellColor,
backgroundColor = LocalCellColor.current,
shape = RoundedCornerShape(16.dp),
elevation = 0.dp,
modifier = Modifier
@ -378,6 +229,7 @@ fun CellWithPaddingAndMargin(
) {
Box(Modifier.padding(padding)) { content() }
}
}
}
@Composable
@ -434,27 +286,10 @@ fun Modifier.contentDescription(id: Int?): Modifier {
}
@Composable
fun OutlineButton(text: GetString, contentDescription: GetString? = text, modifier: Modifier = Modifier, onClick: () -> Unit) {
OutlinedButton(
modifier = modifier
.size(108.dp, 34.dp)
.contentDescription(contentDescription),
onClick = onClick,
border = BorderStroke(1.dp, LocalExtraColors.current.prominentButtonColor),
shape = RoundedCornerShape(50), // = 50% percent
colors = ButtonDefaults.outlinedButtonColors(
contentColor = LocalExtraColors.current.prominentButtonColor,
backgroundColor = MaterialTheme.colors.background
)
){
Text(text = text())
}
fun Modifier.contentDescription(text: String?): Modifier {
return text?.let { semantics { contentDescription = it } } ?: this
}
private val Colors.cellColor: Color
@Composable
get() = LocalExtraColors.current.settingsBackground
fun Modifier.fadingEdges(
scrollState: ScrollState,
topEdgeHeight: Dp = 0.dp,
@ -577,5 +412,20 @@ fun RowScope.SessionShieldIcon() {
@Composable
fun LaunchedEffectAsync(block: suspend CoroutineScope.() -> Unit) {
rememberCoroutineScope().apply { LaunchedEffect(Unit) { launch { block() } } }
val scope = rememberCoroutineScope()
LaunchedEffect(Unit) { scope.launch(Dispatchers.IO) { block() } }
}
@Composable
fun LoadingArcOr(loading: Boolean, content: @Composable () -> Unit) {
AnimatedVisibility(loading) {
CircularProgressIndicator(
modifier = Modifier.size(20.dp),
color = LocalContentColor.current,
strokeWidth = 2.dp
)
}
AnimatedVisibility(!loading) {
content()
}
}

View File

@ -27,15 +27,10 @@ import com.google.accompanist.themeadapter.appcompat.createAppCompatTheme
import com.google.android.material.color.MaterialColors
import network.loki.messenger.R
val LocalExtraColors = staticCompositionLocalOf<ExtraColors> { error("No Custom Attribute value provided") }
data class ExtraColors(
val settingsBackground: Color,
val prominentButtonColor: Color,
val lightCell: Color,
val onLightCell: Color,
)
val LocalCellColor = staticCompositionLocalOf { Color.Black }
val LocalButtonColor = staticCompositionLocalOf { Color.Black }
val LocalLightCell = staticCompositionLocalOf { Color.Black }
val LocalOnLightCell = staticCompositionLocalOf { Color.Black }
/**
* Converts current Theme to Compose Theme.
@ -46,19 +41,16 @@ fun AppTheme(
) {
val context = LocalContext.current
val extraColors = context.run {
ExtraColors(
settingsBackground = getColorFromTheme(R.attr.colorSettingsBackground),
prominentButtonColor = getColorFromTheme(R.attr.prominentButtonColor),
lightCell = getColorFromTheme(R.attr.lightCell),
onLightCell = getColorFromTheme(R.attr.onLightCell),
)
}
val surface = context.getColorFromTheme(R.attr.colorSettingsBackground)
CompositionLocalProvider(LocalExtraColors provides extraColors) {
CompositionLocalProvider(
*listOf(
LocalCellColor to R.attr.colorSettingsBackground,
LocalButtonColor to R.attr.prominentButtonColor,
LocalLightCell to R.attr.lightCell,
LocalOnLightCell to R.attr.onLightCell
).map { (local, attr) -> local provides context.getColorFromTheme(attr) }.toTypedArray()
) {
AppCompatTheme(surface = surface) {
CompositionLocalProvider(LocalTextSelectionColors provides TextSelectionColors(
handleColor = MaterialTheme.colors.secondary,

View File

@ -0,0 +1,174 @@
package org.thoughtcrime.securesms.ui.components
import androidx.annotation.StringRes
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.ContentAlpha
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filter
import org.thoughtcrime.securesms.ui.GetString
import org.thoughtcrime.securesms.ui.LaunchedEffectAsync
import org.thoughtcrime.securesms.ui.LocalButtonColor
import org.thoughtcrime.securesms.ui.colorDestructive
import org.thoughtcrime.securesms.ui.contentDescription
import kotlin.time.Duration.Companion.seconds
val LocalButtonSize = staticCompositionLocalOf { mediumButton }
val LocalButtonShape = staticCompositionLocalOf<Shape> { RoundedCornerShape(percent = 50) }
@Composable
fun Modifier.applyButtonSize() = then(LocalButtonSize.current)
val mediumButton = Modifier.height(41.dp)
val smallButton = Modifier.wrapContentHeight()
@Composable
fun OutlineButton(@StringRes textId: Int, modifier: Modifier = Modifier, onClick: () -> Unit) {
OutlineButton(stringResource(textId), modifier, onClick)
}
@Composable
fun OutlineButton(text: String, modifier: Modifier = Modifier, onClick: () -> Unit) {
OutlineButton(modifier.contentDescription(text), onClick = onClick) { Text(text) }
}
@Composable
fun OutlineButton(
modifier: Modifier = Modifier,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
onClick: () -> Unit,
content: @Composable () -> Unit = {}
) {
OutlinedButton(
modifier = modifier.applyButtonSize(),
interactionSource = interactionSource,
onClick = onClick,
border = BorderStroke(1.dp, LocalButtonColor.current),
shape = LocalButtonShape.current,
colors = ButtonDefaults.outlinedButtonColors(
contentColor = LocalButtonColor.current,
backgroundColor = Color.Unspecified
)
) {
content()
}
}
@Composable
fun TemporaryStateButton(
content: @Composable (MutableInteractionSource, Boolean) -> Unit,
) {
val interactions = remember { MutableInteractionSource() }
var clicked by remember { mutableStateOf(false) }
content(interactions, clicked)
LaunchedEffectAsync {
interactions.releases.collectLatest {
clicked = true
delay(2.seconds)
clicked = false
}
}
}
@Composable
fun FilledButton(
text: String,
modifier: Modifier = Modifier,
onClick: () -> Unit
) {
OutlinedButton(
modifier = modifier.size(108.dp, 34.dp),
onClick = onClick,
shape = RoundedCornerShape(50),
colors = ButtonDefaults.outlinedButtonColors(
contentColor = MaterialTheme.colors.background,
backgroundColor = LocalButtonColor.current
)
) {
Text(text = text)
}
}
@Composable
fun BorderlessButtonSecondary(
text: String,
onClick: () -> Unit
) {
BorderlessButton(
text,
contentColor = MaterialTheme.colors.onSurface.copy(ContentAlpha.medium),
onClick = onClick
)
}
@Composable
fun BorderlessButton(
text: String,
modifier: Modifier = Modifier,
contentDescription: GetString = GetString(text),
fontSize: TextUnit = TextUnit.Unspecified,
lineHeight: TextUnit = TextUnit.Unspecified,
contentColor: Color = MaterialTheme.colors.onBackground,
backgroundColor: Color = Color.Transparent,
onClick: () -> Unit
) {
TextButton(
onClick = onClick,
modifier = modifier.contentDescription(contentDescription),
shape = RoundedCornerShape(percent = 50),
colors = ButtonDefaults.outlinedButtonColors(
contentColor = contentColor,
backgroundColor = backgroundColor
)
) {
Text(
text = text,
textAlign = TextAlign.Center,
fontSize = fontSize,
lineHeight = lineHeight,
modifier = Modifier.padding(horizontal = 2.dp)
)
}
}
private val MutableInteractionSource.releases
get() = interactions.filter { it is PressInteraction.Release }
@Composable
fun SmallButtons(content: @Composable () -> Unit) {
CompositionLocalProvider(LocalButtonSize provides smallButton) { content() }
}
@Composable
fun DestructiveButtons(content: @Composable () -> Unit) {
CompositionLocalProvider(LocalButtonColor provides colorDestructive) { content() }
}

View File

@ -52,7 +52,6 @@ import kotlinx.coroutines.flow.emptyFlow
import network.loki.messenger.R
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.onboarding.Analyzer
import org.thoughtcrime.securesms.ui.OutlineButton
import java.util.concurrent.Executors
typealias CameraPreview = androidx.camera.core.Preview
@ -96,17 +95,18 @@ fun MaybeScanQrCode(
)
Spacer(modifier = Modifier.size(20.dp))
OutlineButton(
text = stringResource(R.string.sessionSettings),
modifier = Modifier.align(Alignment.CenterHorizontally),
onClick = onClickSettings
)
) {
Text(stringResource(R.string.sessionSettings))
}
}
} else {
OutlineButton(
text = stringResource(R.string.cameraGrantAccess),
modifier = Modifier.align(Alignment.Center)
modifier = Modifier.align(Alignment.Center),
onClick = { cameraPermissionState.run { launchPermissionRequest() } }
) {
cameraPermissionState.run { launchPermissionRequest() }
Text(stringResource(R.string.cameraGrantAccess))
}
}
}

View File

@ -1,9 +1,13 @@
package org.thoughtcrime.securesms.ui.components
import android.graphics.Bitmap
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.scaleIn
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
@ -25,7 +29,8 @@ import androidx.compose.ui.unit.dp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import network.loki.messenger.R
import org.thoughtcrime.securesms.ui.LocalExtraColors
import org.thoughtcrime.securesms.ui.LocalLightCell
import org.thoughtcrime.securesms.ui.LocalOnLightCell
import org.thoughtcrime.securesms.util.QRCodeUtilities
@Composable
@ -36,7 +41,7 @@ fun QrImageCard(
icon: Int = R.drawable.session_shield
) {
Card(
backgroundColor = LocalExtraColors.current.lightCell,
backgroundColor = LocalLightCell.current,
elevation = 0.dp,
modifier = modifier
) { QrImage(string, contentDescription, icon) }
@ -55,24 +60,37 @@ fun QrImage(string: String, contentDescription: String, icon: Int = R.drawable.s
}
}
Box {
Box(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f)
) {
AnimatedVisibility(
visible = bitmap != null,
enter = scaleIn()
) {
bitmap?.let {
Image(
bitmap = it.asImageBitmap(),
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f),
contentDescription = contentDescription,
colorFilter = ColorFilter.tint(LocalExtraColors.current.onLightCell)
colorFilter = ColorFilter.tint(LocalOnLightCell.current)
)
}
}
Icon(
painter = painterResource(id = icon),
contentDescription = "",
tint = LocalExtraColors.current.onLightCell,
tint = LocalOnLightCell.current,
modifier = Modifier
.align(Alignment.Center)
.width(46.dp)
.height(56.dp)
.background(color = LocalExtraColors.current.lightCell)
.background(color = LocalLightCell.current)
.padding(horizontal = 3.dp, vertical = 1.dp)
)
}

View File

@ -19,7 +19,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
import network.loki.messenger.R
import org.thoughtcrime.securesms.ui.LocalExtraColors
import org.thoughtcrime.securesms.ui.LocalButtonColor
import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
@ -31,7 +31,7 @@ fun SessionTabRow(pagerState: PagerState, titles: List<Int>) {
TabRow(
backgroundColor = Color.Unspecified,
selectedTabIndex = pagerState.currentPage,
contentColor = LocalExtraColors.current.prominentButtonColor,
contentColor = LocalButtonColor.current,
divider = { TabRowDefaults.Divider(color = MaterialTheme.colors.onPrimary.copy(alpha = TabRowDefaults.DividerOpacity)) },
modifier = Modifier
.height(48.dp)