Updates for code review

This commit is contained in:
Andrew 2024-04-04 22:14:55 +10:30
parent 3ae2dc5bc5
commit 2cc83cd650
8 changed files with 110 additions and 51 deletions

View File

@ -5,7 +5,7 @@ buildscript {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.4.2' classpath "com.android.tools.build:gradle:$gradlePluginVersion"
classpath files('libs/gradle-witness.jar') classpath files('libs/gradle-witness.jar')
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion"

View File

@ -14,6 +14,7 @@ import androidx.annotation.LayoutRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.annotation.StyleRes import androidx.annotation.StyleRes
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.text.HtmlCompat
import androidx.core.view.setMargins import androidx.core.view.setMargins
import androidx.core.view.setPadding import androidx.core.view.setPadding
import androidx.core.view.updateMargins import androidx.core.view.updateMargins
@ -21,7 +22,6 @@ import androidx.fragment.app.Fragment
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.util.toPx import org.thoughtcrime.securesms.util.toPx
@DslMarker @DslMarker
@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE) @Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE)
annotation class DialogDsl annotation class DialogDsl
@ -64,7 +64,6 @@ class SessionDialogBuilder(val context: Context) {
} }
} }
private fun text(text: CharSequence?, @StyleRes style: Int, modify: TextView.() -> Unit) { private fun text(text: CharSequence?, @StyleRes style: Int, modify: TextView.() -> Unit) {
text ?: return text ?: return
TextView(context, null, 0, style) TextView(context, null, 0, style)
@ -75,6 +74,8 @@ class SessionDialogBuilder(val context: Context) {
}.let(topView::addView) }.let(topView::addView)
} }
fun htmlText(@StringRes id: Int, @StyleRes style: Int = 0, modify: TextView.() -> Unit = {}) { text(context.resources.getText(id)) }
fun view(view: View) = contentView.addView(view) fun view(view: View) = contentView.addView(view)
fun view(@LayoutRes layout: Int): View = LayoutInflater.from(context).inflate(layout, contentView) fun view(@LayoutRes layout: Int): View = LayoutInflater.from(context).inflate(layout, contentView)

View File

@ -18,9 +18,11 @@ import androidx.compose.runtime.remember
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.res.stringResource
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
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.dependencies.ConfigFactory import org.thoughtcrime.securesms.dependencies.ConfigFactory
@ -99,8 +101,8 @@ class LoadingActivity: BaseActionBarActivity() {
Column { Column {
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
ProgressArc(animatable.value, modifier = Modifier.align(Alignment.CenterHorizontally)) ProgressArc(animatable.value, modifier = Modifier.align(Alignment.CenterHorizontally))
Text("One moment please..", modifier = Modifier.align(Alignment.CenterHorizontally), style = MaterialTheme.typography.h6) Text(stringResource(R.string.waitOneMoment), modifier = Modifier.align(Alignment.CenterHorizontally), style = MaterialTheme.typography.h6)
Text("Loading your account", modifier = Modifier.align(Alignment.CenterHorizontally)) Text(stringResource(R.string.loadAccountProgressMessage), modifier = Modifier.align(Alignment.CenterHorizontally))
Spacer(modifier = Modifier.weight(2f)) Spacer(modifier = Modifier.weight(2f))
} }
} }

View File

