From 625a9d35b2bc302b102a7b6eee6ea448da430364 Mon Sep 17 00:00:00 2001 From: Amanpal Singh <87360222+aman-alfresco@users.noreply.github.com> Date: Thu, 25 Jan 2024 14:28:17 +0530 Subject: [PATCH] added datee and time field --- .../com/alfresco/content/ZoneDateTimeExt.kt | 1 + component/build.gradle | 9 ++ .../content/component/DatePickerBuilder.kt | 102 ++++++++++++++---- .../content/data/payloads/FieldsData.kt | 2 + process-app/build.gradle | 2 + .../content/process/NavigationComponent.kt | 2 +- .../content/process/ProcessFormActivity.kt | 37 ++++++- .../{FormFragment.kt => FormDetailScreen.kt} | 23 ++-- .../content/process/ui/ProcessFormFragment.kt | 36 +++++++ .../process/ui/components/AmountInputField.kt | 3 + .../process/ui/components/DateTimeField.kt | 88 +++++++++++++++ .../process/ui/components/InputField.kt | 79 ++++++-------- .../ui/components/IntegerInputField.kt | 3 + .../ui/components/MultiLineInputField.kt | 3 + .../ui/components/SingleLineInputField.kt | 3 + .../ui/components/TrailingInputField.kt | 55 ++++++++++ .../content/process/ui/components/Utils.kt | 16 +++ .../content/process/ui/theme/Theme.kt | 29 +++-- process-app/src/main/res/values/ids.xml | 4 + process-app/src/main/res/values/strings.xml | 1 + 20 files changed, 395 insertions(+), 103 deletions(-) rename process-app/src/main/kotlin/com/alfresco/content/process/ui/{FormFragment.kt => FormDetailScreen.kt} (92%) create mode 100644 process-app/src/main/kotlin/com/alfresco/content/process/ui/ProcessFormFragment.kt create mode 100644 process-app/src/main/kotlin/com/alfresco/content/process/ui/components/DateTimeField.kt create mode 100644 process-app/src/main/kotlin/com/alfresco/content/process/ui/components/TrailingInputField.kt create mode 100644 process-app/src/main/kotlin/com/alfresco/content/process/ui/components/Utils.kt create mode 100644 process-app/src/main/res/values/ids.xml diff --git a/common/src/main/kotlin/com/alfresco/content/ZoneDateTimeExt.kt b/common/src/main/kotlin/com/alfresco/content/ZoneDateTimeExt.kt index 606bb92c4..8c9de673f 100644 --- a/common/src/main/kotlin/com/alfresco/content/ZoneDateTimeExt.kt +++ b/common/src/main/kotlin/com/alfresco/content/ZoneDateTimeExt.kt @@ -7,6 +7,7 @@ import java.util.TimeZone const val DATE_FORMAT_1 = "yyyy-MM-dd" const val DATE_FORMAT_2 = "dd-MMM-yyyy" +const val DATE_FORMAT_2_1 = "dd-MM-yyyy" const val DATE_FORMAT_3 = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" const val DATE_FORMAT_4 = "dd MMM yyyy" const val DATE_FORMAT_5 = "yyyy-MM-dd'T'HH:mm:ss'Z'" diff --git a/component/build.gradle b/component/build.gradle index feaa39089..fe94e69a8 100644 --- a/component/build.gradle +++ b/component/build.gradle @@ -13,6 +13,12 @@ android { buildFeatures { viewBinding true } + buildFeatures { // Enables Jetpack Compose for this module + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = "1.5.4" + } } kapt { @@ -40,6 +46,9 @@ dependencies { implementation libs.epoxy.core kapt libs.epoxy.processor + implementation libs.ui + implementation libs.activity.compose + // Testing testImplementation libs.junit androidTestImplementation libs.androidx.test.core diff --git a/component/src/main/java/com/alfresco/content/component/DatePickerBuilder.kt b/component/src/main/java/com/alfresco/content/component/DatePickerBuilder.kt index ba3692416..01f6642c7 100644 --- a/component/src/main/java/com/alfresco/content/component/DatePickerBuilder.kt +++ b/component/src/main/java/com/alfresco/content/component/DatePickerBuilder.kt @@ -3,11 +3,16 @@ package com.alfresco.content.component import android.content.Context import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment +import com.alfresco.content.DATE_FORMAT_2_1 +import com.alfresco.content.DATE_FORMAT_6 +import com.alfresco.content.data.payloads.FieldType +import com.alfresco.content.data.payloads.FieldsData import com.google.android.material.datepicker.CalendarConstraints import com.google.android.material.datepicker.CompositeDateValidator import com.google.android.material.datepicker.DateValidatorPointBackward import com.google.android.material.datepicker.DateValidatorPointForward import com.google.android.material.datepicker.MaterialDatePicker +import com.google.android.material.timepicker.MaterialTimePicker import java.text.SimpleDateFormat import java.util.Calendar import java.util.Date @@ -29,6 +34,7 @@ data class DatePickerBuilder( var isFutureDate: Boolean = false, var onSuccess: DatePickerOnSuccess? = null, var onFailure: DatePickerOnFailure? = null, + var fieldsData: FieldsData? = null, ) { private val dateFormatddMMMyy = "dd-MMM-yy" @@ -65,13 +71,17 @@ data class DatePickerBuilder( val constraintsBuilder = CalendarConstraints.Builder() - constraintsBuilder.setValidator(CompositeDateValidator.allOf(getValidators())) + constraintsBuilder.setValidator(CompositeDateValidator.allOf(getValidators(fieldsData))) val datePicker = MaterialDatePicker.Builder.datePicker().apply { - if (isFrom) { - setTitleText(context.getString(R.string.hint_range_from_date)) + if (fieldsData != null) { + setTitleText(fieldsData?.name) } else { - setTitleText(context.getString(R.string.hint_range_to_date)) + if (isFrom) { + setTitleText(context.getString(R.string.hint_range_from_date)) + } else { + setTitleText(context.getString(R.string.hint_range_to_date)) + } } setSelection(getSelectionDate()) setCalendarConstraints(constraintsBuilder.build()) @@ -79,11 +89,38 @@ data class DatePickerBuilder( datePicker.show(fragmentManager, DatePickerBuilder::class.java.simpleName) + val timePicker = MaterialTimePicker + .Builder() + .setTitleText(fieldsData?.name) + .build() + + var stringDateTime = "" datePicker.addOnPositiveButtonClickListener { val date = Date(it) - val stringDate = getFormatDate(date) - onSuccess?.invoke(stringDate) + if (fieldsData?.type == FieldType.DATETIME.value()) { + stringDateTime = getFormatDate(date) + timePicker.show(fragmentManager, DatePickerBuilder::class.java.name) + } else if (fieldsData?.type == FieldType.DATE.value()) { + onSuccess?.invoke(getFormatDate(date)) + } } + + timePicker.addOnPositiveButtonClickListener { + val hour = timePicker.hour + val minute = timePicker.minute + println("string date $stringDateTime || $hour || $minute") + val combinedDateTime = "$stringDateTime $hour:$minute" + onSuccess?.invoke(combinedDateTime) + } + + timePicker.addOnNegativeButtonClickListener { + onFailure?.invoke() + } + + timePicker.addOnCancelListener { + onFailure?.invoke() + } + datePicker.addOnCancelListener { onFailure?.invoke() } @@ -92,27 +129,42 @@ data class DatePickerBuilder( } } - private fun getValidators(): ArrayList { + private fun getValidators(fieldsData: FieldsData? = null): ArrayList { val validators: ArrayList = ArrayList() var endDate = MaterialDatePicker.todayInUtcMilliseconds() var requiredEndDate = false - if (isFrom) { - if (toDate.isNotEmpty()) { - toDate.getDateFromString()?.let { date -> - endDate = Date(date.time.plus(addOneDay)).time + + if (fieldsData != null) { + fieldsData.minValue?.apply { + this.getDateFromString(getFieldDateFormat(fieldsData))?.let { date -> + validators.add(DateValidatorPointForward.from(date.time)) + } + } + + fieldsData.maxValue?.apply { + this.getDateFromString(getFieldDateFormat(fieldsData))?.let { date -> + validators.add(DateValidatorPointBackward.before(date.time)) } - requiredEndDate = true } } else { - if (fromDate.isNotEmpty()) { - fromDate.getDateFromString()?.let { date -> - validators.add(DateValidatorPointForward.from(date.time)) + if (isFrom) { + if (toDate.isNotEmpty()) { + toDate.getDateFromString()?.let { date -> + endDate = Date(date.time.plus(addOneDay)).time + } + requiredEndDate = true + } + } else { + if (fromDate.isNotEmpty()) { + fromDate.getDateFromString()?.let { date -> + validators.add(DateValidatorPointForward.from(date.time)) + } + requiredEndDate = !isFutureDate } - requiredEndDate = !isFutureDate } - } - if (requiredEndDate) { - validators.add(DateValidatorPointBackward.before(endDate)) + if (requiredEndDate) { + validators.add(DateValidatorPointBackward.before(endDate)) + } } return validators @@ -140,8 +192,8 @@ data class DatePickerBuilder( return SimpleDateFormat(dateFormat, Locale.ENGLISH).format(currentTime) } - private fun String.getDateFromString(): Date? { - return SimpleDateFormat(dateFormat, Locale.ENGLISH).parse(this) + private fun String.getDateFromString(format: String = dateFormat): Date? { + return SimpleDateFormat(format, Locale.ENGLISH).parse(this) } private fun String.getddMMyyyyStringDate(): String? { @@ -162,4 +214,12 @@ data class DatePickerBuilder( calendar[Calendar.YEAR] = splitDate[2].toInt() return calendar.timeInMillis } + + private fun getFieldDateFormat(fieldsData: FieldsData? = null): String { + return when (fieldsData?.type) { + FieldType.DATETIME.value() -> DATE_FORMAT_6 + FieldType.DATE.value() -> DATE_FORMAT_2_1 + else -> dateFormat + } + } } diff --git a/data/src/main/kotlin/com/alfresco/content/data/payloads/FieldsData.kt b/data/src/main/kotlin/com/alfresco/content/data/payloads/FieldsData.kt index 95cb0b7dc..258fb11ee 100644 --- a/data/src/main/kotlin/com/alfresco/content/data/payloads/FieldsData.kt +++ b/data/src/main/kotlin/com/alfresco/content/data/payloads/FieldsData.kt @@ -73,6 +73,8 @@ enum class FieldType { INTEGER, AMOUNT, BOOLEAN, + DATETIME, + DATE, ; fun value() = name.lowercase() diff --git a/process-app/build.gradle b/process-app/build.gradle index b598a973d..5330cba46 100644 --- a/process-app/build.gradle +++ b/process-app/build.gradle @@ -48,6 +48,7 @@ dependencies { implementation project(':common') implementation project(':actions') implementation project(':data') + implementation project(':component') implementation libs.androidx.core implementation libs.androidx.appcompat @@ -55,6 +56,7 @@ dependencies { implementation libs.activity.compose implementation platform(libs.compose.bom) implementation libs.ui + implementation libs.activity.compose implementation libs.ui.graphics implementation libs.ui.tooling.preview implementation libs.material3 diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/NavigationComponent.kt b/process-app/src/main/kotlin/com/alfresco/content/process/NavigationComponent.kt index bd15e8f7e..a3e441146 100644 --- a/process-app/src/main/kotlin/com/alfresco/content/process/NavigationComponent.kt +++ b/process-app/src/main/kotlin/com/alfresco/content/process/NavigationComponent.kt @@ -17,7 +17,7 @@ fun NavigationComponent() { NavHost(navController = navController, startDestination = "first_screen") { composable("first_screen") { // Replace with the content of your first fragment - FormFragment(navController = navController) + FormFragment(navController) } // Add more composable entries for other fragments in your navigation graph } diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/ProcessFormActivity.kt b/process-app/src/main/kotlin/com/alfresco/content/process/ProcessFormActivity.kt index e04ce69c2..5e9af7307 100644 --- a/process-app/src/main/kotlin/com/alfresco/content/process/ProcessFormActivity.kt +++ b/process-app/src/main/kotlin/com/alfresco/content/process/ProcessFormActivity.kt @@ -1,28 +1,55 @@ package com.alfresco.content.process import android.os.Bundle -import androidx.activity.ComponentActivity +import android.view.ViewGroup import androidx.activity.compose.setContent +import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview +import androidx.fragment.app.FragmentContainerView +import com.alfresco.content.process.ui.ProcessFormFragment import com.alfresco.content.process.ui.theme.AlfrescoBaseTheme -class ProcessFormActivity : ComponentActivity() { +class ProcessFormActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { AlfrescoBaseTheme { - // A surface container using the 'background' color from the theme - Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) { - NavigationComponent() + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background, + ) { + composeApp() } } } } + + private fun composeApp() { + val fragmentManager = supportFragmentManager + val containerId = resources.getIdentifier("frame_container", "id", packageName) + + // Check if the fragment is already added + + // Create FragmentContainerView and add it to the activity + val fragmentContainer = FragmentContainerView(this).apply { + id = containerId + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, + ) + } + setContentView(fragmentContainer) + + fragmentManager + .beginTransaction() + .replace(fragmentContainer.id, ProcessFormFragment(), "firstFragment") + .commit() + } } @Preview(showBackground = true) diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/ui/FormFragment.kt b/process-app/src/main/kotlin/com/alfresco/content/process/ui/FormDetailScreen.kt similarity index 92% rename from process-app/src/main/kotlin/com/alfresco/content/process/ui/FormFragment.kt rename to process-app/src/main/kotlin/com/alfresco/content/process/ui/FormDetailScreen.kt index 2d7e92244..0ab3a2468 100644 --- a/process-app/src/main/kotlin/com/alfresco/content/process/ui/FormFragment.kt +++ b/process-app/src/main/kotlin/com/alfresco/content/process/ui/FormDetailScreen.kt @@ -10,10 +10,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface @@ -41,6 +37,7 @@ import com.alfresco.content.process.FormViewState import com.alfresco.content.process.ui.components.AmountInputField import com.alfresco.content.process.ui.components.CheckBoxField import com.alfresco.content.process.ui.components.CustomLinearProgressIndicator +import com.alfresco.content.process.ui.components.DateTimeField import com.alfresco.content.process.ui.components.IntegerInputField import com.alfresco.content.process.ui.components.MultiLineInputField import com.alfresco.content.process.ui.components.SingleLineInputField @@ -77,13 +74,6 @@ fun FormFragment(navController: NavController) { ) } -@Composable -fun BackButton(onClick: () -> Unit) { - IconButton(onClick = onClick) { - Icon(imageVector = Icons.Default.ArrowBack, contentDescription = "Back") - } -} - @OptIn(ExperimentalComposeUiApi::class) @Composable fun FormDetailScreen(padding: PaddingValues, state: FormViewState, viewModel: FormViewModel) { @@ -162,6 +152,17 @@ fun FormDetailScreen(padding: PaddingValues, state: FormViewState, viewModel: Fo field, ) } + + FieldType.DATETIME.value(), FieldType.DATE.value() -> { + var textFieldValue by remember { mutableStateOf(field.value as? String ?: "") } + DateTimeField( + dateTimeValue = textFieldValue, + onValueChanged = { newText -> + textFieldValue = newText + }, + field, + ) + } } } } diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/ui/ProcessFormFragment.kt b/process-app/src/main/kotlin/com/alfresco/content/process/ui/ProcessFormFragment.kt new file mode 100644 index 000000000..a048b15f7 --- /dev/null +++ b/process-app/src/main/kotlin/com/alfresco/content/process/ui/ProcessFormFragment.kt @@ -0,0 +1,36 @@ +package com.alfresco.content.process.ui + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.ComposeView +import androidx.fragment.app.Fragment +import com.alfresco.content.process.NavigationComponent +import com.alfresco.content.process.ui.theme.AlfrescoBaseTheme + +class ProcessFormFragment : Fragment() { + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + return ComposeView(requireContext()).apply { + layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) + setContent { + AlfrescoBaseTheme { + NavigationComponent() + } + } + } + } +} + +@Composable +fun BackButton(onClick: () -> Unit) { + IconButton(onClick = onClick) { + Icon(imageVector = Icons.Default.ArrowBack, contentDescription = "Back") + } +} diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/AmountInputField.kt b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/AmountInputField.kt index f2b0cc0fc..ea98fc227 100644 --- a/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/AmountInputField.kt +++ b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/AmountInputField.kt @@ -3,12 +3,14 @@ package com.alfresco.content.process.ui.components import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import com.alfresco.content.data.payloads.FieldsData import com.alfresco.content.process.R +import inputField @Composable fun AmountInputField( @@ -38,6 +40,7 @@ fun AmountInputField( val errorData = isValidInput(inputText = textFieldValue, fieldsData = fieldsData) InputFieldWithLeading( + modifier = Modifier.inputField(), maxLines = 1, textFieldValue = textFieldValue, onValueChanged = onValueChanged, diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/DateTimeField.kt b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/DateTimeField.kt new file mode 100644 index 000000000..099fb9e76 --- /dev/null +++ b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/DateTimeField.kt @@ -0,0 +1,88 @@ +package com.alfresco.content.process.ui.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.tooling.preview.Preview +import com.alfresco.content.DATE_FORMAT_4 +import com.alfresco.content.component.DatePickerBuilder +import com.alfresco.content.data.payloads.FieldsData +import com.alfresco.content.process.R +import inputField + +@Composable +fun DateTimeField( + dateTimeValue: String = "", + onValueChanged: (String) -> Unit = { }, + fieldsData: FieldsData = FieldsData(), +) { + val keyboardOptions = KeyboardOptions.Default.copy( + imeAction = ImeAction.Next, + keyboardType = KeyboardType.Text, + ) + + val isError = dateTimeValue.isNotEmpty() && dateTimeValue.length < fieldsData.minLength + + val errorMessage = if (isError) { + stringResource(R.string.error_min_length, fieldsData.minLength) + } else { + "" + } + + val textFieldColors = if (dateTimeValue.isEmpty()) { + OutlinedTextFieldDefaults.colors( + disabledBorderColor = MaterialTheme.colorScheme.primary, + disabledTextColor = MaterialTheme.colorScheme.onSurface, + disabledPlaceholderColor = MaterialTheme.colorScheme.primary, + ) + } else + OutlinedTextFieldDefaults.colors( + disabledBorderColor = MaterialTheme.colorScheme.primary, + disabledTextColor = MaterialTheme.colorScheme.onSurface, + disabledPlaceholderColor = MaterialTheme.colorScheme.primary, + disabledLabelColor = MaterialTheme.colorScheme.primary, + ) + + val context = LocalContext.current + InputField( + colors = textFieldColors, + modifier = Modifier + .inputField() + .clickable { + DatePickerBuilder( + context = context, + fromDate = "", + isFrom = true, + isFutureDate = true, + dateFormat = DATE_FORMAT_4, + fieldsData = fieldsData, + ) + .onSuccess { date -> + onValueChanged(date) + } + .onFailure {} + .show() + }, + maxLines = 1, + textFieldValue = dateTimeValue, + onValueChanged = onValueChanged, + fieldsData = fieldsData, + keyboardOptions = keyboardOptions, + isError = isError, + errorMessage = errorMessage, + isEnabled = false, + ) +} + +@Preview +@Composable +fun DateTimeFieldPreview() { + DateTimeField() +} diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/InputField.kt b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/InputField.kt index 1036c09c2..ee4f6ee6f 100644 --- a/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/InputField.kt +++ b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/InputField.kt @@ -1,6 +1,5 @@ package com.alfresco.content.process.ui.components -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardActions @@ -14,6 +13,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Text +import androidx.compose.material3.TextFieldColors import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf @@ -34,9 +34,12 @@ import androidx.compose.ui.unit.dp import com.alfresco.content.data.payloads.FieldsData import com.alfresco.content.process.R import com.alfresco.content.process.ui.theme.AlfrescoError +import trailingIconColor @Composable fun InputField( + modifier: Modifier = Modifier, + colors: TextFieldColors = OutlinedTextFieldDefaults.colors(), maxLines: Int = 1, textFieldValue: String? = null, onValueChanged: (String) -> Unit = { }, @@ -44,6 +47,7 @@ fun InputField( keyboardOptions: KeyboardOptions, isError: Boolean = false, errorMessage: String = "", + isEnabled: Boolean = true, ) { var selectionState by remember { mutableIntStateOf(0) } // State to keep track of focus state @@ -59,23 +63,14 @@ fun InputField( focusState = it.isFocused } - val modifiedModifier: Modifier = Modifier - .fillMaxWidth() - .padding(start = 16.dp, end = 16.dp, top = 12.dp) // Add padding or other modifiers as needed - .onFocusChanged(onFocusChanged) + modifier.onFocusChanged(onFocusChanged) val adjustedModifier = if (maxLines > 1) { - modifiedModifier.height(100.dp) + modifier.height(100.dp) } else { - modifiedModifier + modifier } - val customTextFieldColors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = MaterialTheme.colorScheme.primary, // Change focused border color - unfocusedBorderColor = MaterialTheme.colorScheme.onSurface, // Change unfocused border color - errorBorderColor = AlfrescoError, // Change error border color - ) - val labelWithAsterisk = buildAnnotatedString { append(fieldsData.name) if (fieldsData.required) { @@ -86,7 +81,8 @@ fun InputField( } OutlinedTextField( - colors = customTextFieldColors, + colors = colors, + enabled = isEnabled, value = textFieldValue ?: "", // Initial value of the text field onValueChange = { newValue -> val newText = if (fieldsData.maxLength > 0) { @@ -120,27 +116,14 @@ fun InputField( keyboardActions = keyboardActions, isError = isError, trailingIcon = { - if (focusState && !textFieldValue.isNullOrEmpty()) { - val iconSize = with(LocalDensity.current) { 24.dp.toPx() } - if (isError) { - Icon( - imageVector = Icons.Default.Error, - contentDescription = errorMessage, - tint = AlfrescoError, - ) - } else { - IconButton( - onClick = { - onValueChanged("") - }, - ) { - Icon( - imageVector = Icons.Default.Cancel, - contentDescription = stringResource(R.string.accessibility_clear_text), - ) - } - } - } + TrailingInputField( + focusState = focusState, + textValue = textFieldValue, + errorMessage = errorMessage, + isError = isError, + fieldsData = fieldsData, + onValueChanged = onValueChanged, + ) }, supportingText = { if (focusState) { @@ -155,8 +138,14 @@ fun InputField( ) } +@Composable +fun trailingIcon() { +} + @Composable fun InputFieldWithLeading( + modifier: Modifier = Modifier, + colors: TextFieldColors = OutlinedTextFieldDefaults.colors(), maxLines: Int = 1, textFieldValue: String? = null, onValueChanged: (String) -> Unit = { }, @@ -180,23 +169,14 @@ fun InputFieldWithLeading( focusState = it.isFocused } - val modifiedModifier: Modifier = Modifier - .fillMaxWidth() - .padding(start = 16.dp, end = 16.dp, top = 12.dp) // Add padding or other modifiers as needed - .onFocusChanged(onFocusChanged) + modifier.onFocusChanged(onFocusChanged) val adjustedModifier = if (maxLines > 1) { - modifiedModifier.height(100.dp) + modifier.height(100.dp) } else { - modifiedModifier + modifier } - val customTextFieldColors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = MaterialTheme.colorScheme.primary, // Change focused border color - unfocusedBorderColor = MaterialTheme.colorScheme.onSurface, // Change unfocused border color - errorBorderColor = AlfrescoError, // Change error border color - ) - val labelWithAsterisk = buildAnnotatedString { append(fieldsData.name) if (fieldsData.required) { @@ -207,7 +187,7 @@ fun InputFieldWithLeading( } OutlinedTextField( - colors = customTextFieldColors, + colors = colors, value = textFieldValue ?: "", // Initial value of the text field onValueChange = { newValue -> val newText = if (fieldsData.maxLength > 0) { @@ -248,7 +228,7 @@ fun InputFieldWithLeading( Icon( imageVector = Icons.Default.Error, contentDescription = errorMessage, - tint = AlfrescoError, + tint = MaterialTheme.colorScheme.error, ) } else { IconButton( @@ -259,6 +239,7 @@ fun InputFieldWithLeading( Icon( imageVector = Icons.Default.Cancel, contentDescription = stringResource(R.string.accessibility_clear_text), + tint = trailingIconColor(), ) } } diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/IntegerInputField.kt b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/IntegerInputField.kt index 3f214f33a..da8c00b3f 100644 --- a/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/IntegerInputField.kt +++ b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/IntegerInputField.kt @@ -2,12 +2,14 @@ package com.alfresco.content.process.ui.components import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import com.alfresco.content.data.payloads.FieldsData import com.alfresco.content.process.R +import inputField @Composable fun IntegerInputField( @@ -39,6 +41,7 @@ fun IntegerInputField( } InputField( + modifier = Modifier.inputField(), maxLines = 1, textFieldValue = textFieldValue, onValueChanged = onValueChanged, diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/MultiLineInputField.kt b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/MultiLineInputField.kt index 2c6924a36..aadcdfdea 100644 --- a/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/MultiLineInputField.kt +++ b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/MultiLineInputField.kt @@ -2,12 +2,14 @@ package com.alfresco.content.process.ui.components import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import com.alfresco.content.data.payloads.FieldsData import com.alfresco.content.process.R +import inputField @Composable fun MultiLineInputField( @@ -29,6 +31,7 @@ fun MultiLineInputField( } InputField( + modifier = Modifier.inputField(), maxLines = 4, textFieldValue = textFieldValue, onValueChanged = onValueChanged, diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/SingleLineInputField.kt b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/SingleLineInputField.kt index 387cb16dd..a61e39e76 100644 --- a/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/SingleLineInputField.kt +++ b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/SingleLineInputField.kt @@ -2,12 +2,14 @@ package com.alfresco.content.process.ui.components import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import com.alfresco.content.data.payloads.FieldsData import com.alfresco.content.process.R +import inputField @Composable fun SingleLineInputField( @@ -29,6 +31,7 @@ fun SingleLineInputField( } InputField( + modifier = Modifier.inputField(), maxLines = 1, textFieldValue = textFieldValue, onValueChanged = onValueChanged, diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/TrailingInputField.kt b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/TrailingInputField.kt new file mode 100644 index 000000000..f6c72ccf2 --- /dev/null +++ b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/TrailingInputField.kt @@ -0,0 +1,55 @@ +package com.alfresco.content.process.ui.components + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Cancel +import androidx.compose.material.icons.filled.DateRange +import androidx.compose.material.icons.filled.Error +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import com.alfresco.content.data.payloads.FieldType +import com.alfresco.content.data.payloads.FieldsData +import com.alfresco.content.process.R +import trailingIconColor + +@Composable +fun TrailingInputField( + focusState: Boolean = false, + textValue: String? = null, + errorMessage: String = "", + isError: Boolean = false, + fieldsData: FieldsData = FieldsData(), + onValueChanged: (String) -> Unit = { }, +) { + if (fieldsData.type == FieldType.DATETIME.value() || fieldsData.type == FieldType.DATE.value()) { + Icon( + imageVector = Icons.Default.DateRange, + contentDescription = stringResource(R.string.accessibility_date_icon), + tint = trailingIconColor(), + ) + } else { + if (focusState && !textValue.isNullOrEmpty()) { + if (isError) { + Icon( + imageVector = Icons.Default.Error, + contentDescription = errorMessage, + tint = MaterialTheme.colorScheme.error, + ) + } else { + IconButton( + onClick = { + onValueChanged("") + }, + ) { + Icon( + imageVector = Icons.Default.Cancel, + contentDescription = stringResource(R.string.accessibility_clear_text), + tint = trailingIconColor(), + ) + } + } + } + } +} diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/Utils.kt b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/Utils.kt new file mode 100644 index 000000000..3de37f9ec --- /dev/null +++ b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/Utils.kt @@ -0,0 +1,16 @@ +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.alfresco.content.process.ui.theme.AlfrescoGray90060 +import com.alfresco.content.process.ui.theme.White60 +import com.alfresco.content.process.ui.theme.isNightMode + +@Composable +fun trailingIconColor() = if (isNightMode()) White60 else AlfrescoGray90060 + +fun Modifier.inputField() = + this + .fillMaxWidth() + .padding(start = 16.dp, end = 16.dp, top = 12.dp) // Add padding or other modifiers as needed diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/ui/theme/Theme.kt b/process-app/src/main/kotlin/com/alfresco/content/process/ui/theme/Theme.kt index acd120118..e582a8f42 100644 --- a/process-app/src/main/kotlin/com/alfresco/content/process/ui/theme/Theme.kt +++ b/process-app/src/main/kotlin/com/alfresco/content/process/ui/theme/Theme.kt @@ -1,31 +1,32 @@ package com.alfresco.content.process.ui.theme import android.app.Activity -import android.os.Build import androidx.appcompat.app.AppCompatDelegate import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme -import androidx.compose.material3.dynamicDarkColorScheme -import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView import androidx.core.view.WindowCompat private val DarkColorScheme = darkColorScheme( - primary = Color.White, + primary = AlfrescoBlue700, + onSurface = Color.White, + onSurfaceVariant = Color.White, + onBackground = Color.White, + background = designDefaultDarkBackgroundColor, + error = AlfrescoError, ) private val LightColorScheme = lightColorScheme( - primary = AlfrescoBlue700, // Replace with your alfresco_blue_700 color - surface = Color.White, - onSurface = Color(0xFF212121), // Replace with your alfresco_gray_900 color - background = Color.White, - onBackground = Color.Black, + primary = AlfrescoBlue700, + onSurface = AlfrescoGray900, + onSurfaceVariant = AlfrescoGray900, + outline = AlfrescoGray90015, + error = AlfrescoError, ) @Composable @@ -44,14 +45,10 @@ fun AlfrescoBaseTheme( } val colorScheme = when { - dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { - val context = LocalContext.current - if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) - } - darkTheme -> DarkColorScheme.copy( secondary = MaterialTheme.colorScheme.primary, // ) + else -> LightColorScheme.copy( secondary = MaterialTheme.colorScheme.primary, // ) @@ -75,7 +72,7 @@ fun AlfrescoBaseTheme( } @Composable -private fun isNightMode() = when (AppCompatDelegate.getDefaultNightMode()) { +fun isNightMode() = when (AppCompatDelegate.getDefaultNightMode()) { AppCompatDelegate.MODE_NIGHT_NO -> false AppCompatDelegate.MODE_NIGHT_YES -> true else -> isSystemInDarkTheme() diff --git a/process-app/src/main/res/values/ids.xml b/process-app/src/main/res/values/ids.xml new file mode 100644 index 000000000..3b3cc5ba9 --- /dev/null +++ b/process-app/src/main/res/values/ids.xml @@ -0,0 +1,4 @@ + + + + diff --git a/process-app/src/main/res/values/strings.xml b/process-app/src/main/res/values/strings.xml index 890e571d3..220a82d6f 100644 --- a/process-app/src/main/res/values/strings.xml +++ b/process-app/src/main/res/values/strings.xml @@ -1,6 +1,7 @@ Enter at least %1$d characters Clear Text + Date Icon Can\'t be less than %1$d Can\'t be greater than %1$d Use a different number format