summaryrefslogtreecommitdiff
path: root/feature/settings/src/main/java/com/google/jetpackcamera/settings/ui/SettingsComponents.kt
diff options
context:
space:
mode:
Diffstat (limited to 'feature/settings/src/main/java/com/google/jetpackcamera/settings/ui/SettingsComponents.kt')
-rw-r--r--feature/settings/src/main/java/com/google/jetpackcamera/settings/ui/SettingsComponents.kt376
1 files changed, 376 insertions, 0 deletions
diff --git a/feature/settings/src/main/java/com/google/jetpackcamera/settings/ui/SettingsComponents.kt b/feature/settings/src/main/java/com/google/jetpackcamera/settings/ui/SettingsComponents.kt
new file mode 100644
index 0000000..738d489
--- /dev/null
+++ b/feature/settings/src/main/java/com/google/jetpackcamera/settings/ui/SettingsComponents.kt
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.jetpackcamera.settings.ui
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.selection.selectable
+import androidx.compose.foundation.selection.selectableGroup
+import androidx.compose.foundation.selection.toggleable
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.ListItem
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.RadioButton
+import androidx.compose.material3.Switch
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.google.jetpackcamera.settings.R
+import com.google.jetpackcamera.settings.model.AspectRatio
+import com.google.jetpackcamera.settings.model.CameraAppSettings
+import com.google.jetpackcamera.settings.model.CaptureMode
+import com.google.jetpackcamera.settings.model.DarkMode
+import com.google.jetpackcamera.settings.model.FlashMode
+
+/**
+ * MAJOR SETTING UI COMPONENTS
+ * these are ready to be popped into the ui
+ */
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun SettingsPageHeader(modifier: Modifier = Modifier, title: String, navBack: () -> Unit) {
+ TopAppBar(
+ modifier = modifier,
+ title = {
+ Text(title)
+ },
+ navigationIcon = {
+ IconButton(onClick = { navBack() }) {
+ Icon(Icons.AutoMirrored.Filled.ArrowBack, stringResource(id = R.string.nav_back_accessibility))
+ }
+ }
+ )
+}
+
+@Composable
+fun SectionHeader(modifier: Modifier = Modifier, title: String) {
+ Text(
+ modifier = modifier
+ .padding(start = 20.dp, top = 10.dp),
+ text = title,
+ color = MaterialTheme.colorScheme.primary,
+ fontSize = 18.sp
+ )
+}
+
+@Composable
+fun DefaultCameraFacing(
+ modifier: Modifier = Modifier,
+ cameraAppSettings: CameraAppSettings,
+ onClick: () -> Unit
+) {
+ SwitchSettingUI(
+ modifier = modifier,
+ title = stringResource(id = R.string.default_facing_camera_title),
+ description = null,
+ leadingIcon = null,
+ onClick = { onClick() },
+ settingValue = cameraAppSettings.isFrontCameraFacing,
+ enabled = cameraAppSettings.isBackCameraAvailable &&
+ cameraAppSettings.isFrontCameraAvailable
+ )
+}
+
+@Composable
+fun DarkModeSetting(
+ modifier: Modifier = Modifier,
+ currentDarkMode: DarkMode,
+ setDarkMode: (DarkMode) -> Unit
+) {
+ BasicPopupSetting(
+ modifier = modifier,
+ title = stringResource(id = R.string.dark_mode_title),
+ leadingIcon = null,
+ description = when (currentDarkMode) {
+ DarkMode.SYSTEM -> stringResource(id = R.string.dark_mode_description_system)
+ DarkMode.DARK -> stringResource(id = R.string.dark_mode_description_dark)
+ DarkMode.LIGHT -> stringResource(id = R.string.dark_mode_description_light)
+ },
+ popupContents = {
+ Column(Modifier.selectableGroup()) {
+ SingleChoiceSelector(
+ text = stringResource(id = R.string.dark_mode_selector_dark),
+ selected = currentDarkMode == DarkMode.DARK,
+ onClick = { setDarkMode(DarkMode.DARK) }
+ )
+ SingleChoiceSelector(
+ text = stringResource(id = R.string.dark_mode_selector_light),
+ selected = currentDarkMode == DarkMode.LIGHT,
+ onClick = { setDarkMode(DarkMode.LIGHT) }
+ )
+ SingleChoiceSelector(
+ text = stringResource(id = R.string.dark_mode_selector_system),
+ selected = currentDarkMode == DarkMode.SYSTEM,
+ onClick = { setDarkMode(DarkMode.SYSTEM) }
+ )
+ }
+ }
+ )
+}
+
+@Composable
+fun FlashModeSetting(
+ modifier: Modifier = Modifier,
+ currentFlashMode: FlashMode,
+ setFlashMode: (FlashMode) -> Unit
+) {
+ BasicPopupSetting(
+ modifier = modifier,
+ title = stringResource(id = R.string.flash_mode_title),
+ leadingIcon = null,
+ description = when (currentFlashMode) {
+ FlashMode.AUTO -> stringResource(id = R.string.flash_mode_description_auto)
+ FlashMode.ON -> stringResource(id = R.string.flash_mode_description_on)
+ FlashMode.OFF -> stringResource(id = R.string.flash_mode_description_off)
+ },
+ popupContents = {
+ Column(Modifier.selectableGroup()) {
+ SingleChoiceSelector(
+ text = stringResource(id = R.string.flash_mode_selector_auto),
+ selected = currentFlashMode == FlashMode.AUTO,
+ onClick = { setFlashMode(FlashMode.AUTO) }
+ )
+ SingleChoiceSelector(
+ text = stringResource(id = R.string.flash_mode_selector_on),
+ selected = currentFlashMode == FlashMode.ON,
+ onClick = { setFlashMode(FlashMode.ON) }
+ )
+ SingleChoiceSelector(
+ text = stringResource(id = R.string.flash_mode_selector_off),
+ selected = currentFlashMode == FlashMode.OFF,
+ onClick = { setFlashMode(FlashMode.OFF) }
+ )
+ }
+ }
+ )
+}
+
+@Composable
+fun AspectRatioSetting(currentAspectRatio: AspectRatio, setAspectRatio: (AspectRatio) -> Unit) {
+ BasicPopupSetting(
+ title = stringResource(id = R.string.aspect_ratio_title),
+ leadingIcon = null,
+ description = when (currentAspectRatio) {
+ AspectRatio.NINE_SIXTEEN -> stringResource(id = R.string.aspect_ratio_description_9_16)
+ AspectRatio.THREE_FOUR -> stringResource(id = R.string.aspect_ratio_description_3_4)
+ AspectRatio.ONE_ONE -> stringResource(id = R.string.aspect_ratio_description_1_1)
+ },
+ popupContents = {
+ Column(Modifier.selectableGroup()) {
+ SingleChoiceSelector(
+ text = stringResource(id = R.string.aspect_ratio_selector_9_16),
+ selected = currentAspectRatio == AspectRatio.NINE_SIXTEEN,
+ onClick = { setAspectRatio(AspectRatio.NINE_SIXTEEN) }
+ )
+ SingleChoiceSelector(
+ text = stringResource(id = R.string.aspect_ratio_selector_3_4),
+ selected = currentAspectRatio == AspectRatio.THREE_FOUR,
+ onClick = { setAspectRatio(AspectRatio.THREE_FOUR) }
+ )
+ SingleChoiceSelector(
+ text = stringResource(id = R.string.aspect_ratio_selector_1_1),
+ selected = currentAspectRatio == AspectRatio.ONE_ONE,
+ onClick = { setAspectRatio(AspectRatio.ONE_ONE) }
+ )
+ }
+ }
+ )
+}
+
+@Composable
+fun CaptureModeSetting(currentCaptureMode: CaptureMode, setCaptureMode: (CaptureMode) -> Unit) {
+ // todo: string resources
+ BasicPopupSetting(
+ title = stringResource(R.string.capture_mode_title),
+ leadingIcon = null,
+ description = when (currentCaptureMode) {
+ CaptureMode.MULTI_STREAM -> stringResource(
+ id = R.string.capture_mode_description_multi_stream
+ )
+ CaptureMode.SINGLE_STREAM -> stringResource(
+ id = R.string.capture_mode_description_single_stream
+ )
+ },
+ popupContents = {
+ Column(Modifier.selectableGroup()) {
+ SingleChoiceSelector(
+ text = stringResource(id = R.string.capture_mode_selector_multi_stream),
+ selected = currentCaptureMode == CaptureMode.MULTI_STREAM,
+ onClick = { setCaptureMode(CaptureMode.MULTI_STREAM) }
+ )
+ SingleChoiceSelector(
+ text = stringResource(id = R.string.capture_mode_description_single_stream),
+ selected = currentCaptureMode == CaptureMode.SINGLE_STREAM,
+ onClick = { setCaptureMode(CaptureMode.SINGLE_STREAM) }
+ )
+ }
+ }
+ )
+}
+
+/**
+ * Setting UI sub-Components
+ * small and whimsical :)
+ * don't use these directly, use them to build the ready-to-use setting components
+ */
+
+/** a composable for creating a simple popup setting **/
+
+@Composable
+fun BasicPopupSetting(
+ modifier: Modifier = Modifier,
+ title: String,
+ description: String?,
+ enabled: Boolean = true,
+ leadingIcon: @Composable (() -> Unit)?,
+ popupContents: @Composable () -> Unit
+) {
+ val popupStatus = remember { mutableStateOf(false) }
+ SettingUI(
+ modifier = modifier.clickable(enabled = enabled) { popupStatus.value = true },
+ title = title,
+ description = description,
+ leadingIcon = leadingIcon,
+ trailingContent = null
+ )
+ if (popupStatus.value) {
+ AlertDialog(
+ onDismissRequest = { popupStatus.value = false },
+ confirmButton = {
+ Text(
+ text = "Close",
+ modifier = Modifier.clickable { popupStatus.value = false }
+ )
+ },
+ title = { Text(text = title) },
+ text = popupContents
+ )
+ }
+}
+
+/**
+ * A composable for creating a setting with a Switch.
+ *
+ * <p> the value should correspond to the setting's UI state value. the switch will only change
+ * appearance if the UI state has been successfully updated
+ */
+@Composable
+fun SwitchSettingUI(
+ modifier: Modifier = Modifier,
+ title: String,
+ description: String?,
+ leadingIcon: @Composable (() -> Unit)?,
+ onClick: () -> Unit,
+ settingValue: Boolean,
+ enabled: Boolean
+) {
+ SettingUI(
+ modifier = modifier.toggleable(
+ enabled = enabled,
+ role = Role.Switch,
+ value = settingValue,
+ onValueChange = { _ -> onClick() }
+ ),
+ title = title,
+ description = description,
+ leadingIcon = leadingIcon,
+ trailingContent = {
+ Switch(
+ enabled = enabled,
+ checked = settingValue,
+ onCheckedChange = {
+ onClick()
+ }
+ )
+ }
+ )
+}
+
+/**
+ * A composable used as a template used to construct other settings components
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun SettingUI(
+ modifier: Modifier = Modifier,
+ title: String,
+ description: String? = null,
+ leadingIcon: @Composable (() -> Unit)?,
+ trailingContent: @Composable (() -> Unit)?
+) {
+ ListItem(
+ modifier = modifier,
+ headlineContent = { Text(title) },
+ supportingContent = when (description) {
+ null -> null
+ else -> {
+ { Text(description) }
+ }
+ },
+ leadingContent = leadingIcon,
+ trailingContent = trailingContent
+ )
+}
+
+/**
+ * A component for a single-choice selector for a multiple choice list
+ */
+@Composable
+fun SingleChoiceSelector(
+ modifier: Modifier = Modifier,
+ text: String,
+ selected: Boolean,
+ onClick: () -> Unit,
+ enabled: Boolean = true
+) {
+ Row(
+ modifier
+ .fillMaxWidth()
+ .selectable(
+ selected = selected,
+ role = Role.RadioButton,
+ onClick = onClick
+ ),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ RadioButton(
+ selected = selected,
+ onClick = onClick,
+ enabled = enabled
+ )
+ Spacer(Modifier.width(8.dp))
+ Text(text)
+ }
+}