mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-23 11:15:44 +00:00
Add round checkbox composable.
Adds `RoundCheckbox` composable, which is styled to match the appearance of the other view checkboxes used in the app.
This commit is contained in:
committed by
Michelle Tang
parent
b79ec79644
commit
9867fa3f50
@@ -70,10 +70,10 @@ import org.thoughtcrime.securesms.calls.YouAreAlreadyInACallSnackbar.show
|
||||
import org.thoughtcrime.securesms.calls.log.CallLogFilter
|
||||
import org.thoughtcrime.securesms.calls.log.CallLogFragment
|
||||
import org.thoughtcrime.securesms.calls.new.NewCallActivity
|
||||
import org.thoughtcrime.securesms.components.ConnectivityWarningBottomSheet
|
||||
import org.thoughtcrime.securesms.components.DebugLogsPromptDialogFragment
|
||||
import org.thoughtcrime.securesms.components.DeviceSpecificNotificationBottomSheet
|
||||
import org.thoughtcrime.securesms.components.PromptBatterySaverDialogFragment
|
||||
import org.thoughtcrime.securesms.components.compose.ConnectivityWarningBottomSheet
|
||||
import org.thoughtcrime.securesms.components.compose.DeviceSpecificNotificationBottomSheet
|
||||
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity
|
||||
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity.Companion.manageSubscriptions
|
||||
import org.thoughtcrime.securesms.components.settings.app.notifications.manual.NotificationProfileSelectionFragment
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
package org.thoughtcrime.securesms.components
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.components.compose
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -3,7 +3,7 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.components
|
||||
package org.thoughtcrime.securesms.components.compose
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
@@ -1,4 +1,9 @@
|
||||
package org.thoughtcrime.securesms.components
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.components.compose
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.components.compose
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.scaleIn
|
||||
import androidx.compose.animation.scaleOut
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.role
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.signal.core.ui.compose.SignalPreview
|
||||
import org.signal.core.ui.compose.theme.SignalTheme
|
||||
import org.thoughtcrime.securesms.R
|
||||
|
||||
/**
|
||||
* A custom circular [Checkbox] that can be toggled between checked an unchecked states.
|
||||
*
|
||||
* @param checked Indicates whether the checkbox is checked or not.
|
||||
* @param onCheckedChange A callback function invoked when this checkbox is clicked.
|
||||
* @param modifier The [Modifier] to be applied to this checkbox.
|
||||
*/
|
||||
@Composable
|
||||
fun RoundCheckbox(
|
||||
checked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val contentDescription = if (checked) {
|
||||
stringResource(R.string.SignalCheckbox_accessibility_checked_description)
|
||||
} else {
|
||||
stringResource(R.string.SignalCheckbox_accessibility_unchecked_description)
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.padding(12.dp)
|
||||
.size(24.dp)
|
||||
.aspectRatio(1f)
|
||||
.border(
|
||||
width = 1.5.dp,
|
||||
color = if (checked) {
|
||||
MaterialTheme.colorScheme.primary
|
||||
} else {
|
||||
MaterialTheme.colorScheme.outline
|
||||
},
|
||||
shape = CircleShape
|
||||
)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null,
|
||||
onClick = { onCheckedChange(!checked) },
|
||||
onClickLabel = stringResource(R.string.SignalCheckbox_accessibility_on_click_label)
|
||||
)
|
||||
.semantics(mergeDescendants = true) {
|
||||
this.role = Role.Checkbox
|
||||
this.contentDescription = contentDescription
|
||||
}
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = checked,
|
||||
enter = fadeIn(animationSpec = tween(durationMillis = 150)) + scaleIn(initialScale = 1.20f, animationSpec = tween(durationMillis = 500)),
|
||||
exit = fadeOut(animationSpec = tween(durationMillis = 300)) + scaleOut(targetScale = 0.50f, animationSpec = tween(durationMillis = 600))
|
||||
) {
|
||||
Image(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_check_circle_solid_24),
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.primary),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SignalPreview
|
||||
@Composable
|
||||
private fun RoundCheckboxCheckedPreview() = SignalTheme {
|
||||
RoundCheckbox(checked = true, onCheckedChange = {})
|
||||
}
|
||||
|
||||
@SignalPreview
|
||||
@Composable
|
||||
private fun RoundCheckboxUncheckedPreview() = SignalTheme {
|
||||
RoundCheckbox(checked = false, onCheckedChange = {})
|
||||
}
|
||||
@@ -28,7 +28,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
@@ -83,6 +82,7 @@ import org.signal.core.util.getLength
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.attachments.AttachmentId
|
||||
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
|
||||
import org.thoughtcrime.securesms.components.compose.RoundCheckbox
|
||||
import org.thoughtcrime.securesms.components.settings.app.internal.backup.InternalBackupPlaygroundViewModel.DialogState
|
||||
import org.thoughtcrime.securesms.components.settings.app.internal.backup.InternalBackupPlaygroundViewModel.ScreenState
|
||||
import org.thoughtcrime.securesms.compose.ComposeFragment
|
||||
@@ -629,7 +629,7 @@ fun MediaList(
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||
) {
|
||||
if (selectionState.selecting) {
|
||||
Checkbox(
|
||||
RoundCheckbox(
|
||||
checked = selectionState.selected.contains(attachment.id),
|
||||
onCheckedChange = { selected ->
|
||||
selectionState = selectionState.copy(selected = if (selected) selectionState.selected + attachment.id else selectionState.selected - attachment.id)
|
||||
|
||||
@@ -122,7 +122,6 @@ import org.thoughtcrime.securesms.calls.YouAreAlreadyInACallSnackbar
|
||||
import org.thoughtcrime.securesms.components.AnimatingToggle
|
||||
import org.thoughtcrime.securesms.components.ComposeText
|
||||
import org.thoughtcrime.securesms.components.ConversationSearchBottomBar
|
||||
import org.thoughtcrime.securesms.components.DeleteSyncEducationDialog
|
||||
import org.thoughtcrime.securesms.components.HidingLinearLayout
|
||||
import org.thoughtcrime.securesms.components.InputAwareConstraintLayout
|
||||
import org.thoughtcrime.securesms.components.InputPanel
|
||||
@@ -130,6 +129,7 @@ import org.thoughtcrime.securesms.components.InsetAwareConstraintLayout
|
||||
import org.thoughtcrime.securesms.components.ScrollToPositionDelegate
|
||||
import org.thoughtcrime.securesms.components.SendButton
|
||||
import org.thoughtcrime.securesms.components.ViewBinderDelegate
|
||||
import org.thoughtcrime.securesms.components.compose.DeleteSyncEducationDialog
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiEventListener
|
||||
import org.thoughtcrime.securesms.components.emoji.MediaKeyboard
|
||||
import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel
|
||||
|
||||
@@ -98,7 +98,7 @@ import org.thoughtcrime.securesms.banner.banners.OutdatedBuildBanner;
|
||||
import org.thoughtcrime.securesms.banner.banners.ServiceOutageBanner;
|
||||
import org.thoughtcrime.securesms.banner.banners.UnauthorizedBanner;
|
||||
import org.thoughtcrime.securesms.banner.banners.UsernameOutOfSyncBanner;
|
||||
import org.thoughtcrime.securesms.components.DeleteSyncEducationDialog;
|
||||
import org.thoughtcrime.securesms.components.compose.DeleteSyncEducationDialog;
|
||||
import org.thoughtcrime.securesms.components.RatingManager;
|
||||
import org.thoughtcrime.securesms.components.SignalProgressDialog;
|
||||
import org.thoughtcrime.securesms.components.menu.ActionItem;
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.LoggingFragment;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
import org.thoughtcrime.securesms.components.DeleteSyncEducationDialog;
|
||||
import org.thoughtcrime.securesms.components.compose.DeleteSyncEducationDialog;
|
||||
import org.thoughtcrime.securesms.components.menu.ActionItem;
|
||||
import org.thoughtcrime.securesms.components.menu.SignalBottomActionBar;
|
||||
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController;
|
||||
|
||||
@@ -47,8 +47,8 @@ import org.thoughtcrime.securesms.LoggingFragment
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.attachments.AttachmentSaver
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment
|
||||
import org.thoughtcrime.securesms.components.DeleteSyncEducationDialog
|
||||
import org.thoughtcrime.securesms.components.ViewBinderDelegate
|
||||
import org.thoughtcrime.securesms.components.compose.DeleteSyncEducationDialog
|
||||
import org.thoughtcrime.securesms.components.mention.MentionAnnotation
|
||||
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragment
|
||||
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs
|
||||
|
||||
@@ -19,7 +19,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
@@ -37,6 +36,7 @@ import org.signal.core.ui.compose.SignalPreview
|
||||
import org.signal.core.ui.compose.theme.SignalTheme
|
||||
import org.signal.core.util.nullIfBlank
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.compose.RoundCheckbox
|
||||
import org.thoughtcrime.securesms.components.transfercontrols.TransferProgressIndicator
|
||||
import org.thoughtcrime.securesms.components.transfercontrols.TransferProgressState
|
||||
import org.thoughtcrime.securesms.compose.GlideImage
|
||||
@@ -158,14 +158,14 @@ fun InstalledStickerPackRow(
|
||||
color = if (selected) SignalTheme.colors.colorSurface2 else MaterialTheme.colorScheme.surface,
|
||||
shape = RoundedCornerShape(18.dp)
|
||||
)
|
||||
.padding(vertical = 10.dp)
|
||||
.padding(horizontal = 4.dp, vertical = 10.dp)
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = multiSelectEnabled,
|
||||
enter = fadeIn() + expandHorizontally(),
|
||||
exit = fadeOut() + shrinkHorizontally()
|
||||
) {
|
||||
Checkbox(
|
||||
RoundCheckbox(
|
||||
checked = selected,
|
||||
onCheckedChange = { onSelectionToggle(pack) },
|
||||
modifier = Modifier.padding(end = 8.dp)
|
||||
|
||||
@@ -8498,5 +8498,12 @@
|
||||
<!-- Accessibility label for a button displayed in the toolbar to return to the previous screen. -->
|
||||
<string name="DefaultTopAppBar__navigate_up_content_description">Navigate up</string>
|
||||
|
||||
<!-- Accessibility label describing the action that occurs when clicking a checkbox. -->
|
||||
<string name="SignalCheckbox_accessibility_on_click_label">Toggle</string>
|
||||
<!-- Accessibility label describing a checked checkbox. -->
|
||||
<string name="SignalCheckbox_accessibility_checked_description">Ticked</string>
|
||||
<!-- Accessibility label describing an unchecked checkbox. -->
|
||||
<string name="SignalCheckbox_accessibility_unchecked_description">Unticked</string>
|
||||
|
||||
<!-- EOF -->
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user