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 package org.thoughtcrime.securesms.conversation.disappearingmessages.ui
import androidx.compose.foundation.clickable
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
@ -24,8 +25,8 @@ import org.thoughtcrime.securesms.ui.Callbacks
import org.thoughtcrime.securesms.ui.GetString import org.thoughtcrime.securesms.ui.GetString
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.OutlineButton
import org.thoughtcrime.securesms.ui.RadioOption import org.thoughtcrime.securesms.ui.RadioOption
import org.thoughtcrime.securesms.ui.components.OutlineButton
import org.thoughtcrime.securesms.ui.contentDescription import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.fadingEdges import org.thoughtcrime.securesms.ui.fadingEdges
@ -64,9 +65,9 @@ fun DisappearingMessages(
} }
if (state.showSetButton) OutlineButton( if (state.showSetButton) OutlineButton(
GetString(R.string.disappearing_messages_set_button_title), textId = R.string.disappearing_messages_set_button_title,
modifier = Modifier modifier = Modifier
.contentDescription(GetString(R.string.AccessibilityId_set_button)) .contentDescription(R.string.AccessibilityId_set_button)
.align(Alignment.CenterHorizontally) .align(Alignment.CenterHorizontally)
.padding(bottom = 20.dp), .padding(bottom = 20.dp),
onClick = callbacks::onSetClick onClick = callbacks::onSetClick

View File

@ -1,12 +1,10 @@
package org.thoughtcrime.securesms.dms package org.thoughtcrime.securesms.dms
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast
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.Arrangement
@ -17,13 +15,13 @@ import androidx.compose.foundation.layout.width
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.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.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
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource 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
@ -41,14 +39,16 @@ import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
import org.thoughtcrime.securesms.dependencies.DatabaseComponent import org.thoughtcrime.securesms.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.showOpenUrlDialog import org.thoughtcrime.securesms.showOpenUrlDialog
import org.thoughtcrime.securesms.ui.AppTheme import org.thoughtcrime.securesms.ui.AppTheme
import org.thoughtcrime.securesms.ui.BorderlessButtonSecondary import org.thoughtcrime.securesms.ui.LoadingArcOr
import org.thoughtcrime.securesms.ui.OutlineButton
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
import org.thoughtcrime.securesms.ui.components.AppBar 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.MaybeScanQrCode
import org.thoughtcrime.securesms.ui.components.OutlineButton
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
@AndroidEntryPoint @AndroidEntryPoint
class NewMessageFragment : Fragment() { class NewMessageFragment : Fragment() {
@ -157,12 +157,16 @@ fun EnterAccountId(
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
OutlineButton( OutlineButton(
text = stringResource(id = R.string.continue_2),
modifier = Modifier modifier = Modifier
.align(Alignment.CenterHorizontally) .align(Alignment.CenterHorizontally)
.padding(horizontal = 64.dp, vertical = 20.dp) .padding(horizontal = 64.dp, vertical = 20.dp)
.width(200.dp), .width(200.dp)
loading = state.loading .contentDescription(R.string.continue_2),
) { callbacks.onContinue() } 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.showMuteDialog
import org.thoughtcrime.securesms.showSessionDialog import org.thoughtcrime.securesms.showSessionDialog
import org.thoughtcrime.securesms.ui.AppTheme 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.PreviewTheme
import org.thoughtcrime.securesms.ui.SessionShieldIcon import org.thoughtcrime.securesms.ui.SessionShieldIcon
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider 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.h8
import org.thoughtcrime.securesms.ui.small import org.thoughtcrime.securesms.ui.small
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
@ -372,10 +372,11 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
} }
Spacer(Modifier.width(12.dp)) Spacer(Modifier.width(12.dp))
OutlineButton( OutlineButton(
stringResource(R.string.continue_2), textId = R.string.continue_2,
Modifier.align(Alignment.CenterVertically), Modifier.align(Alignment.CenterVertically)
contentDescription = GetString(R.string.AccessibilityId_reveal_recovery_phrase_button) .contentDescription(R.string.AccessibilityId_reveal_recovery_phrase_button),
) { start<RecoveryPasswordActivity>() } 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.onboarding.pickname.startPickDisplayNameActivity
import org.thoughtcrime.securesms.service.KeyCachingService import org.thoughtcrime.securesms.service.KeyCachingService
import org.thoughtcrime.securesms.showOpenUrlDialog import org.thoughtcrime.securesms.showOpenUrlDialog
import org.thoughtcrime.securesms.showSessionDialog
import org.thoughtcrime.securesms.ui.AppTheme 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.GetString
import org.thoughtcrime.securesms.ui.OutlineButton
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
import org.thoughtcrime.securesms.ui.classicDarkColors 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.contentDescription
import org.thoughtcrime.securesms.ui.session_accent import org.thoughtcrime.securesms.ui.session_accent
import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo
@ -94,28 +93,30 @@ class LandingActivity : BaseActionBarActivity() {
OutlineButton( OutlineButton(
text = stringResource(R.string.onboardingAccountCreate), text = stringResource(R.string.onboardingAccountCreate),
modifier = Modifier modifier = Modifier
.contentDescription(R.string.AccessibilityId_create_account_button)
.width(262.dp) .width(262.dp)
.align(Alignment.CenterHorizontally), .align(Alignment.CenterHorizontally),
contentDescription = GetString(R.string.AccessibilityId_create_account_button) onClick = ::startPickDisplayNameActivity
) { startPickDisplayNameActivity() } )
Spacer(modifier = Modifier.height(14.dp)) Spacer(modifier = Modifier.height(14.dp))
FilledButton( FilledButton(
text = stringResource(R.string.onboardingAccountExists), text = stringResource(R.string.onboardingAccountExists),
modifier = Modifier modifier = Modifier
.width(262.dp) .width(262.dp)
.align(Alignment.CenterHorizontally), .align(Alignment.CenterHorizontally)
contentDescription = GetString(R.string.AccessibilityId_restore_account_button) .contentDescription(R.string.AccessibilityId_restore_account_button)
) { startLinkDeviceActivity() } ) { startLinkDeviceActivity() }
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
BorderlessButton( BorderlessButton(
text = stringResource(R.string.onboardingTosPrivacy), text = stringResource(R.string.onboardingTosPrivacy),
modifier = Modifier modifier = Modifier
.width(262.dp) .width(262.dp)
.align(Alignment.CenterHorizontally), .align(Alignment.CenterHorizontally)
contentDescription = GetString(R.string.AccessibilityId_privacy_policy_link), .contentDescription(R.string.AccessibilityId_privacy_policy_link),
fontSize = 11.sp, fontSize = 11.sp,
lineHeight = 13.sp lineHeight = 13.sp,
) { openDialog() } onClick = ::openDialog
)
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
} }
} }
@ -136,44 +137,44 @@ class LandingActivity : BaseActionBarActivity() {
private fun open(url: String) { private fun open(url: String) {
Intent(Intent.ACTION_VIEW, Uri.parse(url)).let(::startActivity) Intent(Intent.ACTION_VIEW, Uri.parse(url)).let(::startActivity)
} }
}
@Composable
private fun IncomingText(text: String) { @Composable
ChatText( private fun IncomingText(text: String) {
text, ChatText(
color = classicDarkColors[2] text,
) color = classicDarkColors[2]
} )
}
@Composable
private fun ColumnScope.OutgoingText(text: String) { @Composable
ChatText( private fun ColumnScope.OutgoingText(text: String) {
text, ChatText(
color = session_accent, text,
textColor = MaterialTheme.colors.primary, color = session_accent,
modifier = Modifier.align(Alignment.End) textColor = MaterialTheme.colors.primary,
) modifier = Modifier.align(Alignment.End)
} )
}
@Composable
private fun ChatText( @Composable
text: String, private fun ChatText(
color: Color, text: String,
modifier: Modifier = Modifier, color: Color,
textColor: Color = Color.Unspecified modifier: Modifier = Modifier,
) { textColor: Color = Color.Unspecified
Text( ) {
text, Text(
fontSize = 16.sp, text,
lineHeight = 19.sp, fontSize = 16.sp,
color = textColor, lineHeight = 19.sp,
modifier = modifier color = textColor,
.fillMaxWidth(0.666f) modifier = modifier
.background( .fillMaxWidth(0.666f)
color = color, .background(
shape = RoundedCornerShape(size = 13.dp) color = color,
) shape = RoundedCornerShape(size = 13.dp)
.padding(horizontal = 16.dp, vertical = 12.dp) )
) .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.session.libsession.utilities.TextSecurePreferences
import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.ui.AppTheme import org.thoughtcrime.securesms.ui.AppTheme
import org.thoughtcrime.securesms.ui.OutlineButton
import org.thoughtcrime.securesms.ui.baseBold import org.thoughtcrime.securesms.ui.baseBold
import org.thoughtcrime.securesms.ui.components.MaybeScanQrCode 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.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
@ -150,12 +150,13 @@ fun RecoveryPassword(state: LinkDeviceState, onChange: (String) -> Unit = {}, on
} }
Spacer(Modifier.weight(2f)) Spacer(Modifier.weight(2f))
OutlineButton( OutlineButton(
text = stringResource(id = R.string.continue_2), textId = R.string.continue_2,
modifier = Modifier modifier = Modifier
.align(Alignment.CenterHorizontally) .align(Alignment.CenterHorizontally)
.padding(horizontal = 64.dp, vertical = 20.dp) .padding(horizontal = 64.dp, vertical = 20.dp)
.width(200.dp) .width(200.dp),
) { onContinue() } onClick = onContinue
)
} }
} }