@ -61,16 +61,16 @@ class MessageNotificationsActivity : BaseActionBarActivity() {
TextSecurePreferences.setHasSeenWelcomeScreen(this, true) TextSecurePreferences.setHasSeenWelcomeScreen(this, true)
ComposeView(this) ComposeView(this)
.apply { setContent { MessageNotifications() } } .apply { setContent { MessageNotificationsScreen() } }
.let(::setContentView) .let(::setContentView)
} }
@Composable @Composable
private fun MessageNotifications() { private fun MessageNotificationsScreen() {
val state by viewModel.stateFlow.collectAsState() val state by viewModel.stateFlow.collectAsState()
AppTheme { AppTheme {
MessageNotifications(state, viewModel::setEnabled, ::register) MessageNotificationsScreen(state, viewModel::setEnabled, ::register)
} }
} }
@ -87,25 +87,25 @@ class MessageNotificationsActivity : BaseActionBarActivity() {
@Preview @Preview
@Composable @Composable
fun MessageNotificationsPreview( fun MessageNotificationsScreenPreview(
@PreviewParameter(ThemeResPreviewParameterProvider::class) themeResId: Int @PreviewParameter(ThemeResPreviewParameterProvider::class) themeResId: Int
) { ) {
PreviewTheme(themeResId) { PreviewTheme(themeResId) {
MessageNotifications() MessageNotificationsScreen()
} }
} }
@Composable @Composable
fun MessageNotifications( fun MessageNotificationsScreen(
state: MessageNotificationsState = MessageNotificationsState(), state: MessageNotificationsState = MessageNotificationsState(),
setEnabled: (Boolean) -> Unit = {}, setEnabled: (Boolean) -> Unit = {},
onContinue: () -> Unit = {} onContinue: () -> Unit = {}
) { ) {
Column(Modifier.padding(horizontal = 32.dp)) { Column(Modifier.padding(horizontal = 32.dp)) {
Spacer(Modifier.weight(1f)) Spacer(Modifier.weight(1f))
Text("Message notifications", style = MaterialTheme.typography.h4) Text(stringResource(R.string.notificationsMessage), style = MaterialTheme.typography.h4)
Spacer(Modifier.height(16.dp)) Spacer(Modifier.height(16.dp))
Text("There are two ways Session can notify you of new messages.") Text(stringResource(R.string.onboardingMessageNotificationExplaination))
Spacer(Modifier.height(16.dp)) Spacer(Modifier.height(16.dp))
NotificationRadioButton( NotificationRadioButton(
R.string.activity_pn_mode_fast_mode, R.string.activity_pn_mode_fast_mode,
@ -125,8 +125,8 @@ fun MessageNotifications(
OutlineButton( OutlineButton(
stringResource(R.string.continue_2), stringResource(R.string.continue_2),
modifier = Modifier modifier = Modifier
.align(Alignment.CenterHorizontally) .align(Alignment.CenterHorizontally)
.width(262.dp), .width(262.dp),
onClick = onContinue onClick = onContinue
) )
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))

View File

@ -7,6 +7,8 @@ import android.os.Bundle
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.Crossfade import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.animateValue
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
@ -27,9 +29,11 @@ import androidx.compose.material.Icon
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.LaunchedEffect
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
@ -41,6 +45,9 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.showSessionDialog import org.thoughtcrime.securesms.showSessionDialog
@ -55,6 +62,7 @@ import org.thoughtcrime.securesms.ui.classicDarkColors
import org.thoughtcrime.securesms.ui.colorDestructive import org.thoughtcrime.securesms.ui.colorDestructive
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 kotlin.time.Duration.Companion.seconds
class RecoveryPasswordActivity : BaseActionBarActivity() { class RecoveryPasswordActivity : BaseActionBarActivity() {
@ -73,20 +81,18 @@ class RecoveryPasswordActivity : BaseActionBarActivity() {
private fun onHide() { private fun onHide() {
showSessionDialog { showSessionDialog {
title("Hide Recovery Password Permanently") title(R.string.recoveryPasswordHidePermanently)
text("Without your recovery password, you cannot load your account on new devices.\n" + htmlText(R.string.recoveryPasswordHidePermanentlyDescription1)
"\n" +
"We strongly recommend you save your recovery password in a safe and secure place before continuing.")
destructiveButton(R.string.continue_2) { onHideConfirm() } destructiveButton(R.string.continue_2) { onHideConfirm() }
button(R.string.cancel) {} cancelButton()
} }
} }
private fun onHideConfirm() { private fun onHideConfirm() {
showSessionDialog { showSessionDialog {
title("Hide Recovery Password Permanently") title(R.string.recoveryPasswordHidePermanently)
text("Are you sure you want to permanently hide your recovery password on this device? This cannot be undone.") text(R.string.recoveryPasswordHidePermanentlyDescription2)
button(R.string.cancel) {} cancelButton()
destructiveButton(R.string.yes) { destructiveButton(R.string.yes) {
viewModel.permanentlyHidePassword() viewModel.permanentlyHidePassword()
finish() finish()
@ -131,31 +137,27 @@ fun RecoveryPasswordCell(seed: String = "", qrBitmap: Bitmap? = null, copySeed:(
mutableStateOf(false) mutableStateOf(false)
} }
val copied = remember {
mutableStateOf(false)
}
CellWithPaddingAndMargin { CellWithPaddingAndMargin {
Column { Column {
Row { Row {
Text("Recovery Password") Text(stringResource(R.string.sessionRecoveryPassword))
Spacer(Modifier.width(8.dp)) Spacer(Modifier.width(8.dp))
SessionShieldIcon() SessionShieldIcon()
} }
Text("Use your recovery password to load your account on new devices.\n\nYour account cannot be recovered without your recovery password. Make sure it's stored somewhere safe and secure — and don't share it with anyone.") Text(stringResource(R.string.recoveryPasswordDescription))
AnimatedVisibility(!showQr.value) { AnimatedVisibility(!showQr.value) {
Text( Text(
seed, seed,
modifier = Modifier modifier = Modifier
.padding(vertical = 24.dp) .padding(vertical = 24.dp)
.border( .border(
width = 1.dp, width = 1.dp,
color = classicDarkColors[3], color = classicDarkColors[3],
shape = RoundedCornerShape(11.dp) shape = RoundedCornerShape(11.dp)
) )
.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 = LocalExtraColors.current.prominentButtonColor,
) )
@ -166,8 +168,8 @@ fun RecoveryPasswordCell(seed: String = "", qrBitmap: Bitmap? = null, copySeed:(
backgroundColor = LocalExtraColors.current.lightCell, backgroundColor = LocalExtraColors.current.lightCell,
elevation = 0.dp, elevation = 0.dp,
modifier = Modifier modifier = Modifier
.align(Alignment.CenterHorizontally) .align(Alignment.CenterHorizontally)
.padding(vertical = 24.dp) .padding(vertical = 24.dp)
) { ) {
Box { Box {
qrBitmap?.let { qrBitmap?.let {
@ -183,11 +185,11 @@ fun RecoveryPasswordCell(seed: String = "", qrBitmap: Bitmap? = null, copySeed:(
contentDescription = "", contentDescription = "",
tint = LocalExtraColors.current.onLightCell, tint = LocalExtraColors.current.onLightCell,
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 = LocalExtraColors.current.lightCell)
.padding(horizontal = 3.dp, vertical = 1.dp) .padding(horizontal = 3.dp, vertical = 1.dp)
) )
} }
} }
@ -195,16 +197,36 @@ fun RecoveryPasswordCell(seed: String = "", qrBitmap: Bitmap? = null, copySeed:(
AnimatedVisibility(!showQr.value) { AnimatedVisibility(!showQr.value) {
Row(horizontalArrangement = Arrangement.spacedBy(32.dp)) { Row(horizontalArrangement = Arrangement.spacedBy(32.dp)) {
Crossfade(targetState = if (copied.value) R.string.copied else R.string.copy, modifier = Modifier.weight(1f), label = "Copy to Copied CrossFade") { val scope = rememberCoroutineScope()
OutlineButton(text = stringResource(it), modifier = Modifier.fillMaxWidth(), color = MaterialTheme.colors.onPrimary) { copySeed(); copied.value = true } val revertCopiedTextJob = remember { mutableStateOf<Job?>(null) }
val copied = remember { mutableStateOf(false) }
OutlineButton(
modifier = Modifier.weight(1f),
color = MaterialTheme.colors.onPrimary,
onClick = {
copySeed()
revertCopiedTextJob.value?.cancel()
revertCopiedTextJob.value = scope.launch {
copied.value = true
delay(2.seconds)
copied.value = false
}
}
) {
AnimatedVisibility(!copied.value) {
Text(stringResource(R.string.copy))
}
AnimatedVisibility(copied.value) {
Text(stringResource(R.string.copied))
}
} }
OutlineButton(text = "View QR", modifier = Modifier.weight(1f), color = MaterialTheme.colors.onPrimary) { showQr.toggle() } OutlineButton(text = stringResource(R.string.qrView), modifier = Modifier.weight(1f), color = MaterialTheme.colors.onPrimary) { showQr.toggle() }
} }
} }
AnimatedVisibility(showQr.value, modifier = Modifier.align(Alignment.CenterHorizontally)) { AnimatedVisibility(showQr.value, modifier = Modifier.align(Alignment.CenterHorizontally)) {
OutlineButton( OutlineButton(
text = "View Password", text = stringResource(R.string.recoveryPasswordView),
color = MaterialTheme.colors.onPrimary, color = MaterialTheme.colors.onPrimary,
modifier = Modifier.align(Alignment.CenterHorizontally) modifier = Modifier.align(Alignment.CenterHorizontally)
) { showQr.toggle() } ) { showQr.toggle() }
@ -220,11 +242,11 @@ fun HideRecoveryPasswordCell(onHide: () -> Unit = {}) {
CellWithPaddingAndMargin { CellWithPaddingAndMargin {
Row { Row {
Column(Modifier.weight(1f)) { Column(Modifier.weight(1f)) {
Text(text = "Hide Recovery Password", style = MaterialTheme.typography.h8) Text(text = stringResource(R.string.recoveryPasswordHideRecoveryPassword), style = MaterialTheme.typography.h8)
Text(text = "Permanently hide your recovery password on this device.") Text(text = stringResource(R.string.recoveryPasswordHideRecoveryPasswordDescription))
} }
OutlineButton( OutlineButton(
"Hide", stringResource(R.string.hide),
modifier = Modifier.align(Alignment.CenterVertically), modifier = Modifier.align(Alignment.CenterVertically),
color = colorDestructive color = colorDestructive
) { onHide() } ) { onHide() }

View File

@ -84,6 +84,27 @@ fun OutlineButton(
} }
} }
@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(50), // = 50% percent
colors = ButtonDefaults.outlinedButtonColors(
contentColor = color,
backgroundColor = Color.Unspecified
)
) {
content()
}
}
@Composable @Composable
fun FilledButton(text: String, modifier: Modifier = Modifier, onClick: () -> Unit) { fun FilledButton(text: String, modifier: Modifier = Modifier, onClick: () -> Unit) {
OutlinedButton( OutlinedButton(

View File

@ -1081,4 +1081,17 @@
<string name="activity_link_load_account">Load Account</string> <string name="activity_link_load_account">Load Account</string>
<string name="activity_link_camera_permission_permanently_denied_configure_in_settings">Camera Permission permanently denied. Configure in settings.</string> <string name="activity_link_camera_permission_permanently_denied_configure_in_settings">Camera Permission permanently denied. Configure in settings.</string>
<string name="activity_link_enter_your_recovery_password_to_load_your_account_if_you_haven_t_saved_it_you_can_find_it_in_your_app_settings">Enter your recovery password to load your account. If you haven\'t saved it, you can find it in your app settings.</string> <string name="activity_link_enter_your_recovery_password_to_load_your_account_if_you_haven_t_saved_it_you_can_find_it_in_your_app_settings">Enter your recovery password to load your account. If you haven\'t saved it, you can find it in your app settings.</string>
<string name="waitOneMoment">One moment please..</string>
<string name="loadAccountProgressMessage">Loading your account</string>
<string name="notificationsMessage">Message notifications</string>
<string name="onboardingMessageNotificationExplaination">There are two ways Session can notify you of new messages.</string>
<string name="recoveryPasswordHidePermanently">Hide Recovery Password Permanently</string>
<string name="recoveryPasswordHidePermanentlyDescription1"><![CDATA[Without your recovery password, you cannot load your account on new devices. <br /><br />We strongly recommend you save your recovery password in a safe and secure place before continuing.]]></string>
<string name="recoveryPasswordHidePermanentlyDescription2">Are you sure you want to permanently hide your recovery password on this device? This cannot be undone.</string>
<string name="recoveryPasswordDescription">Use your recovery password to load your account on new devices. Your account cannot be recovered without your recovery password. Make sure it\'s stored somewhere safe and secure — and don\'t share it with anyone.</string>
<string name="hide">Hide</string>
<string name="recoveryPasswordHideRecoveryPassword">Hide Recovery Password</string>
<string name="qrView">View QR</string>
<string name="recoveryPasswordView">View Password</string>
<string name="recoveryPasswordHideRecoveryPasswordDescription">Permanently hide your recovery password on this device.</string>
</resources> </resources>

View File

@ -16,7 +16,7 @@ android.enableJetifier=true
org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M" org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M"
org.gradle.unsafe.configuration-cache=true org.gradle.unsafe.configuration-cache=true
gradlePluginVersion=7.3.1 gradlePluginVersion=7.4.2
googleServicesVersion=4.3.12 googleServicesVersion=4.3.12
kotlinVersion=1.8.21 kotlinVersion=1.8.21
android.useAndroidX=true android.useAndroidX=true