Add PNP settings.

main
Alex Hart 2023-02-28 13:19:34 -04:00 zatwierdzone przez Greyson Parrelli
rodzic f3693c966a
commit 45a04423b0
15 zmienionych plików z 425 dodań i 167 usunięć

Wyświetl plik

@ -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

Wyświetl plik

@ -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,

Wyświetl plik

@ -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()),

Wyświetl plik

@ -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)
)
}
}
}
}
}
}

Wyświetl plik

@ -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
)

Wyświetl plik

@ -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
)
}
}

Wyświetl plik

@ -35,6 +35,13 @@
app:exitAnim="@anim/fragment_open_exit"
app:popEnterAnim="@anim/fragment_close_enter"
app:popExitAnim="@anim/fragment_close_exit" />
<action
android:id="@+id/action_privacySettingsFragment_to_phoneNumberPrivacySettingsFragment"
app:destination="@id/phoneNumberPrivacySettingsFragment"
app:enterAnim="@anim/fragment_open_enter"
app:exitAnim="@anim/fragment_open_exit"
app:popEnterAnim="@anim/fragment_close_enter"
app:popExitAnim="@anim/fragment_close_exit" />
</fragment>
@ -48,6 +55,11 @@
android:name="org.thoughtcrime.securesms.components.settings.app.privacy.advanced.AdvancedPrivacySettingsFragment"
android:label="advanced_privacy_settings_fragment" />
<fragment
android:id="@+id/phoneNumberPrivacySettingsFragment"
android:name="org.thoughtcrime.securesms.components.settings.app.privacy.pnp.PhoneNumberPrivacySettingsFragment"
android:label="phone_number_privacy_settings_fragment" />
<include app:graph="@navigation/app_settings_expire_timer" />
</navigation>

Wyświetl plik

@ -3458,6 +3458,18 @@
<string name="BackupUtil_unknown">Unknown</string>
<string name="preferences_app_protection__see_my_phone_number">See my phone number</string>
<string name="preferences_app_protection__find_me_by_phone_number">Find me by phone number</string>
<!-- Phone number heading displayed as a screen title -->
<string name="preferences_app_protection__phone_number">Phone number</string>
<!-- Subtext below option to launch into phone number privacy settings screen -->
<string name="preferences_app_protection__choose_who_can_see">Choose who can see your phone number and who can contact you on Signal with it.</string>
<!-- Section title above two radio buttons for enabling and disabling phone number display -->
<string name="PhoneNumberPrivacySettingsFragment__who_can_see_my_number">Who can see my number</string>
<!-- Subtext below radio buttons when who can see my number is set to nobody -->
<string name="PhoneNumberPrivacySettingsFragment__nobody_will_see">Nobody will see your phone number on Signal</string>
<!-- Section title above two radio buttons for enabling and disabling whether users can find me by my phone number -->
<string name="PhoneNumberPrivacySettingsFragment__who_can_find_me_by_number">Who can find me by number</string>
<!-- Subtext below radio buttons when who can see my number is set to everyone -->
<string name="PhoneNumberPrivacySettingsFragment__your_phone_number">Your phone number will be visible to people and groups you message. People who have your number in their phone contacts will also see it on Signal.</string>
<string name="PhoneNumberPrivacy_everyone">Everyone</string>
<string name="PhoneNumberPrivacy_my_contacts">My contacts</string>
<string name="PhoneNumberPrivacy_nobody">Nobody</string>

Wyświetl plik

@ -0,0 +1,32 @@
package org.signal.core.ui
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.signal.core.ui.theme.SignalTheme
/**
* Thin divider lines for separating content.
*/
object Dividers {
@Composable
fun Default(modifier: Modifier = Modifier) {
Divider(
thickness = 1.5.dp,
color = MaterialTheme.colorScheme.surfaceVariant,
modifier = modifier.padding(vertical = 16.25.dp)
)
}
}
@Preview
@Composable
private fun DefaultPreview() {
SignalTheme(isDarkMode = false) {
Dividers.Default()
}
}

Wyświetl plik

@ -0,0 +1,55 @@
package org.signal.core.ui
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.signal.core.ui.theme.SignalTheme
object Rows {
/**
* A row consisting of a radio button and text, which takes up the full
* width of the screen.
*/
@Composable
fun RadioRow(
selected: Boolean,
text: String,
modifier: Modifier = Modifier
) {
Row(
modifier = modifier
.fillMaxWidth()
.padding(
horizontal = dimensionResource(id = R.dimen.core_ui__gutter),
vertical = 16.dp
)
) {
RadioButton(
selected = selected,
onClick = null,
modifier = Modifier.padding(end = 24.dp)
)
Text(
text = text,
style = MaterialTheme.typography.bodyLarge
)
}
}
}
@Preview
@Composable
private fun RadioRowPreview() {
SignalTheme(isDarkMode = false) {
Rows.RadioRow(true, "RadioRow")
}
}

Wyświetl plik

@ -0,0 +1,54 @@
package org.signal.core.ui
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.unit.dp
@OptIn(ExperimentalMaterial3Api::class)
object Scaffolds {
@Composable
fun Settings(
title: String,
onNavigationClick: () -> Unit,
painter: Painter,
modifier: Modifier = Modifier,
navigationContentDescription: String? = null,
content: @Composable (PaddingValues) -> Unit
) {
Scaffold(
topBar = {
TopAppBar(
title = {
Text(
text = title,
style = MaterialTheme.typography.titleLarge
)
},
navigationIcon = {
IconButton(
onClick = onNavigationClick,
Modifier.padding(end = 16.dp)
) {
Icon(
painter = painter,
contentDescription = navigationContentDescription
)
}
}
)
},
modifier = modifier,
content = content
)
}
}

Wyświetl plik

@ -0,0 +1,40 @@
package org.signal.core.ui
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.signal.core.ui.theme.SignalTheme
object Texts {
/**
* Header row for settings pages.
*/
@Composable
fun SectionHeader(
text: String,
modifier: Modifier = Modifier
) {
Text(
text = text,
style = MaterialTheme.typography.titleSmall,
modifier = modifier
.padding(
horizontal = dimensionResource(id = R.dimen.core_ui__gutter)
)
.padding(top = 16.dp, bottom = 12.dp)
)
}
}
@Preview
@Composable
private fun SectionHeaderPreview() {
SignalTheme(isDarkMode = false) {
Texts.SectionHeader("Header")
}
}

Wyświetl plik

@ -11,7 +11,7 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.unit.sp
private val typography = Typography().apply {
private val typography = Typography().run {
copy(
headlineLarge = headlineLarge.copy(
lineHeight = 40.sp,

Wyświetl plik

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="core_ui__gutter">24dp</dimen>
</resources>

Wyświetl plik

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="core_ui__gutter">16dp</dimen>
</resources>