Add hide password dialogs

This commit is contained in:
andrew 2023-11-30 00:45:53 +10:30
parent a256a04154
commit 62ef9d3ed4
6 changed files with 73 additions and 15 deletions

View File

@ -105,7 +105,7 @@ class SessionDialogBuilder(val context: Context) {
fun destructiveButton( fun destructiveButton(
@StringRes text: Int, @StringRes text: Int,
@StringRes contentDescription: Int, @StringRes contentDescription: Int = text,
listener: () -> Unit = {} listener: () -> Unit = {}
) = button( ) = button(
text, text,

View File

@ -46,6 +46,7 @@ import androidx.compose.ui.unit.dp
import network.loki.messenger.R 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.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.LocalExtraColors import org.thoughtcrime.securesms.ui.LocalExtraColors
@ -68,7 +69,7 @@ class RecoveryPasswordActivity : BaseActionBarActivity() {
ComposeView(this).apply { ComposeView(this).apply {
setContent { setContent {
RecoveryPassword(viewModel.seed, viewModel.qrBitmap) { copySeed() } RecoveryPassword(viewModel.seed, viewModel.qrBitmap, { copySeed() }) { onHide() }
} }
}.let(::setContentView) }.let(::setContentView)
} }
@ -84,6 +85,29 @@ class RecoveryPasswordActivity : BaseActionBarActivity() {
clipboard.setPrimaryClip(clip) clipboard.setPrimaryClip(clip)
Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
} }
private fun onHide() {
showSessionDialog {
title("Hide Recovery Password Permanently")
text("Without your recovery password, you cannot load your account on new devices.\n" +
"\n" +
"We strongly recommend you save your recovery password in a safe and secure place before continuing.")
destructiveButton(R.string.continue_2) { onHideConfirm() }
button(R.string.cancel) {}
}
}
private fun onHideConfirm() {
showSessionDialog {
title("Hide Recovery Password Permanently")
text("Are you sure you want to permanently hide your recovery password on this device? This cannot be undone.")
button(R.string.cancel) {}
destructiveButton(R.string.yes) {
viewModel.permanentlyHidePassword()
finish()
}
}
}
} }
@Preview @Preview
@ -97,7 +121,12 @@ fun PreviewMessageDetails(
} }
@Composable @Composable
fun RecoveryPassword(seed: String = "", qrBitmap: Bitmap? = null, copySeed:() -> Unit = {}) { fun RecoveryPassword(
seed: String = "",
qrBitmap: Bitmap? = null,
copySeed:() -> Unit = {},
onHide:() -> Unit = {}
) {
AppTheme { AppTheme {
Column( Column(
verticalArrangement = Arrangement.spacedBy(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp),
@ -105,7 +134,7 @@ fun RecoveryPassword(seed: String = "", qrBitmap: Bitmap? = null, copySeed:() ->
.padding(bottom = 16.dp) .padding(bottom = 16.dp)
) { ) {
RecoveryPasswordCell(seed, qrBitmap, copySeed) RecoveryPasswordCell(seed, qrBitmap, copySeed)
HideRecoveryPasswordCell() HideRecoveryPasswordCell(onHide)
} }
} }
} }
@ -194,7 +223,7 @@ fun RecoveryPasswordCell(seed: String = "", qrBitmap: Bitmap? = null, copySeed:(
private fun MutableState<Boolean>.toggle() { value = !value } private fun MutableState<Boolean>.toggle() { value = !value }
@Composable @Composable
fun HideRecoveryPasswordCell() { fun HideRecoveryPasswordCell(onHide: () -> Unit = {}) {
CellWithPaddingAndMargin { CellWithPaddingAndMargin {
Row { Row {
Column(Modifier.weight(1f)) { Column(Modifier.weight(1f)) {
@ -205,7 +234,7 @@ fun HideRecoveryPasswordCell() {
"Hide", "Hide",
modifier = Modifier.align(Alignment.CenterVertically), modifier = Modifier.align(Alignment.CenterVertically),
color = colorDestructive color = colorDestructive
) {} ) { onHide() }
} }
} }
} }

View File

@ -3,7 +3,9 @@ package org.thoughtcrime.securesms.onboarding.recoverypassword
import android.app.Application import android.app.Application
import android.graphics.Bitmap import android.graphics.Bitmap
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartFragmentCallback
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import org.session.libsession.utilities.AppTextSecurePreferences
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.crypto.MnemonicCodec import org.session.libsignal.crypto.MnemonicCodec
import org.session.libsignal.utilities.hexEncodedPrivateKey import org.session.libsignal.utilities.hexEncodedPrivateKey
@ -18,6 +20,12 @@ class RecoveryPasswordViewModel @Inject constructor(
private val application: Application private val application: Application
): AndroidViewModel(application) { ): AndroidViewModel(application) {
val prefs = AppTextSecurePreferences(application)
fun permanentlyHidePassword() {
prefs.setHidePassword(true)
}
val seed by lazy { val seed by lazy {
val hexEncodedSeed = IdentityKeyUtil.retrieve(application, IdentityKeyUtil.LOKI_SEED) val hexEncodedSeed = IdentityKeyUtil.retrieve(application, IdentityKeyUtil.LOKI_SEED)
?: IdentityKeyUtil.getIdentityKeyPair(application).hexEncodedPrivateKey // Legacy account ?: IdentityKeyUtil.getIdentityKeyPair(application).hexEncodedPrivateKey // Legacy account

View File

@ -19,6 +19,7 @@ import android.view.MenuItem
import android.view.View import android.view.View
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.Toast import android.widget.Toast
import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import network.loki.messenger.BuildConfig import network.loki.messenger.BuildConfig
@ -64,6 +65,10 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
@Inject @Inject
lateinit var configFactory: ConfigFactory lateinit var configFactory: ConfigFactory
@Inject
lateinit var prefs: TextSecurePreferences
private lateinit var binding: ActivitySettingsBinding private lateinit var binding: ActivitySettingsBinding
private var displayNameEditActionMode: ActionMode? = null private var displayNameEditActionMode: ActionMode? = null
@ -86,13 +91,17 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
super.onCreate(savedInstanceState, isReady) super.onCreate(savedInstanceState, isReady)
binding = ActivitySettingsBinding.inflate(layoutInflater) binding = ActivitySettingsBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
val displayName = getDisplayName()
glide = GlideApp.with(this) glide = GlideApp.with(this)
with(binding) { }
override fun onStart() {
super.onStart()
binding.run {
setupProfilePictureView(profilePictureView) setupProfilePictureView(profilePictureView)
profilePictureView.setOnClickListener { showEditProfilePictureUI() } profilePictureView.setOnClickListener { showEditProfilePictureUI() }
ctnGroupNameSection.setOnClickListener { startActionMode(DisplayNameEditActionModeCallback()) } ctnGroupNameSection.setOnClickListener { startActionMode(DisplayNameEditActionModeCallback()) }
btnGroupNameDisplay.text = displayName btnGroupNameDisplay.text = getDisplayName()
publicKeyTextView.text = hexEncodedPublicKey publicKeyTextView.text = hexEncodedPublicKey
copyButton.setOnClickListener { copyPublicKey() } copyButton.setOnClickListener { copyPublicKey() }
shareButton.setOnClickListener { sharePublicKey() } shareButton.setOnClickListener { sharePublicKey() }
@ -105,7 +114,8 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
appearanceButton.setOnClickListener { showAppearanceSettings() } appearanceButton.setOnClickListener { showAppearanceSettings() }
inviteFriendButton.setOnClickListener { sendInvitation() } inviteFriendButton.setOnClickListener { sendInvitation() }
helpButton.setOnClickListener { showHelp() } helpButton.setOnClickListener { showHelp() }
seedButton.setOnClickListener { showSeed() } passwordButton.isGone = prefs.getHidePassword()
passwordButton.setOnClickListener { showPassword() }
clearAllDataButton.setOnClickListener { clearAllData() } clearAllDataButton.setOnClickListener { clearAllData() }
versionTextView.text = String.format(getString(R.string.version_s), "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})") versionTextView.text = String.format(getString(R.string.version_s), "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
} }
@ -384,7 +394,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
show(intent) show(intent)
} }
private fun showSeed() { private fun showPassword() {
startRecoveryPasswordActivity() startRecoveryPasswordActivity()
} }

View File

@ -369,14 +369,14 @@
android:background="?colorDividerBackground" /> android:background="?colorDividerBackground" />
<RelativeLayout <RelativeLayout
android:id="@+id/seedButton" android:id="@+id/passwordButton"
android:background="?selectableItemBackground" android:background="?selectableItemBackground"
android:paddingHorizontal="@dimen/large_spacing" android:paddingHorizontal="@dimen/large_spacing"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/setting_button_height" android:layout_height="@dimen/setting_button_height"
android:contentDescription="@string/AccessibilityId_recovery_password"> android:contentDescription="@string/AccessibilityId_recovery_password">
<ImageView <ImageView
android:id="@+id/seedContainer" android:id="@+id/passwordContainer"
android:layout_width="@dimen/small_profile_picture_size" android:layout_width="@dimen/small_profile_picture_size"
android:layout_height="@dimen/small_profile_picture_size" android:layout_height="@dimen/small_profile_picture_size"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
@ -393,7 +393,7 @@
android:layout_marginHorizontal="@dimen/medium_spacing" android:layout_marginHorizontal="@dimen/medium_spacing"
android:textStyle="bold" android:textStyle="bold"
android:gravity="center" android:gravity="center"
android:layout_toEndOf="@+id/seedContainer" android:layout_toEndOf="@+id/passwordContainer"
android:text="@string/activity_settings_recovery_password_button_title" /> android:text="@string/activity_settings_recovery_password_button_title" />
</RelativeLayout> </RelativeLayout>

View File

@ -18,6 +18,7 @@ import org.session.libsession.utilities.TextSecurePreferences.Companion.CALL_NOT
import org.session.libsession.utilities.TextSecurePreferences.Companion.CLASSIC_DARK import org.session.libsession.utilities.TextSecurePreferences.Companion.CLASSIC_DARK
import org.session.libsession.utilities.TextSecurePreferences.Companion.CLASSIC_LIGHT import org.session.libsession.utilities.TextSecurePreferences.Companion.CLASSIC_LIGHT
import org.session.libsession.utilities.TextSecurePreferences.Companion.FOLLOW_SYSTEM_SETTINGS import org.session.libsession.utilities.TextSecurePreferences.Companion.FOLLOW_SYSTEM_SETTINGS
import org.session.libsession.utilities.TextSecurePreferences.Companion.HIDE_PASSWORD
import org.session.libsession.utilities.TextSecurePreferences.Companion.LAST_VACUUM_TIME import org.session.libsession.utilities.TextSecurePreferences.Companion.LAST_VACUUM_TIME
import org.session.libsession.utilities.TextSecurePreferences.Companion.LEGACY_PREF_KEY_SELECTED_UI_MODE import org.session.libsession.utilities.TextSecurePreferences.Companion.LEGACY_PREF_KEY_SELECTED_UI_MODE
import org.session.libsession.utilities.TextSecurePreferences.Companion.OCEAN_DARK import org.session.libsession.utilities.TextSecurePreferences.Companion.OCEAN_DARK
@ -30,6 +31,7 @@ import java.io.IOException
import java.util.Arrays import java.util.Arrays
import java.util.Date import java.util.Date
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton
interface TextSecurePreferences { interface TextSecurePreferences {
@ -182,6 +184,8 @@ interface TextSecurePreferences {
fun hasForcedNewConfig(): Boolean fun hasForcedNewConfig(): Boolean
fun hasPreference(key: String): Boolean fun hasPreference(key: String): Boolean
fun clearAll() fun clearAll()
fun getHidePassword(): Boolean
fun setHidePassword(value: Boolean)
companion object { companion object {
val TAG = TextSecurePreferences::class.simpleName val TAG = TextSecurePreferences::class.simpleName
@ -283,6 +287,7 @@ interface TextSecurePreferences {
const val SELECTED_STYLE = "pref_selected_style" // classic_dark/light, ocean_dark/light const val SELECTED_STYLE = "pref_selected_style" // classic_dark/light, ocean_dark/light
const val FOLLOW_SYSTEM_SETTINGS = "pref_follow_system" // follow system day/night const val FOLLOW_SYSTEM_SETTINGS = "pref_follow_system" // follow system day/night
const val HIDE_PASSWORD = "pref_hide_password"
const val LEGACY_PREF_KEY_SELECTED_UI_MODE = "SELECTED_UI_MODE" // this will be cleared upon launching app, for users migrating to theming build const val LEGACY_PREF_KEY_SELECTED_UI_MODE = "SELECTED_UI_MODE" // this will be cleared upon launching app, for users migrating to theming build
const val CLASSIC_DARK = "classic.dark" const val CLASSIC_DARK = "classic.dark"
@ -1016,6 +1021,7 @@ interface TextSecurePreferences {
} }
} }
@Singleton
class AppTextSecurePreferences @Inject constructor( class AppTextSecurePreferences @Inject constructor(
@ApplicationContext private val context: Context @ApplicationContext private val context: Context
): TextSecurePreferences { ): TextSecurePreferences {
@ -1711,4 +1717,9 @@ class AppTextSecurePreferences @Inject constructor(
getDefaultSharedPreferences(context).edit().clear().commit() getDefaultSharedPreferences(context).edit().clear().commit()
} }
} override fun getHidePassword() = getBooleanPreference(HIDE_PASSWORD, false)
override fun setHidePassword(value: Boolean) {
setBooleanPreference(HIDE_PASSWORD, value)
}
}