Add PNP settings.

This commit is contained in:
Alex Hart
2023-02-28 13:19:34 -04:00
committed by Greyson Parrelli
parent f3693c966a
commit 45a04423b0
15 changed files with 425 additions and 167 deletions

View File

@@ -1,21 +1,16 @@
package org.thoughtcrime.securesms.components.settings.app.privacy
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.style.TextAppearanceSpan
import android.view.View
import android.view.WindowManager
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.StringRes
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.biometric.BiometricPrompt.PromptInfo
@@ -41,8 +36,6 @@ import org.thoughtcrime.securesms.components.settings.PreferenceModel
import org.thoughtcrime.securesms.components.settings.PreferenceViewHolder
import org.thoughtcrime.securesms.components.settings.configure
import org.thoughtcrime.securesms.crypto.MasterSecretUtil
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues.PhoneNumberListingMode
import org.thoughtcrime.securesms.service.KeyCachingService
import org.thoughtcrime.securesms.util.CommunicationActions
import org.thoughtcrime.securesms.util.ConversationUtil
@@ -126,6 +119,19 @@ class PrivacySettingsFragment : DSLSettingsFragment(R.string.preferences__privac
private fun getConfiguration(state: PrivacySettingsState): DSLConfiguration {
return configure {
if (FeatureFlags.phoneNumberPrivacy()) {
clickPref(
title = DSLSettingsText.from(R.string.preferences_app_protection__phone_number),
summary = DSLSettingsText.from(R.string.preferences_app_protection__choose_who_can_see),
onClick = {
Navigation.findNavController(requireView())
.safeNavigate(R.id.action_privacySettingsFragment_to_phoneNumberPrivacySettingsFragment)
}
)
dividerPref()
}
clickPref(
title = DSLSettingsText.from(R.string.PrivacySettingsFragment__blocked),
summary = DSLSettingsText.from(getString(R.string.PrivacySettingsFragment__d_contacts, state.blockedCount)),
@@ -137,28 +143,6 @@ class PrivacySettingsFragment : DSLSettingsFragment(R.string.preferences__privac
dividerPref()
if (FeatureFlags.phoneNumberPrivacy()) {
sectionHeaderPref(R.string.preferences_app_protection__who_can)
clickPref(
title = DSLSettingsText.from(R.string.preferences_app_protection__see_my_phone_number),
summary = DSLSettingsText.from(getWhoCanSeeMyPhoneNumberSummary(state.seeMyPhoneNumber)),
onClick = {
onSeeMyPhoneNumberClicked(state.seeMyPhoneNumber)
}
)
clickPref(
title = DSLSettingsText.from(R.string.preferences_app_protection__find_me_by_phone_number),
summary = DSLSettingsText.from(getWhoCanFindMeByPhoneNumberSummary(state.findMeByPhoneNumber)),
onClick = {
onFindMyPhoneNumberClicked(state.findMeByPhoneNumber)
}
)
dividerPref()
}
sectionHeaderPref(R.string.PrivacySettingsFragment__messaging)
switchPref(
@@ -389,117 +373,6 @@ class PrivacySettingsFragment : DSLSettingsFragment(R.string.preferences__privac
}
}
@StringRes
private fun getWhoCanSeeMyPhoneNumberSummary(phoneNumberSharingMode: PhoneNumberPrivacyValues.PhoneNumberSharingMode): Int {
return when (phoneNumberSharingMode) {
PhoneNumberPrivacyValues.PhoneNumberSharingMode.EVERYONE -> R.string.PhoneNumberPrivacy_everyone
PhoneNumberPrivacyValues.PhoneNumberSharingMode.CONTACTS -> R.string.PhoneNumberPrivacy_my_contacts
PhoneNumberPrivacyValues.PhoneNumberSharingMode.NOBODY -> R.string.PhoneNumberPrivacy_nobody
}
}
@StringRes
private fun getWhoCanFindMeByPhoneNumberSummary(phoneNumberListingMode: PhoneNumberListingMode): Int {
return when (phoneNumberListingMode) {
PhoneNumberListingMode.LISTED -> R.string.PhoneNumberPrivacy_everyone
PhoneNumberListingMode.UNLISTED -> R.string.PhoneNumberPrivacy_nobody
}
}
private fun onSeeMyPhoneNumberClicked(phoneNumberSharingMode: PhoneNumberPrivacyValues.PhoneNumberSharingMode) {
val value = arrayOf(phoneNumberSharingMode)
val items = items(requireContext())
val modes: List<PhoneNumberPrivacyValues.PhoneNumberSharingMode> = ArrayList(items.keys)
val modeStrings = items.values.toTypedArray()
val selectedMode = modes.indexOf(value[0])
MaterialAlertDialogBuilder(requireActivity()).apply {
setTitle(R.string.preferences_app_protection__see_my_phone_number)
setCancelable(true)
setSingleChoiceItems(
modeStrings,
selectedMode
) { _: DialogInterface?, which: Int -> value[0] = modes[which] }
setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
val newSharingMode = value[0]
Log.i(
TAG,
String.format(
"PhoneNumberSharingMode changed to %s. Scheduling storage value sync",
newSharingMode
)
)
viewModel.setPhoneNumberSharingMode(value[0])
}
setNegativeButton(android.R.string.cancel, null)
show()
}
}
private fun items(context: Context): Map<PhoneNumberPrivacyValues.PhoneNumberSharingMode, CharSequence> {
val map: MutableMap<PhoneNumberPrivacyValues.PhoneNumberSharingMode, CharSequence> = LinkedHashMap()
map[PhoneNumberPrivacyValues.PhoneNumberSharingMode.EVERYONE] = titleAndDescription(
context,
context.getString(R.string.PhoneNumberPrivacy_everyone),
context.getString(R.string.PhoneNumberPrivacy_everyone_see_description)
)
map[PhoneNumberPrivacyValues.PhoneNumberSharingMode.NOBODY] =
context.getString(R.string.PhoneNumberPrivacy_nobody)
return map
}
private fun titleAndDescription(
context: Context,
header: String,
description: String
): CharSequence {
return SpannableStringBuilder().apply {
append("\n")
append(header)
append("\n")
setSpan(
TextAppearanceSpan(context, android.R.style.TextAppearance_Small),
length,
length,
Spanned.SPAN_INCLUSIVE_INCLUSIVE
)
append(description)
append("\n")
}
}
fun onFindMyPhoneNumberClicked(phoneNumberListingMode: PhoneNumberListingMode) {
val context = requireContext()
val value = arrayOf(phoneNumberListingMode)
MaterialAlertDialogBuilder(requireActivity()).apply {
setTitle(R.string.preferences_app_protection__find_me_by_phone_number)
setCancelable(true)
setSingleChoiceItems(
arrayOf(
titleAndDescription(
context,
context.getString(R.string.PhoneNumberPrivacy_everyone),
context.getString(R.string.PhoneNumberPrivacy_everyone_find_description)
),
context.getString(R.string.PhoneNumberPrivacy_nobody)
),
value[0].ordinal
) { _: DialogInterface?, which: Int -> value[0] = PhoneNumberListingMode.values()[which] }
setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
Log.i(
TAG,
String.format(
"PhoneNumberListingMode changed to %s. Scheduling storage value sync",
value[0]
)
)
viewModel.setPhoneNumberListingMode(value[0])
}
setNegativeButton(android.R.string.cancel, null)
show()
}
}
private class ValueClickPreference(
val value: DSLSettingsText,
val clickPreference: ClickPreference

View File

@@ -1,11 +1,7 @@
package org.thoughtcrime.securesms.components.settings.app.privacy
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues
data class PrivacySettingsState(
val blockedCount: Int,
val seeMyPhoneNumber: PhoneNumberPrivacyValues.PhoneNumberSharingMode,
val findMeByPhoneNumber: PhoneNumberPrivacyValues.PhoneNumberListingMode,
val readReceipts: Boolean,
val typingIndicators: Boolean,
val screenLock: Boolean,

View File

@@ -4,14 +4,8 @@ import android.content.SharedPreferences
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob
import org.thoughtcrime.securesms.jobs.RefreshOwnProfileJob
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.storage.StorageSyncHelper
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.livedata.Store
@@ -58,20 +52,6 @@ class PrivacySettingsViewModel(
refresh()
}
fun setPhoneNumberSharingMode(phoneNumberSharingMode: PhoneNumberPrivacyValues.PhoneNumberSharingMode) {
SignalStore.phoneNumberPrivacy().phoneNumberSharingMode = phoneNumberSharingMode
SignalDatabase.recipients.markNeedsSync(Recipient.self().id)
StorageSyncHelper.scheduleSyncForDataChange()
refresh()
}
fun setPhoneNumberListingMode(phoneNumberListingMode: PhoneNumberPrivacyValues.PhoneNumberListingMode) {
SignalStore.phoneNumberPrivacy().phoneNumberListingMode = phoneNumberListingMode
StorageSyncHelper.scheduleSyncForDataChange()
ApplicationDependencies.getJobManager().startChain(RefreshAttributesJob()).then(RefreshOwnProfileJob()).enqueue()
refresh()
}
fun setIncognitoKeyboard(enabled: Boolean) {
sharedPreferences.edit().putBoolean(TextSecurePreferences.INCOGNITO_KEYBORAD_PREF, enabled).apply()
refresh()
@@ -106,8 +86,6 @@ class PrivacySettingsViewModel(
screenSecurity = TextSecurePreferences.isScreenSecurityEnabled(ApplicationDependencies.getApplication()),
incognitoKeyboard = TextSecurePreferences.isIncognitoKeyboardEnabled(ApplicationDependencies.getApplication()),
paymentLock = SignalStore.paymentsValues().paymentLock,
seeMyPhoneNumber = SignalStore.phoneNumberPrivacy().phoneNumberSharingMode,
findMeByPhoneNumber = SignalStore.phoneNumberPrivacy().phoneNumberListingMode,
isObsoletePasswordEnabled = !TextSecurePreferences.isPasswordDisabled(ApplicationDependencies.getApplication()),
isObsoletePasswordTimeoutEnabled = TextSecurePreferences.isPassphraseTimeoutEnabled(ApplicationDependencies.getApplication()),
obsoletePasswordTimeout = TextSecurePreferences.getPassphraseTimeoutInterval(ApplicationDependencies.getApplication()),

View File

@@ -0,0 +1,127 @@
package org.thoughtcrime.securesms.components.settings.app.privacy.pnp
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import org.signal.core.ui.Dividers
import org.signal.core.ui.Rows
import org.signal.core.ui.Scaffolds
import org.signal.core.ui.Texts
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.compose.ComposeFragment
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues.PhoneNumberListingMode
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues.PhoneNumberSharingMode
class PhoneNumberPrivacySettingsFragment : ComposeFragment() {
private val viewModel: PhoneNumberPrivacySettingsViewModel by viewModels()
@Composable
override fun SheetContent() {
val state: PhoneNumberPrivacySettingsState by viewModel.state
val onNavigationClick: () -> Unit = remember {
{ findNavController().popBackStack() }
}
Scaffolds.Settings(
title = stringResource(id = R.string.preferences_app_protection__phone_number),
onNavigationClick = onNavigationClick,
painter = painterResource(id = R.drawable.ic_arrow_left_24),
navigationContentDescription = stringResource(id = R.string.Material3SearchToolbar__close)
) { contentPadding ->
Box(modifier = Modifier.padding(contentPadding)) {
LazyColumn {
item {
Texts.SectionHeader(
text = stringResource(id = R.string.PhoneNumberPrivacySettingsFragment__who_can_see_my_number)
)
}
item {
Rows.RadioRow(
selected = state.seeMyPhoneNumber == PhoneNumberSharingMode.EVERYONE,
text = stringResource(id = R.string.PhoneNumberPrivacy_everyone),
modifier = Modifier.clickable(onClick = viewModel::setEveryoneCanSeeMyNumber)
)
}
item {
Rows.RadioRow(
selected = state.seeMyPhoneNumber == PhoneNumberSharingMode.NOBODY,
text = stringResource(id = R.string.PhoneNumberPrivacy_nobody),
modifier = Modifier.clickable(onClick = viewModel::setNobodyCanSeeMyNumber)
)
}
item {
Text(
text = stringResource(
id = when (state.seeMyPhoneNumber) {
PhoneNumberSharingMode.EVERYONE -> R.string.PhoneNumberPrivacySettingsFragment__your_phone_number
PhoneNumberSharingMode.NOBODY -> R.string.PhoneNumberPrivacySettingsFragment__nobody_will_see
else -> error("Unexpected state $state")
}
),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(horizontal = dimensionResource(id = R.dimen.core_ui__gutter), vertical = 16.dp)
)
}
item {
Dividers.Default()
}
item {
Texts.SectionHeader(text = stringResource(id = R.string.PhoneNumberPrivacySettingsFragment__who_can_find_me_by_number))
}
item {
Rows.RadioRow(
selected = state.findMeByPhoneNumber == PhoneNumberListingMode.LISTED,
text = stringResource(id = R.string.PhoneNumberPrivacy_everyone),
modifier = Modifier.clickable(onClick = viewModel::setEveryoneCanFindMeByMyNumber)
)
}
if (state.seeMyPhoneNumber == PhoneNumberSharingMode.NOBODY) {
item {
Rows.RadioRow(
selected = state.findMeByPhoneNumber == PhoneNumberListingMode.UNLISTED,
text = stringResource(id = R.string.PhoneNumberPrivacy_nobody),
modifier = Modifier.clickable(onClick = viewModel::setNobodyCanFindMeByMyNumber)
)
}
}
item {
Text(
text = stringResource(
id = when (state.findMeByPhoneNumber) {
PhoneNumberListingMode.UNLISTED -> R.string.WhoCanSeeMyPhoneNumberFragment__nobody_on_signal
PhoneNumberListingMode.LISTED -> R.string.WhoCanSeeMyPhoneNumberFragment__anyone_who_has
}
),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(horizontal = dimensionResource(id = R.dimen.core_ui__gutter), vertical = 16.dp)
)
}
}
}
}
}
}

View File

@@ -0,0 +1,8 @@
package org.thoughtcrime.securesms.components.settings.app.privacy.pnp
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues
data class PhoneNumberPrivacySettingsState(
val seeMyPhoneNumber: PhoneNumberPrivacyValues.PhoneNumberSharingMode,
val findMeByPhoneNumber: PhoneNumberPrivacyValues.PhoneNumberListingMode
)

View File

@@ -0,0 +1,63 @@
package org.thoughtcrime.securesms.components.settings.app.privacy.pnp
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob
import org.thoughtcrime.securesms.jobs.RefreshOwnProfileJob
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues.PhoneNumberListingMode
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues.PhoneNumberSharingMode
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.storage.StorageSyncHelper
class PhoneNumberPrivacySettingsViewModel : ViewModel() {
private val _state = mutableStateOf(
PhoneNumberPrivacySettingsState(
seeMyPhoneNumber = SignalStore.phoneNumberPrivacy().phoneNumberSharingMode,
findMeByPhoneNumber = SignalStore.phoneNumberPrivacy().phoneNumberListingMode
)
)
val state: State<PhoneNumberPrivacySettingsState> = _state
fun setNobodyCanSeeMyNumber() {
setPhoneNumberSharingMode(PhoneNumberSharingMode.NOBODY)
}
fun setEveryoneCanSeeMyNumber() {
setPhoneNumberSharingMode(PhoneNumberSharingMode.EVERYONE)
}
fun setNobodyCanFindMeByMyNumber() {
setPhoneNumberListingMode(PhoneNumberListingMode.UNLISTED)
}
fun setEveryoneCanFindMeByMyNumber() {
setPhoneNumberListingMode(PhoneNumberListingMode.LISTED)
}
private fun setPhoneNumberSharingMode(phoneNumberSharingMode: PhoneNumberSharingMode) {
SignalStore.phoneNumberPrivacy().phoneNumberSharingMode = phoneNumberSharingMode
SignalDatabase.recipients.markNeedsSync(Recipient.self().id)
StorageSyncHelper.scheduleSyncForDataChange()
refresh()
}
private fun setPhoneNumberListingMode(phoneNumberListingMode: PhoneNumberListingMode) {
SignalStore.phoneNumberPrivacy().phoneNumberListingMode = phoneNumberListingMode
StorageSyncHelper.scheduleSyncForDataChange()
ApplicationDependencies.getJobManager().startChain(RefreshAttributesJob()).then(RefreshOwnProfileJob()).enqueue()
refresh()
}
fun refresh() {
_state.value = PhoneNumberPrivacySettingsState(
seeMyPhoneNumber = SignalStore.phoneNumberPrivacy().phoneNumberSharingMode,
findMeByPhoneNumber = SignalStore.phoneNumberPrivacy().phoneNumberListingMode
)
}
}