View File

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

View File

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

View File

@ -1,8 +1,5 @@
package org.thoughtcrime.securesms.onboarding.recoverypassword package org.thoughtcrime.securesms.onboarding.recoverypassword
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.compose.animation.AnimatedVisibility 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.Spacer
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ComposeView
@ -35,15 +36,16 @@ import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.showSessionDialog import org.thoughtcrime.securesms.showSessionDialog
import org.thoughtcrime.securesms.ui.AppTheme import org.thoughtcrime.securesms.ui.AppTheme
import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin
import org.thoughtcrime.securesms.ui.GetString import org.thoughtcrime.securesms.ui.LocalButtonColor
import org.thoughtcrime.securesms.ui.LocalExtraColors
import org.thoughtcrime.securesms.ui.OutlineButton
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.SessionShieldIcon import org.thoughtcrime.securesms.ui.SessionShieldIcon
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
import org.thoughtcrime.securesms.ui.classicDarkColors import org.thoughtcrime.securesms.ui.classicDarkColors
import org.thoughtcrime.securesms.ui.colorDestructive 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.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.contentDescription
import org.thoughtcrime.securesms.ui.h8 import org.thoughtcrime.securesms.ui.h8
import org.thoughtcrime.securesms.ui.small import org.thoughtcrime.securesms.ui.small
@ -114,15 +116,17 @@ fun RecoveryPassword(
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(bottom = 16.dp) .padding(bottom = 16.dp)
) { ) {
RecoveryPasswordCell(seed, copySeed) SmallButtons {
HideRecoveryPasswordCell(onHide) RecoveryPasswordCell(seed, copySeed)
HideRecoveryPasswordCell(onHide)
}
} }
} }
} }
@Composable @Composable
fun RecoveryPasswordCell(seed: String, copySeed:() -> Unit = {}) { fun RecoveryPasswordCell(seed: String, copySeed:() -> Unit = {}) {
val showQr = remember { var showQr by remember {
mutableStateOf(false) mutableStateOf(false)
} }
@ -136,7 +140,7 @@ fun RecoveryPasswordCell(seed: String, copySeed:() -> Unit = {}) {
Text(stringResource(R.string.recoveryPasswordDescription)) Text(stringResource(R.string.recoveryPasswordDescription))
AnimatedVisibility(!showQr.value) { AnimatedVisibility(!showQr) {
Text( Text(
seed, seed,
modifier = Modifier modifier = Modifier
@ -149,62 +153,70 @@ fun RecoveryPasswordCell(seed: String, copySeed:() -> Unit = {}) {
) )
.padding(24.dp), .padding(24.dp),
style = MaterialTheme.typography.small.copy(fontFamily = FontFamily.Monospace), style = MaterialTheme.typography.small.copy(fontFamily = FontFamily.Monospace),
color = LocalExtraColors.current.prominentButtonColor, color = MaterialTheme.colors.run { if (isLight) onSurface else secondary },
) )
} }
AnimatedVisibility( AnimatedVisibility(
showQr.value, showQr,
modifier = Modifier.align(Alignment.CenterHorizontally).padding(vertical = 24.dp) modifier = Modifier.align(Alignment.CenterHorizontally)
) { ) {
QrImageCard( QrImageCard(
seed, seed,
modifier = Modifier.padding(vertical = 24.dp),
contentDescription = "QR code of your recovery password", contentDescription = "QR code of your recovery password",
icon = R.drawable.session_shield icon = R.drawable.session_shield
) )
} }
AnimatedVisibility(!showQr.value) { AnimatedVisibility(!showQr) {
Row(horizontalArrangement = Arrangement.spacedBy(32.dp)) { Row(horizontalArrangement = Arrangement.spacedBy(32.dp)) {
OutlineButton( TemporaryStateButton { source, temporary ->
modifier = Modifier.weight(1f), OutlineButton(
color = MaterialTheme.colors.onPrimary, modifier = Modifier.weight(1f),
onClick = copySeed, interactionSource = source,
temporaryContent = { Text(stringResource(R.string.copied)) } 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( OutlineButton(
text = stringResource(R.string.recoveryPasswordView), textId = R.string.recoveryPasswordView,
color = MaterialTheme.colors.onPrimary, onClick = { showQr = !showQr }
modifier = Modifier.align(Alignment.CenterHorizontally) )
) { showQr.toggle() }
} }
} }
} }
} }
private fun MutableState<Boolean>.toggle() { value = !value }
@Composable @Composable
fun HideRecoveryPasswordCell(onHide: () -> Unit = {}) { fun HideRecoveryPasswordCell(onHide: () -> Unit = {}) {
CellWithPaddingAndMargin { CellWithPaddingAndMargin {
Row { Row {
Column(Modifier.weight(1f)) { Column(
Modifier.weight(1f)
) {
Text(text = stringResource(R.string.recoveryPasswordHideRecoveryPassword), style = MaterialTheme.typography.h8) Text(text = stringResource(R.string.recoveryPasswordHideRecoveryPassword), style = MaterialTheme.typography.h8)
Text(text = stringResource(R.string.recoveryPasswordHideRecoveryPasswordDescription)) Text(text = stringResource(R.string.recoveryPasswordHideRecoveryPasswordDescription))
} }
OutlineButton( CompositionLocalProvider(
stringResource(R.string.hide), LocalButtonColor provides colorDestructive,
contentDescription = GetString(R.string.AccessibilityId_hide_recovery_password_button), ) {
modifier = Modifier.align(Alignment.CenterVertically), OutlineButton(
color = colorDestructive textId = R.string.hide,
) { onHide() } 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.EditorInfo
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.Toast 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.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row 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.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material.Card import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text 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.profiles.ProfileMediaConstraints
import org.thoughtcrime.securesms.showSessionDialog import org.thoughtcrime.securesms.showSessionDialog
import org.thoughtcrime.securesms.ui.AppTheme import org.thoughtcrime.securesms.ui.AppTheme
import org.thoughtcrime.securesms.ui.BorderlessButton
import org.thoughtcrime.securesms.ui.Cell import org.thoughtcrime.securesms.ui.Cell
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.ItemButtonWithDrawable import org.thoughtcrime.securesms.ui.ItemButtonWithDrawable
import org.thoughtcrime.securesms.ui.OutlineButton import org.thoughtcrime.securesms.ui.components.OutlineButton
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.components.TemporaryStateButton
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
import org.thoughtcrime.securesms.ui.destructiveButtonColors import org.thoughtcrime.securesms.ui.destructiveButtonColors
import org.thoughtcrime.securesms.util.BitmapDecodingException import org.thoughtcrime.securesms.util.BitmapDecodingException
import org.thoughtcrime.securesms.util.BitmapUtil import org.thoughtcrime.securesms.util.BitmapUtil
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
import org.thoughtcrime.securesms.util.disableClipping
import org.thoughtcrime.securesms.util.push import org.thoughtcrime.securesms.util.push
import org.thoughtcrime.securesms.util.show import org.thoughtcrime.securesms.util.show
import java.io.File import java.io.File
@ -429,20 +429,25 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
fun Buttons() { fun Buttons() {
Column { Column {
Row( Row(
modifier = Modifier.padding(horizontal = 24.dp), modifier = Modifier.padding(horizontal = 24.dp).padding(top = 8.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp),
) { ) {
OutlineButton( OutlineButton(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
onClick = { sharePublicKey() } onClick = ::sharePublicKey
) { Text(stringResource(R.string.share)) }
OutlineButton(
modifier = Modifier.weight(1f),
onClick = { copyPublicKey() },
temporaryContent = { Text(stringResource(R.string.copied)) }
) { ) {
Text(stringResource(R.string.copy)) Text(stringResource(R.string.share))
}
TemporaryStateButton { source, temporary ->
OutlineButton(
modifier = Modifier.weight(1f),
interactionSource = source,
onClick = { copyPublicKey() },
) {
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.util.SparseArray
import android.view.View import android.view.View
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.widget.SwitchCompat
import androidx.core.view.children import androidx.core.view.children
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@ -31,8 +30,8 @@ class AppearanceSettingsActivity: PassphraseRequiredActionBarActivity(), View.On
var currentTheme: ThemeState? = null var currentTheme: ThemeState? = null
private val accentColors private val accentColors by lazy {
get() = mapOf( mapOf(
binding.accentGreen to R.style.PrimaryGreen, binding.accentGreen to R.style.PrimaryGreen,
binding.accentBlue to R.style.PrimaryBlue, binding.accentBlue to R.style.PrimaryBlue,
binding.accentYellow to R.style.PrimaryYellow, binding.accentYellow to R.style.PrimaryYellow,
@ -41,9 +40,10 @@ class AppearanceSettingsActivity: PassphraseRequiredActionBarActivity(), View.On
binding.accentOrange to R.style.PrimaryOrange, binding.accentOrange to R.style.PrimaryOrange,
binding.accentRed to R.style.PrimaryRed binding.accentRed to R.style.PrimaryRed
) )
}
private val themeViews private val themeViews by lazy {
get() = listOf( listOf(
binding.themeOptionClassicDark, binding.themeOptionClassicDark,
binding.themeRadioClassicDark, binding.themeRadioClassicDark,
binding.themeOptionClassicLight, binding.themeOptionClassicLight,
@ -53,6 +53,7 @@ class AppearanceSettingsActivity: PassphraseRequiredActionBarActivity(), View.On
binding.themeOptionOceanLight, binding.themeOptionOceanLight,
binding.themeRadioOceanLight binding.themeRadioOceanLight
) )
}
override fun onClick(v: View?) { override fun onClick(v: View?) {
v ?: return v ?: return

View File

@ -1,14 +1,13 @@
package org.thoughtcrime.securesms.ui package org.thoughtcrime.securesms.ui
import android.graphics.drawable.BitmapDrawable
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Canvas import androidx.compose.foundation.Canvas
import androidx.compose.foundation.Image
import androidx.compose.foundation.ScrollState import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.Image
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.BoxScope 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.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Button
import androidx.compose.material.ButtonColors import androidx.compose.material.ButtonColors
import androidx.compose.material.ButtonDefaults import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Card import androidx.compose.material.Card
@ -35,12 +33,14 @@ import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Colors import androidx.compose.material.Colors
import androidx.compose.material.ContentAlpha import androidx.compose.material.ContentAlpha
import androidx.compose.material.Icon import androidx.compose.material.Icon
import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedButton import androidx.compose.material.OutlinedButton
import androidx.compose.material.RadioButton import androidx.compose.material.RadioButton
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.TextButton import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -49,6 +49,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Size 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.Color
import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalContext 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.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.res.ResourcesCompat
import com.google.accompanist.drawablepainter.rememberDrawablePainter import com.google.accompanist.drawablepainter.rememberDrawablePainter
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay 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
@ -83,155 +82,6 @@ import org.thoughtcrime.securesms.components.ProfilePictureView
import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.OptionsCard import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.OptionsCard
import kotlin.math.min import kotlin.math.min
import kotlin.math.roundToInt 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> { interface Callbacks<in T> {
fun onSetClick(): Any? fun onSetClick(): Any?
@ -367,16 +217,18 @@ fun CellWithPaddingAndMargin(
margin: Dp = 32.dp, margin: Dp = 32.dp,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
Card( CompositionLocalProvider(LocalButtonColor provides MaterialTheme.colors.onPrimary) {
backgroundColor = MaterialTheme.colors.cellColor, Card(
shape = RoundedCornerShape(16.dp), backgroundColor = LocalCellColor.current,
elevation = 0.dp, shape = RoundedCornerShape(16.dp),
modifier = Modifier elevation = 0.dp,
.wrapContentHeight() modifier = Modifier
.fillMaxWidth() .wrapContentHeight()
.padding(horizontal = margin), .fillMaxWidth()
) { .padding(horizontal = margin),
Box(Modifier.padding(padding)) { content() } ) {
Box(Modifier.padding(padding)) { content() }
}
} }
} }
@ -434,27 +286,10 @@ fun Modifier.contentDescription(id: Int?): Modifier {
} }
@Composable @Composable
fun OutlineButton(text: GetString, contentDescription: GetString? = text, modifier: Modifier = Modifier, onClick: () -> Unit) { fun Modifier.contentDescription(text: String?): Modifier {
OutlinedButton( return text?.let { semantics { contentDescription = it } } ?: this
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())
}
} }
private val Colors.cellColor: Color
@Composable
get() = LocalExtraColors.current.settingsBackground
fun Modifier.fadingEdges( fun Modifier.fadingEdges(
scrollState: ScrollState, scrollState: ScrollState,
topEdgeHeight: Dp = 0.dp, topEdgeHeight: Dp = 0.dp,
@ -577,5 +412,20 @@ fun RowScope.SessionShieldIcon() {
@Composable @Composable
fun LaunchedEffectAsync(block: suspend CoroutineScope.() -> Unit) { 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 com.google.android.material.color.MaterialColors
import network.loki.messenger.R import network.loki.messenger.R
val LocalExtraColors = staticCompositionLocalOf<ExtraColors> { error("No Custom Attribute value provided") } val LocalCellColor = staticCompositionLocalOf { Color.Black }
val LocalButtonColor = staticCompositionLocalOf { Color.Black }
val LocalLightCell = staticCompositionLocalOf { Color.Black }
data class ExtraColors( val LocalOnLightCell = staticCompositionLocalOf { Color.Black }
val settingsBackground: Color,
val prominentButtonColor: Color,
val lightCell: Color,
val onLightCell: Color,
)
/** /**
* Converts current Theme to Compose Theme. * Converts current Theme to Compose Theme.
@ -46,19 +41,16 @@ fun AppTheme(
) { ) {
val context = LocalContext.current 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) val surface = context.getColorFromTheme(R.attr.colorSettingsBackground)
CompositionLocalProvider(
CompositionLocalProvider(LocalExtraColors provides extraColors) { *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) { AppCompatTheme(surface = surface) {
CompositionLocalProvider(LocalTextSelectionColors provides TextSelectionColors( CompositionLocalProvider(LocalTextSelectionColors provides TextSelectionColors(
handleColor = MaterialTheme.colors.secondary, 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 network.loki.messenger.R
import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.onboarding.Analyzer import org.thoughtcrime.securesms.onboarding.Analyzer
import org.thoughtcrime.securesms.ui.OutlineButton
import java.util.concurrent.Executors import java.util.concurrent.Executors
typealias CameraPreview = androidx.camera.core.Preview typealias CameraPreview = androidx.camera.core.Preview
@ -96,17 +95,18 @@ fun MaybeScanQrCode(
) )
Spacer(modifier = Modifier.size(20.dp)) Spacer(modifier = Modifier.size(20.dp))
OutlineButton( OutlineButton(
text = stringResource(R.string.sessionSettings),
modifier = Modifier.align(Alignment.CenterHorizontally), modifier = Modifier.align(Alignment.CenterHorizontally),
onClick = onClickSettings onClick = onClickSettings
) ) {
Text(stringResource(R.string.sessionSettings))
}
} }
} else { } else {
OutlineButton( 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))
} }
} }
} }
@ -173,12 +173,12 @@ fun ScanQrCode(errors: Flow<String>, onScan: (String) -> Unit) {
) )
Box( Box(
Modifier Modifier
.aspectRatio(1f) .aspectRatio(1f)
.padding(20.dp) .padding(20.dp)
.clip(shape = RoundedCornerShape(20.dp)) .clip(shape = RoundedCornerShape(20.dp))
.background(Color(0x33ffffff)) .background(Color(0x33ffffff))
.align(Alignment.Center) .align(Alignment.Center)
) )
} }
} }

View File

@ -1,9 +1,13 @@
package org.thoughtcrime.securesms.ui.components package org.thoughtcrime.securesms.ui.components
import android.graphics.Bitmap import android.graphics.Bitmap
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.scaleIn
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
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.layout.aspectRatio
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.foundation.layout.width import androidx.compose.foundation.layout.width
@ -25,7 +29,8 @@ 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.LocalExtraColors import org.thoughtcrime.securesms.ui.LocalLightCell
import org.thoughtcrime.securesms.ui.LocalOnLightCell
import org.thoughtcrime.securesms.util.QRCodeUtilities import org.thoughtcrime.securesms.util.QRCodeUtilities
@Composable @Composable
@ -36,7 +41,7 @@ fun QrImageCard(
icon: Int = R.drawable.session_shield icon: Int = R.drawable.session_shield
) { ) {
Card( Card(
backgroundColor = LocalExtraColors.current.lightCell, backgroundColor = LocalLightCell.current,
elevation = 0.dp, elevation = 0.dp,
modifier = modifier modifier = modifier
) { QrImage(string, contentDescription, icon) } ) { QrImage(string, contentDescription, icon) }
@ -55,24 +60,37 @@ fun QrImage(string: String, contentDescription: String, icon: Int = R.drawable.s
} }
} }
Box { Box(
bitmap?.let { modifier = Modifier
Image( .fillMaxWidth()
bitmap = it.asImageBitmap(), .aspectRatio(1f)
contentDescription = contentDescription, ) {
colorFilter = ColorFilter.tint(LocalExtraColors.current.onLightCell) AnimatedVisibility(
) visible = bitmap != null,
enter = scaleIn()
) {
bitmap?.let {
Image(
bitmap = it.asImageBitmap(),
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f),
contentDescription = contentDescription,
colorFilter = ColorFilter.tint(LocalOnLightCell.current)
)
}
} }
Icon( Icon(
painter = painterResource(id = icon), painter = painterResource(id = icon),
contentDescription = "", contentDescription = "",
tint = LocalExtraColors.current.onLightCell, tint = LocalOnLightCell.current,
modifier = Modifier modifier = Modifier
.align(Alignment.Center) .align(Alignment.Center)
.width(46.dp) .width(46.dp)
.height(56.dp) .height(56.dp)
.background(color = LocalExtraColors.current.lightCell) .background(color = LocalLightCell.current)
.padding(horizontal = 3.dp, vertical = 1.dp) .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 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.LocalExtraColors import org.thoughtcrime.securesms.ui.LocalButtonColor
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
@ -31,7 +31,7 @@ fun SessionTabRow(pagerState: PagerState, titles: List<Int>) {
TabRow( TabRow(
backgroundColor = Color.Unspecified, backgroundColor = Color.Unspecified,
selectedTabIndex = pagerState.currentPage, selectedTabIndex = pagerState.currentPage,
contentColor = LocalExtraColors.current.prominentButtonColor, contentColor = LocalButtonColor.current,
divider = { TabRowDefaults.Divider(color = MaterialTheme.colors.onPrimary.copy(alpha = TabRowDefaults.DividerOpacity)) }, divider = { TabRowDefaults.Divider(color = MaterialTheme.colors.onPrimary.copy(alpha = TabRowDefaults.DividerOpacity)) },
modifier = Modifier modifier = Modifier
.height(48.dp) .height(48.dp)