mirror of
https://github.com/bitwarden/android.git
synced 2026-05-17 03:59:18 -05:00
Compare commits
1 Commits
main
...
PM-37573-d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60487c18d1 |
@@ -23,6 +23,7 @@ import com.bitwarden.cxf.ui.composition.LocalCredentialExchangeRequestValidator
|
||||
import com.bitwarden.cxf.validator.CredentialExchangeRequestValidator
|
||||
import com.bitwarden.cxf.validator.dsl.credentialExchangeRequestValidator
|
||||
import com.bitwarden.ui.platform.composition.LocalCardTextAnalyzer
|
||||
import com.bitwarden.ui.platform.composition.LocalClock
|
||||
import com.bitwarden.ui.platform.composition.LocalExitManager
|
||||
import com.bitwarden.ui.platform.composition.LocalIntentManager
|
||||
import com.bitwarden.ui.platform.composition.LocalQrCodeAnalyzer
|
||||
@@ -134,11 +135,6 @@ val LocalBiometricsManager: ProvidableCompositionLocal<BiometricsManager> = comp
|
||||
error("CompositionLocal BiometricsManager not present")
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides access to the clock throughout the app.
|
||||
*/
|
||||
val LocalClock: ProvidableCompositionLocal<Clock> = compositionLocalOf { Clock.systemDefaultZone() }
|
||||
|
||||
/**
|
||||
* Provides access to the Auth Tab launchers throughout the app.
|
||||
*/
|
||||
|
||||
@@ -13,10 +13,10 @@ import androidx.compose.ui.unit.dp
|
||||
import com.bitwarden.core.data.util.toFormattedDateTimeStyle
|
||||
import com.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton
|
||||
import com.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.bitwarden.ui.platform.composition.LocalClock
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.bitwarden.ui.util.Text
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalClock
|
||||
import kotlinx.collections.immutable.persistentMapOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@@ -11,10 +11,10 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton
|
||||
import com.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.bitwarden.ui.platform.composition.LocalClock
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.bitwarden.ui.util.Text
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalClock
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.bitwarden.ui.platform.components.dialog
|
||||
|
||||
import androidx.compose.material3.DatePicker
|
||||
import androidx.compose.material3.DatePickerColors
|
||||
import androidx.compose.material3.DatePickerDialog
|
||||
import androidx.compose.material3.rememberDatePickerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.semantics.testTag
|
||||
import androidx.compose.ui.semantics.testTagsAsResourceId
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.bitwarden.ui.platform.components.field.color.bitwardenTextFieldColors
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.ZoneOffset
|
||||
|
||||
/**
|
||||
* A custom composable representing a dialog that displays a date picker.
|
||||
*
|
||||
* @param initialDate The initial [LocalDate] to display.
|
||||
* @param onDateSelect The callback invoked with the selected [LocalDate] when the user confirms.
|
||||
* @param onDismissRequest The callback invoked when the dialog is dismissed.
|
||||
*/
|
||||
@Composable
|
||||
fun BitwardenDatePickerDialog(
|
||||
initialDate: LocalDate?,
|
||||
onDateSelect: (LocalDate?) -> Unit,
|
||||
onDismissRequest: () -> Unit,
|
||||
) {
|
||||
val datePickerState = rememberDatePickerState(
|
||||
initialSelectedDateMillis = initialDate
|
||||
?.atStartOfDay(ZoneOffset.UTC)
|
||||
?.toInstant()
|
||||
?.toEpochMilli(),
|
||||
)
|
||||
DatePickerDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
confirmButton = {
|
||||
BitwardenTextButton(
|
||||
label = stringResource(id = BitwardenString.okay),
|
||||
onClick = {
|
||||
datePickerState
|
||||
.selectedDateMillis
|
||||
?.let { millis ->
|
||||
Instant
|
||||
.ofEpochMilli(millis)
|
||||
.atZone(ZoneOffset.UTC)
|
||||
.toLocalDate()
|
||||
}
|
||||
.let(onDateSelect)
|
||||
},
|
||||
modifier = Modifier.testTag(tag = "AcceptAlertButton"),
|
||||
)
|
||||
},
|
||||
dismissButton = {
|
||||
BitwardenTextButton(
|
||||
label = stringResource(id = BitwardenString.clear),
|
||||
onClick = { onDateSelect(null) },
|
||||
contentColor = BitwardenTheme.colorScheme.status.error,
|
||||
modifier = Modifier.testTag(tag = "ClearButton"),
|
||||
)
|
||||
BitwardenTextButton(
|
||||
label = stringResource(id = BitwardenString.cancel),
|
||||
onClick = onDismissRequest,
|
||||
modifier = Modifier.testTag(tag = "DismissAlertButton"),
|
||||
)
|
||||
},
|
||||
shape = BitwardenTheme.shapes.dialog,
|
||||
colors = bitwardenDatePickerColors(),
|
||||
modifier = Modifier.semantics {
|
||||
testTagsAsResourceId = true
|
||||
testTag = "DatePickerDialog"
|
||||
},
|
||||
) {
|
||||
DatePicker(
|
||||
state = datePickerState,
|
||||
colors = bitwardenDatePickerColors(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun bitwardenDatePickerColors(): DatePickerColors = DatePickerColors(
|
||||
containerColor = BitwardenTheme.colorScheme.background.primary,
|
||||
titleContentColor = BitwardenTheme.colorScheme.text.secondary,
|
||||
headlineContentColor = BitwardenTheme.colorScheme.text.primary,
|
||||
weekdayContentColor = BitwardenTheme.colorScheme.text.secondary,
|
||||
subheadContentColor = BitwardenTheme.colorScheme.text.secondary,
|
||||
navigationContentColor = BitwardenTheme.colorScheme.icon.primary,
|
||||
yearContentColor = BitwardenTheme.colorScheme.text.primary,
|
||||
disabledYearContentColor = BitwardenTheme.colorScheme.filledButton.foregroundDisabled,
|
||||
currentYearContentColor = BitwardenTheme.colorScheme.text.primary,
|
||||
selectedYearContentColor = BitwardenTheme.colorScheme.filledButton.foreground,
|
||||
selectedYearContainerColor = BitwardenTheme.colorScheme.filledButton.background,
|
||||
disabledSelectedYearContentColor = BitwardenTheme.colorScheme.filledButton.foregroundDisabled,
|
||||
disabledSelectedYearContainerColor = BitwardenTheme.colorScheme.filledButton.backgroundDisabled,
|
||||
dayContentColor = BitwardenTheme.colorScheme.text.primary,
|
||||
disabledDayContentColor = BitwardenTheme.colorScheme.filledButton.foregroundDisabled,
|
||||
disabledSelectedDayContentColor = BitwardenTheme.colorScheme.filledButton.foregroundDisabled,
|
||||
disabledSelectedDayContainerColor = BitwardenTheme.colorScheme.filledButton.backgroundDisabled,
|
||||
selectedDayContentColor = BitwardenTheme.colorScheme.filledButton.foreground,
|
||||
selectedDayContainerColor = BitwardenTheme.colorScheme.filledButton.background,
|
||||
todayContentColor = BitwardenTheme.colorScheme.text.primary,
|
||||
todayDateBorderColor = BitwardenTheme.colorScheme.filledButton.background,
|
||||
dividerColor = BitwardenTheme.colorScheme.stroke.divider,
|
||||
dayInSelectionRangeContainerColor = BitwardenTheme.colorScheme.filledButton.background,
|
||||
dayInSelectionRangeContentColor = BitwardenTheme.colorScheme.text.primary,
|
||||
dateTextFieldColors = bitwardenTextFieldColors(
|
||||
focusedIndicatorColor = BitwardenTheme.colorScheme.outlineButton.border,
|
||||
unfocusedIndicatorColor = BitwardenTheme.colorScheme.outlineButton.border,
|
||||
disabledIndicatorColor = BitwardenTheme.colorScheme.outlineButton.border,
|
||||
),
|
||||
)
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Preview
|
||||
@Composable
|
||||
private fun BitwardenDatePickerDialog_preview() {
|
||||
BitwardenTheme {
|
||||
BitwardenDatePickerDialog(
|
||||
initialDate = LocalDate.of(2026, 5, 2),
|
||||
onDateSelect = {},
|
||||
onDismissRequest = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.bitwarden.ui.platform.components.dropdown
|
||||
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.bitwarden.core.data.util.toFormattedDateStyle
|
||||
import com.bitwarden.ui.platform.components.button.BitwardenTextSelectionButton
|
||||
import com.bitwarden.ui.platform.components.button.model.BitwardenHelpButtonData
|
||||
import com.bitwarden.ui.platform.components.dialog.BitwardenDatePickerDialog
|
||||
import com.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.bitwarden.ui.platform.composition.LocalClock
|
||||
import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import java.time.Clock
|
||||
import java.time.LocalDate
|
||||
import java.time.format.FormatStyle
|
||||
|
||||
/**
|
||||
* A button that displays a selected date and opens a date picker dialog when clicked.
|
||||
*
|
||||
* @param label The descriptive text label for the [OutlinedTextField].
|
||||
* @param currentDate The currently selected [LocalDate] value.
|
||||
* @param onDateSelect A lambda invoked with the newly selected [LocalDate] when confirmed.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
* @param modifier A [Modifier] that you can use to apply custom modifications to the composable.
|
||||
* @param isEnabled Whether the button is enabled.
|
||||
* @param supportingContent An optional supporting content that will appear below the button.
|
||||
* @param helpData An optional [BitwardenHelpButtonData], representing the help button.
|
||||
* @param insets Inner padding to be applied within the card.
|
||||
* @param textFieldTestTag The optional test tag associated with the inner text field.
|
||||
* @param actionsPadding Padding to be applied to the [actions] block.
|
||||
* @param actions A lambda containing the set of actions (usually icons or similar) to display
|
||||
* in the trailing side. This lambda extends [RowScope], allowing flexibility in defining the
|
||||
* layout of the actions.
|
||||
*/
|
||||
@Composable
|
||||
fun BitwardenDatePickerButton(
|
||||
label: String,
|
||||
currentDate: LocalDate?,
|
||||
onDateSelect: (LocalDate?) -> Unit,
|
||||
cardStyle: CardStyle?,
|
||||
modifier: Modifier = Modifier,
|
||||
isEnabled: Boolean = true,
|
||||
supportingContent: @Composable (ColumnScope.() -> Unit)? = null,
|
||||
helpData: BitwardenHelpButtonData? = null,
|
||||
insets: PaddingValues = PaddingValues(),
|
||||
textFieldTestTag: String? = null,
|
||||
actionsPadding: PaddingValues = PaddingValues(end = 4.dp),
|
||||
clock: Clock = LocalClock.current,
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
) {
|
||||
var shouldShowDialog by rememberSaveable { mutableStateOf(value = false) }
|
||||
BitwardenTextSelectionButton(
|
||||
label = label,
|
||||
selectedOption = currentDate?.toFormattedDateStyle(
|
||||
dateStyle = FormatStyle.LONG,
|
||||
clock = clock,
|
||||
),
|
||||
onClick = { shouldShowDialog = true },
|
||||
cardStyle = cardStyle,
|
||||
enabled = isEnabled,
|
||||
showChevron = true,
|
||||
supportingContent = supportingContent,
|
||||
helpData = helpData,
|
||||
insets = insets,
|
||||
textFieldTestTag = textFieldTestTag,
|
||||
actionsPadding = actionsPadding,
|
||||
actions = actions,
|
||||
modifier = modifier,
|
||||
)
|
||||
if (shouldShowDialog) {
|
||||
BitwardenDatePickerDialog(
|
||||
initialDate = currentDate,
|
||||
onDateSelect = { date ->
|
||||
onDateSelect(date)
|
||||
shouldShowDialog = false
|
||||
},
|
||||
onDismissRequest = { shouldShowDialog = false },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Preview
|
||||
@Composable
|
||||
private fun BitwardenDatePickerButton_preview() {
|
||||
BitwardenTheme {
|
||||
BitwardenDatePickerButton(
|
||||
label = "Date of birth",
|
||||
currentDate = LocalDate.of(2026, 6, 15),
|
||||
onDateSelect = {},
|
||||
cardStyle = CardStyle.Full,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,9 @@ fun bitwardenTextFieldColors(
|
||||
disabledLabelColor: Color = BitwardenTheme.colorScheme.filledButton.foregroundDisabled,
|
||||
disabledPlaceholderColor: Color = BitwardenTheme.colorScheme.text.secondary,
|
||||
disabledSupportingTextColor: Color = BitwardenTheme.colorScheme.filledButton.foregroundDisabled,
|
||||
focusedIndicatorColor: Color = Color.Transparent,
|
||||
unfocusedIndicatorColor: Color = Color.Transparent,
|
||||
disabledIndicatorColor: Color = Color.Transparent,
|
||||
): TextFieldColors = TextFieldColors(
|
||||
focusedTextColor = textColor,
|
||||
unfocusedTextColor = textColor,
|
||||
@@ -46,9 +49,9 @@ fun bitwardenTextFieldColors(
|
||||
handleColor = BitwardenTheme.colorScheme.stroke.border,
|
||||
backgroundColor = BitwardenTheme.colorScheme.stroke.border.copy(alpha = 0.4f),
|
||||
),
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
disabledIndicatorColor = Color.Transparent,
|
||||
focusedIndicatorColor = focusedIndicatorColor,
|
||||
unfocusedIndicatorColor = unfocusedIndicatorColor,
|
||||
disabledIndicatorColor = disabledIndicatorColor,
|
||||
errorIndicatorColor = BitwardenTheme.colorScheme.status.error,
|
||||
focusedLeadingIconColor = BitwardenTheme.colorScheme.icon.primary,
|
||||
unfocusedLeadingIconColor = BitwardenTheme.colorScheme.icon.primary,
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.bitwarden.ui.platform.feature.cardscanner.util.CardTextAnalyzer
|
||||
import com.bitwarden.ui.platform.feature.qrcodescan.util.QrCodeAnalyzer
|
||||
import com.bitwarden.ui.platform.manager.IntentManager
|
||||
import com.bitwarden.ui.platform.manager.exit.ExitManager
|
||||
import java.time.Clock
|
||||
|
||||
/**
|
||||
* Provides access to the exit manager throughout the app.
|
||||
@@ -29,6 +30,11 @@ val LocalCardTextAnalyzer: ProvidableCompositionLocal<CardTextAnalyzer> =
|
||||
error("CompositionLocal LocalCardTextAnalyzer not present")
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides access to the clock throughout the app.
|
||||
*/
|
||||
val LocalClock: ProvidableCompositionLocal<Clock> = compositionLocalOf { Clock.systemDefaultZone() }
|
||||
|
||||
/**
|
||||
* Provides access to the QR Code Analyzer throughout the app.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user