Handle backpresses in onboarding

This commit is contained in:
bemusementpark 2024-07-02 09:25:19 +09:30
parent d621036af6
commit 9cf3a37a2b
17 changed files with 231 additions and 81 deletions

View File

@ -18,6 +18,7 @@ package org.thoughtcrime.securesms;
import static nl.komponents.kovenant.android.KovenantAndroid.startKovenant;
import static nl.komponents.kovenant.android.KovenantAndroid.stopKovenant;
import android.annotation.SuppressLint;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
@ -137,7 +138,6 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
public MessageNotifier messageNotifier = null;
public Poller poller = null;
public Broadcaster broadcaster = null;
private Job firebaseInstanceIdJob;
private WindowDebouncer conversationListDebouncer;
private HandlerThread conversationListHandlerThread;
private Handler conversationListHandler;
@ -504,17 +504,9 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
});
}
public void clearAllData(boolean isMigratingToV2KeyPair) {
if (firebaseInstanceIdJob != null && firebaseInstanceIdJob.isActive()) {
firebaseInstanceIdJob.cancel(null);
}
String displayName = TextSecurePreferences.getProfileName(this);
boolean isUsingFCM = TextSecurePreferences.isPushEnabled(this);
@SuppressLint("ApplySharedPref")
public void clearAllData() {
TextSecurePreferences.clearAll(this);
if (isMigratingToV2KeyPair) {
TextSecurePreferences.setPushEnabled(this, isUsingFCM);
TextSecurePreferences.setProfileName(this, displayName);
}
getSharedPreferences(PREFERENCES_NAME, 0).edit().clear().commit();
if (!deleteDatabase(SQLCipherOpenHelper.DATABASE_NAME)) {
Log.d("Loki", "Failed to delete database.");

View File

@ -82,13 +82,13 @@ internal fun LandingScreen(
text = stringResource(R.string.urlOpenBrowser),
buttons = listOf(
DialogButtonModel(
GetString(R.string.activity_landing_terms_of_service),
GetString(R.string.AccessibilityId_terms_of_service_button),
text = GetString(R.string.activity_landing_terms_of_service),
contentDescription = GetString(R.string.AccessibilityId_terms_of_service_button),
onClick = openTerms
),
DialogButtonModel(
GetString(R.string.activity_landing_privacy_policy),
GetString(R.string.AccessibilityId_privacy_policy_button),
text = GetString(R.string.activity_landing_privacy_policy),
contentDescription = GetString(R.string.AccessibilityId_privacy_policy_button),
onClick = openPrivacyPolicy
)
)

View File

@ -32,10 +32,7 @@ class LandingActivity: BaseActionBarActivity() {
setComposeContent {
LandingScreen(
createAccount = {
prefs.setHasViewedSeed(false)
startPickDisplayNameActivity()
},
createAccount = { startPickDisplayNameActivity() },
loadAccount = { start<LoadAccountActivity>() },
openTerms = { open("https://getsession.org/terms-of-service") },
openPrivacyPolicy = { open("https://getsession.org/privacy-policy") }

View File

@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.onboarding.loadaccount
import android.os.Bundle
import androidx.activity.viewModels
import androidx.camera.core.ExperimentalGetImage
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.lifecycleScope
@ -11,14 +10,13 @@ import kotlinx.coroutines.launch
import network.loki.messenger.R
import org.session.libsession.utilities.TextSecurePreferences
import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.onboarding.loading.LoadingManager
import org.thoughtcrime.securesms.onboarding.manager.LoadingManager
import org.thoughtcrime.securesms.onboarding.messagenotifications.MessageNotificationsActivity
import org.thoughtcrime.securesms.ui.setComposeContent
import org.thoughtcrime.securesms.util.start
import javax.inject.Inject
@AndroidEntryPoint
@androidx.annotation.OptIn(ExperimentalGetImage::class)
class LoadAccountActivity : BaseActionBarActivity() {
@Inject
@ -31,7 +29,6 @@ class LoadAccountActivity : BaseActionBarActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
supportActionBar?.setTitle(R.string.activity_link_load_account)
prefs.setHasViewedSeed(true)
prefs.setConfigurationMessageSynced(false)
prefs.setRestorationTime(System.currentTimeMillis())
prefs.setLastProfileUpdateTime(0)

View File

@ -28,11 +28,6 @@ class LoadingActivity: BaseActionBarActivity() {
private val viewModel: LoadingViewModel by viewModels()
@Deprecated("Deprecated in Java")
override fun onBackPressed() {
return
}
private fun register(loadFailed: Boolean) {
prefs.setLastConfigurationSyncTime(System.currentTimeMillis())
@ -47,6 +42,8 @@ class LoadingActivity: BaseActionBarActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setUpActionBarSessionLogo()
ApplicationContext.getInstance(this).newAccount = false
setComposeContent {
@ -54,8 +51,6 @@ class LoadingActivity: BaseActionBarActivity() {
LoadingScreen(state)
}
setUpActionBarSessionLogo(true)
lifecycleScope.launch {
viewModel.events.collect {
when (it) {

View File

@ -1,4 +1,4 @@
package org.thoughtcrime.securesms.onboarding.loading
package org.thoughtcrime.securesms.onboarding.manager
import android.content.Context
import kotlinx.coroutines.CoroutineScope

View File

@ -31,7 +31,11 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import network.loki.messenger.R
import org.thoughtcrime.securesms.onboarding.messagenotifications.MessageNotificationsViewModel.UiState
import org.thoughtcrime.securesms.onboarding.ui.ContinuePrimaryOutlineButton
import org.thoughtcrime.securesms.ui.AlertDialog
import org.thoughtcrime.securesms.ui.DialogButtonModel
import org.thoughtcrime.securesms.ui.GetString
import org.thoughtcrime.securesms.ui.LocalDimensions
import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider
@ -39,6 +43,7 @@ import org.thoughtcrime.securesms.ui.base
import org.thoughtcrime.securesms.ui.color.Colors
import org.thoughtcrime.securesms.ui.color.LocalColors
import org.thoughtcrime.securesms.ui.color.transparentButtonColors
import org.thoughtcrime.securesms.ui.components.CircularProgressIndicator
import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.h4
import org.thoughtcrime.securesms.ui.h8
@ -47,10 +52,39 @@ import org.thoughtcrime.securesms.ui.small
@Composable
internal fun MessageNotificationsScreen(
state: MessageNotificationsState = MessageNotificationsState(),
state: UiState = UiState(),
setEnabled: (Boolean) -> Unit = {},
onContinue: () -> Unit = {}
onContinue: () -> Unit = {},
quit: () -> Unit = {},
dismissDialog: () -> Unit = {}
) {
if (state.clearData) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(LocalColors.current.primary)
}
return
}
if (state.showDialog) AlertDialog(
onDismissRequest = dismissDialog,
title = stringResource(R.string.warning),
text = stringResource(R.string.you_cannot_go_back_further_in_order_to_stop_loading_your_account_session_needs_to_quit),
buttons = listOf(
DialogButtonModel(
GetString(stringResource(R.string.quit)),
color = LocalColors.current.danger,
onClick = quit
),
DialogButtonModel(
GetString(stringResource(R.string.cancel))
)
)
)
Column {
Spacer(Modifier.weight(1f))
@ -105,9 +139,15 @@ private fun NotificationRadioButton(
Box(
modifier = Modifier
.weight(1f)
.border(LocalDimensions.current.borderStroke, LocalColors.current.borders, RoundedCornerShape(8.dp)),
.border(
LocalDimensions.current.borderStroke,
LocalColors.current.borders,
RoundedCornerShape(8.dp)
),
) {
Column(modifier = Modifier.padding(horizontal = 15.dp).padding(top = 10.dp, bottom = 11.dp)) {
Column(modifier = Modifier
.padding(horizontal = 15.dp)
.padding(top = 10.dp, bottom = 11.dp)) {
Text(stringResource(title), style = h8)
Text(stringResource(explanation), style = small, modifier = Modifier.padding(top = 7.dp))
@ -139,7 +179,9 @@ private fun RadioButtonIndicator(
Box(modifier = modifier) {
AnimatedVisibility(
selected,
modifier = Modifier.padding(2.5.dp).clip(CircleShape),
modifier = Modifier
.padding(2.5.dp)
.clip(CircleShape),
enter = scaleIn(),
exit = scaleOut()
) {

View File

@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.onboarding.messagenotifications
import android.app.Activity
import android.os.Bundle
import androidx.activity.viewModels
import androidx.compose.runtime.Composable
@ -12,7 +13,8 @@ import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.home.startHomeActivity
import org.thoughtcrime.securesms.notifications.PushRegistry
import org.thoughtcrime.securesms.onboarding.loading.LoadingActivity
import org.thoughtcrime.securesms.onboarding.loading.LoadingManager
import org.thoughtcrime.securesms.onboarding.manager.LoadingManager
import org.thoughtcrime.securesms.onboarding.messagenotifications.MessageNotificationsActivity.Companion.EXTRA_PROFILE_NAME
import org.thoughtcrime.securesms.ui.setComposeContent
import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo
import org.thoughtcrime.securesms.util.start
@ -21,11 +23,22 @@ import javax.inject.Inject
@AndroidEntryPoint
class MessageNotificationsActivity : BaseActionBarActivity() {
companion object {
const val EXTRA_PROFILE_NAME = "EXTRA_PROFILE_NAME"
}
@Inject
internal lateinit var viewModelFactory: MessageNotificationsViewModel.AssistedFactory
@Inject lateinit var pushRegistry: PushRegistry
@Inject lateinit var prefs: TextSecurePreferences
@Inject lateinit var loadingManager: LoadingManager
private val viewModel: MessageNotificationsViewModel by viewModels()
val profileName by lazy { intent.getStringExtra(EXTRA_PROFILE_NAME) }
private val viewModel: MessageNotificationsViewModel by viewModels {
viewModelFactory.create(profileName)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -35,14 +48,28 @@ class MessageNotificationsActivity : BaseActionBarActivity() {
setComposeContent { MessageNotificationsScreen() }
}
@Deprecated("Deprecated in Java")
override fun onBackPressed() {
if (viewModel.onBackPressed()) return
@Suppress("DEPRECATION")
super.onBackPressed()
}
@Composable
private fun MessageNotificationsScreen() {
val state by viewModel.states.collectAsState()
MessageNotificationsScreen(state, viewModel::setEnabled, ::register)
val uiState by viewModel.uiStates.collectAsState()
MessageNotificationsScreen(
uiState,
setEnabled = viewModel::setEnabled,
onContinue = ::register,
quit = viewModel::quit,
dismissDialog = viewModel::dismissDialog
)
}
private fun register() {
prefs.setPushEnabled(viewModel.states.value.pushEnabled)
prefs.setPushEnabled(viewModel.uiStates.value.pushEnabled)
ApplicationContext.getInstance(this).startPollingIfNeeded()
pushRegistry.refresh(true)
@ -52,3 +79,7 @@ class MessageNotificationsActivity : BaseActionBarActivity() {
}
}
}
fun Activity.startMessageNotificationsActivity(profileName: String) {
start<MessageNotificationsActivity> { putExtra(EXTRA_PROFILE_NAME, profileName) }
}

View File

@ -1,22 +1,83 @@
package org.thoughtcrime.securesms.onboarding.messagenotifications
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import javax.inject.Inject
import kotlinx.coroutines.launch
import org.thoughtcrime.securesms.ApplicationContext
@HiltViewModel
internal class MessageNotificationsViewModel @Inject constructor(): ViewModel() {
private val _states = MutableStateFlow(MessageNotificationsState())
val states = _states.asStateFlow()
internal class MessageNotificationsViewModel(
private val state: State,
private val application: Application
): AndroidViewModel(application) {
private val _uiStates = MutableStateFlow(UiState())
val uiStates = _uiStates.asStateFlow()
fun setEnabled(enabled: Boolean) {
_states.update { MessageNotificationsState(pushEnabled = enabled) }
_uiStates.update { UiState(pushEnabled = enabled) }
}
/**
* @return [true] if the back press was handled.
*/
fun onBackPressed(): Boolean = when (state) {
is State.CreateAccount -> false
is State.LoadAccount -> {
_uiStates.update { it.copy(showDialog = true) }
true
}
}
fun dismissDialog() {
_uiStates.update { it.copy(showDialog = false) }
}
fun quit() {
_uiStates.update { it.copy(clearData = true) }
viewModelScope.launch(Dispatchers.IO) {
ApplicationContext.getInstance(application).clearAllData()
}
}
data class UiState(
val pushEnabled: Boolean = true,
val showDialog: Boolean = false,
val clearData: Boolean = false
) {
val pushDisabled get() = !pushEnabled
}
sealed interface State {
class CreateAccount(val displayName: String): State
object LoadAccount: State
}
@dagger.assisted.AssistedFactory
interface AssistedFactory {
fun create(profileName: String?): Factory
}
@Suppress("UNCHECKED_CAST")
class Factory @AssistedInject constructor(
@Assisted private val profileName: String?,
private val application: Application
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MessageNotificationsViewModel(
state = profileName?.let(State::CreateAccount) ?: State.LoadAccount,
application = application
) as T
}
}
}
data class MessageNotificationsState(val pushEnabled: Boolean = true) {
val pushDisabled get() = !pushEnabled
}

View File

@ -13,10 +13,9 @@ import org.session.libsession.utilities.TextSecurePreferences
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.home.startHomeActivity
import org.thoughtcrime.securesms.onboarding.messagenotifications.MessageNotificationsActivity
import org.thoughtcrime.securesms.onboarding.messagenotifications.startMessageNotificationsActivity
import org.thoughtcrime.securesms.ui.setComposeContent
import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo
import org.thoughtcrime.securesms.util.start
import javax.inject.Inject
private const val EXTRA_LOAD_FAILED = "extra_load_failed"
@ -41,11 +40,12 @@ class PickDisplayNameActivity : BaseActionBarActivity() {
setComposeContent { DisplayNameScreen(viewModel) }
if (!loadFailed) prefs.setHasViewedSeed(false)
lifecycleScope.launch {
viewModel.events.collect {
if (loadFailed) startHomeActivity() else start<MessageNotificationsActivity>()
when (it) {
is Event.CreateAccount -> startMessageNotificationsActivity(it.profileName)
Event.LoadAccountComplete -> startHomeActivity()
}
}
}
}

View File

@ -49,6 +49,7 @@ internal class PickDisplayNameViewModel(
_states.update { it.copy(isTextErrorColor = false, error = null) }
prefs.setProfileName(displayName)
configFactory.user?.setName(displayName)
if (!loadFailed) {
// This is here to resolve a case where the app restarts before a user completes onboarding
@ -70,7 +71,13 @@ internal class PickDisplayNameViewModel(
prefs.setRestorationTime(0)
}
viewModelScope.launch { _events.emit(Event.DONE) }
viewModelScope.launch {
if (loadFailed) {
_events.emit(Event.LoadAccountComplete)
} else {
_events.emit(Event.CreateAccount(displayName))
}
}
}
}
}
@ -116,5 +123,6 @@ fun pickNewNameState() = State(
)
sealed interface Event {
object DONE: Event
class CreateAccount(val profileName: String): Event
object LoadAccountComplete: Event
}

View File

@ -110,7 +110,7 @@ class ClearAllDataDialog : DialogFragment() {
} catch (e: Exception) {
Log.e("Loki", "Failed to force sync", e)
}
ApplicationContext.getInstance(context).clearAllData(false)
ApplicationContext.getInstance(context).clearAllData()
withContext(Dispatchers.Main) {
dismiss()
}
@ -133,7 +133,7 @@ class ClearAllDataDialog : DialogFragment() {
}
} else if (result.values.all { it }) {
// don't force sync because all the messages are deleted?
ApplicationContext.getInstance(context).clearAllData(false)
ApplicationContext.getInstance(context).clearAllData()
withContext(Dispatchers.Main) {
dismiss()
}

View File

@ -18,6 +18,7 @@ import android.view.View
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@ -25,14 +26,9 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
@ -44,11 +40,8 @@ import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.startWith
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import network.loki.messenger.BuildConfig
import network.loki.messenger.R
import network.loki.messenger.databinding.ActivitySettingsBinding
@ -420,7 +413,9 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
Cell {
Column {
LargeItemButtonWithDrawable(R.string.activity_path_title, if (hasPaths) R.drawable.ic_status else R.drawable.ic_path_yellow) { show<PathActivity>() }
Crossfade(if (hasPaths) R.drawable.ic_status else R.drawable.ic_path_yellow, label = "path") {
LargeItemButtonWithDrawable(R.string.activity_path_title, it) { show<PathActivity>() }
}
Divider()
LargeItemButton(R.string.activity_settings_privacy_button_title, R.drawable.ic_privacy_icon) { show<PrivacySettingsActivity>() }
Divider()

View File

@ -16,7 +16,9 @@ import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import network.loki.messenger.R
@ -25,7 +27,9 @@ import org.thoughtcrime.securesms.ui.color.LocalColors
class DialogButtonModel(
val text: GetString,
val contentDescription: GetString = text,
val onClick: () -> Unit
val color: Color = Color.Unspecified,
val dismissOnClick: Boolean = true,
val onClick: () -> Unit = {},
)
@Composable
@ -33,6 +37,7 @@ fun AlertDialog(
onDismissRequest: () -> Unit,
title: String? = null,
text: String? = null,
content: @Composable () -> Unit = {},
buttons: List<DialogButtonModel>? = null
) {
androidx.compose.material.AlertDialog(
@ -76,6 +81,7 @@ fun AlertDialog(
modifier = Modifier.padding(bottom = LocalDimensions.current.xxsItemSpacing)
)
}
content()
}
buttons?.takeIf { it.isNotEmpty() }?.let {
Row(Modifier.height(IntrinsicSize.Min)) {
@ -85,10 +91,11 @@ fun AlertDialog(
modifier = Modifier
.fillMaxHeight()
.contentDescription(it.contentDescription())
.weight(1f)
.weight(1f),
color = it.color
) {
it.onClick()
onDismissRequest()
if (it.dismissOnClick) onDismissRequest()
}
}
}
@ -100,7 +107,7 @@ fun AlertDialog(
}
@Composable
fun DialogButton(text: String, modifier: Modifier, onClick: () -> Unit) {
fun DialogButton(text: String, modifier: Modifier, color: Color = Color.Unspecified, onClick: () -> Unit) {
TextButton(
modifier = modifier,
shape = RectangleShape,
@ -108,7 +115,7 @@ fun DialogButton(text: String, modifier: Modifier, onClick: () -> Unit) {
) {
Text(
text,
color = LocalColors.current.text,
color = color.takeOrElse { LocalColors.current.text },
style = largeBold,
textAlign = TextAlign.Center,
modifier = Modifier.padding(

View File

@ -26,7 +26,6 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material.ButtonColors
import androidx.compose.material.Card
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Icon
import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme
@ -69,6 +68,7 @@ import org.thoughtcrime.securesms.ui.color.LocalColors
import org.thoughtcrime.securesms.ui.color.divider
import org.thoughtcrime.securesms.ui.color.radioButtonColors
import org.thoughtcrime.securesms.ui.color.transparentButtonColors
import org.thoughtcrime.securesms.ui.components.SmallCircularProgressIndicator
import kotlin.math.min
import kotlin.math.roundToInt
@ -453,11 +453,7 @@ fun LaunchedEffectAsync(block: suspend CoroutineScope.() -> Unit) {
@Composable
fun LoadingArcOr(loading: Boolean, content: @Composable () -> Unit) {
AnimatedVisibility(loading) {
CircularProgressIndicator(
modifier = Modifier.size(20.dp),
color = LocalContentColor.current,
strokeWidth = 2.dp
)
SmallCircularProgressIndicator(color = LocalContentColor.current)
}
AnimatedVisibility(!loading) {
content()

View File

@ -0,0 +1,26 @@
package org.thoughtcrime.securesms.ui.components
import androidx.compose.foundation.layout.size
import androidx.compose.material.LocalContentColor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
@Composable
fun CircularProgressIndicator(color: Color = LocalContentColor.current) {
androidx.compose.material.CircularProgressIndicator(
modifier = Modifier.size(40.dp),
color = color,
strokeWidth = 2.dp
)
}
@Composable
fun SmallCircularProgressIndicator(color: Color = LocalContentColor.current) {
androidx.compose.material.CircularProgressIndicator(
modifier = Modifier.size(20.dp),
color = color,
strokeWidth = 2.dp
)
}

View File

@ -1140,4 +1140,7 @@
<string name="AccessibilityId_copy_button">Copy button</string>
<string name="AccessibilityId_view_qr_code">View QR code</string>
<string name="AccessibilityId_qr_code">QR code</string>
<string name="warning">Warning</string>
<string name="you_cannot_go_back_further_in_order_to_stop_loading_your_account_session_needs_to_quit">You cannot go back further. In order to stop loading your account, Session needs to quit.</string>
<string name="quit">Quit</string>
</resources>