diff --git a/.idea/.gitignore b/.idea/.gitignore index 26d3352..8f00030 100644 --- a/.idea/.gitignore +++ b/.idea/.gitignore @@ -1,3 +1,5 @@ # Default ignored files /shelf/ /workspace.xml +# GitHub Copilot persisted chat sessions +/copilot/chatSessions diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 0da7e9f..4183055 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -4,10 +4,8 @@ diff --git a/app/build.gradle b/app/build.gradle index c1d7c6b..2b4bb35 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,13 +4,13 @@ plugins { } android { - compileSdk 33 + compileSdk 34 namespace 'com.canopas.campose.countypickerdemo' defaultConfig { applicationId "com.canopas.campose.countypickerdemo" minSdk 21 - targetSdk 33 + versionCode 1 versionName "1.0" @@ -52,17 +52,17 @@ android { dependencies { implementation project(":countrypicker") - implementation 'androidx.core:core-ktx:1.10.1' + implementation 'androidx.core:core-ktx:1.13.0' implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.9.0' + implementation 'com.google.android.material:material:1.11.0' implementation platform("androidx.compose:compose-bom:$compose_bom_version") implementation "androidx.compose.ui:ui" implementation "androidx.compose.material3:material3" implementation "androidx.compose.ui:ui-tooling-preview" - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1' - implementation 'androidx.activity:activity-compose:1.7.2' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0' + implementation 'androidx.activity:activity-compose:1.9.0' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' diff --git a/app/src/main/java/com/canopas/campose/jetcountypicker/MainActivity.kt b/app/src/main/java/com/canopas/campose/jetcountypicker/MainActivity.kt index efd4fcf..2a768e7 100644 --- a/app/src/main/java/com/canopas/campose/jetcountypicker/MainActivity.kt +++ b/app/src/main/java/com/canopas/campose/jetcountypicker/MainActivity.kt @@ -3,11 +3,18 @@ package com.canopas.campose.jetcountypicker import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +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.height import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -16,8 +23,8 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight @@ -25,10 +32,11 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.canopas.campose.countrypicker.CountryPickerBottomSheet +import com.canopas.campose.countrypicker.CountryPickerView import com.canopas.campose.countrypicker.CountryTextField import com.canopas.campose.countrypicker.countryList import com.canopas.campose.countrypicker.model.Country +import com.canopas.campose.countrypicker.model.PickerType import com.canopas.campose.countypickerdemo.R import com.canopas.campose.jetcountypicker.ui.theme.JetCountyPickerTheme @@ -38,7 +46,9 @@ class MainActivity : ComponentActivity() { setContent { JetCountyPickerTheme { Surface(color = MaterialTheme.colorScheme.background) { - SampleCountryPicker() + Column { + SampleCountryPickerDialog() + } } } } @@ -47,9 +57,12 @@ class MainActivity : ComponentActivity() { @OptIn(ExperimentalMaterial3Api::class) @Composable -fun SampleCountryPicker() { - var openBottomSheet by rememberSaveable { mutableStateOf(false) } +fun SampleCountryPickerDialog() { + var showCountryPicker by rememberSaveable { mutableStateOf(false) } var selectedCountry by remember { mutableStateOf(null) } + var pickerType by remember { + mutableStateOf(PickerType.DIALOG) + } Box { CountryTextField( @@ -57,17 +70,53 @@ fun SampleCountryPicker() { modifier = Modifier .fillMaxWidth() .padding(top = 50.dp, start = 40.dp, end = 40.dp), + textStyle = MaterialTheme.typography.bodyMedium, + labelTextStyle = MaterialTheme.typography.labelMedium, selectedCountry = selectedCountry, defaultCountry = countryList(LocalContext.current).firstOrNull { it.code == "IN" }, onShowCountryPicker = { - openBottomSheet = true - }, isPickerVisible = openBottomSheet + showCountryPicker = true + }, isPickerVisible = showCountryPicker ) } - if (openBottomSheet) { - CountryPickerBottomSheet( - bottomSheetTitle = { + Column(horizontalAlignment = Alignment.Start) { + Spacer(modifier = Modifier.height(28.dp)) + Text( + text = stringResource(R.string.picker_type), + modifier = Modifier.padding(16.dp), + style = MaterialTheme.typography.bodyMedium + ) + PickerType.entries.forEach { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable( + onClick = { + pickerType = it + } + ), + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.CenterVertically + ) { + RadioButton( + selected = pickerType == it, + onClick = { + pickerType = it + } + ) + Text( + text = stringResource(it.value), + modifier = Modifier.padding(start = 8.dp), + style = MaterialTheme.typography.bodyMedium + ) + } + } + } + + if (showCountryPicker) { + CountryPickerView( + pickerTitle = { Text( modifier = Modifier .fillMaxWidth() @@ -78,12 +127,16 @@ fun SampleCountryPicker() { fontSize = 20.sp ) }, - containerColor = Color.White, + searchFieldTextStyle = MaterialTheme.typography.bodyMedium, + placeholderTextStyle = MaterialTheme.typography.labelMedium, + countriesTextStyle = MaterialTheme.typography.bodyMedium, onItemSelected = { selectedCountry = it - openBottomSheet = false - }, onDismissRequest = { - openBottomSheet = false + showCountryPicker = false + }, + pickerType = pickerType, + onDismissRequest = { + showCountryPicker = false } ) } @@ -93,6 +146,6 @@ fun SampleCountryPicker() { @Composable fun DefaultPreview() { JetCountyPickerTheme { - SampleCountryPicker() + SampleCountryPickerDialog() } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f4b28ae..1ac6a34 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,4 +1,5 @@ JetCountyPicker Select country + Picker Type \ No newline at end of file diff --git a/build.gradle b/build.gradle index 89abfe8..70015dd 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,14 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext { - compose_compiler_version = "1.5.0" - compose_bom_version = "2023.06.01" + compose_compiler_version = "1.5.12" + compose_bom_version = "2024.04.01" } } plugins { - id 'com.android.application' version '8.1.0' apply false - id 'com.android.library' version '8.1.0' apply false - id 'org.jetbrains.kotlin.android' version '1.9.0' apply false + id 'com.android.application' version '8.3.2' apply false + id 'com.android.library' version '8.3.2' apply false + id 'org.jetbrains.kotlin.android' version '1.9.23' apply false id 'io.github.gradle-nexus.publish-plugin' version "1.3.0" } apply from: "${rootDir}/scripts/publish-root.gradle" diff --git a/countrypicker/build.gradle b/countrypicker/build.gradle index d2fcc64..d4a2db0 100644 --- a/countrypicker/build.gradle +++ b/countrypicker/build.gradle @@ -11,12 +11,11 @@ ext { apply from: "${rootDir}/scripts/publish-module.gradle" android { - compileSdk 33 + compileSdk 34 namespace 'com.canopas.campose.countrypicker' defaultConfig { minSdk 21 - targetSdk 33 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" @@ -50,9 +49,9 @@ android { dependencies { - implementation 'androidx.core:core-ktx:1.10.1' + implementation 'androidx.core:core-ktx:1.13.0' implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.9.0' + implementation 'com.google.android.material:material:1.11.0' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' diff --git a/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountryPickerBottomSheet.kt b/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountryPickerBottomSheet.kt index fa7300a..c585be7 100644 --- a/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountryPickerBottomSheet.kt +++ b/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountryPickerBottomSheet.kt @@ -9,6 +9,8 @@ import androidx.compose.foundation.lazy.items import androidx.compose.material3.BottomSheetDefaults import androidx.compose.material3.Divider import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.SheetState import androidx.compose.material3.Text @@ -25,22 +27,46 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import com.canopas.campose.countrypicker.model.Country import kotlinx.coroutines.launch +/** + * Composable for displaying a bottom sheet country picker. + * + * @param sheetState The state of the bottom sheet. + * @param shape The shape of the bottom sheet. + * @param containerColor The color of the bottom sheet container. + * @param contentColor The color of the bottom sheet content. + * @param tonalElevation The elevation of the bottom sheet. + * @param scrimColor The color of the bottom sheet scrim. + * @param bottomSheetTitle The title composable for the bottom sheet. + * @param onItemSelected Callback when a country is selected. + * @param searchFieldTextStyle The text style for the search field. + * @param placeholderTextStyle The text style for the search field placeholder. + * @param countriesTextStyle The text style for the countries list. + * @param onDismissRequest Callback when the bottom sheet is dismissed. + */ @OptIn(ExperimentalMaterial3Api::class) @Composable fun CountryPickerBottomSheet( sheetState: SheetState = rememberModalBottomSheetState(skipPartiallyExpanded = false), - shape: Shape = BottomSheetDefaults.ExpandedShape, + shape: Shape = MaterialTheme.shapes.medium, containerColor: Color = BottomSheetDefaults.ContainerColor, contentColor: Color = contentColorFor(containerColor), tonalElevation: Dp = BottomSheetDefaults.Elevation, scrimColor: Color = BottomSheetDefaults.ScrimColor, bottomSheetTitle: @Composable () -> Unit, onItemSelected: (country: Country) -> Unit, + searchFieldTextStyle: TextStyle = LocalTextStyle.current.copy(fontSize = 14.sp), + placeholderTextStyle: TextStyle = MaterialTheme.typography.labelMedium.copy( + color = Color.Gray, + fontSize = 16.sp, + ), + countriesTextStyle: TextStyle = TextStyle(), onDismissRequest: () -> Unit ) { var searchValue by rememberSaveable { mutableStateOf("") } @@ -57,11 +83,11 @@ fun CountryPickerBottomSheet( ) { bottomSheetTitle() - CountrySearchView(searchValue) { + CountrySearchView(searchValue, searchFieldTextStyle, placeholderTextStyle) { searchValue = it } - Countries(searchValue) { + Countries(searchValue, countriesTextStyle) { scope.launch { sheetState.hide() onItemSelected(it) @@ -70,9 +96,17 @@ fun CountryPickerBottomSheet( } } +/** + * Composable for displaying a list of countries. + * + * @param searchValue The search value for filtering countries. + * @param textStyle The text style for the country list. + * @param onItemSelected Callback when a country is selected. + */ @Composable -fun Countries( +internal fun Countries( searchValue: String, + textStyle: TextStyle = TextStyle(), onItemSelected: (country: Country) -> Unit ) { val context = LocalContext.current @@ -92,21 +126,25 @@ fun Countries( .clickable { onItemSelected(country) } .padding(12.dp) ) { - Text(text = localeToEmoji(country.code)) + Text( + text = localeToEmoji(country.code), + style = textStyle + ) Text( text = country.name, modifier = Modifier .padding(start = 8.dp) - .weight(2f) + .weight(2f), + style = textStyle ) Text( text = country.dial_code, modifier = Modifier - .padding(start = 8.dp) + .padding(start = 8.dp), + style = textStyle ) } Divider(color = Color.LightGray, thickness = 0.5.dp) } } - } diff --git a/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountryPickerDialog.kt b/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountryPickerDialog.kt new file mode 100644 index 0000000..bfe5e41 --- /dev/null +++ b/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountryPickerDialog.kt @@ -0,0 +1,102 @@ +package com.canopas.campose.countrypicker + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.shape.CornerSize +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties +import com.canopas.campose.countrypicker.model.Country +import kotlinx.coroutines.launch + + +/** + * Composable for displaying a country picker as a dialog. + * + * @param shape The shape of the dialog. + * @param backgroundColor The color of the dialog container. + * @param dialogTitle The title composable for the dialog. + * @param onItemSelected Callback when a country is selected. + * @param searchFieldTextStyle The text style for the search field. + * @param placeholderTextStyle The text style for the search field placeholder. + * @param countriesTextStyle The text style for the countries list. + * @param showFullScreenDialog Whether to show the dialog as a full screen dialog. + * @param onDismissRequest Callback when the dialog is dismissed. + */ +@Composable +fun CountryPickerDialog( + shape: Shape = MaterialTheme.shapes.small, + backgroundColor: Color = MaterialTheme.colorScheme.background, + dialogTitle: @Composable () -> Unit, + onItemSelected: (country: Country) -> Unit, + searchFieldTextStyle: TextStyle = LocalTextStyle.current.copy(fontSize = 14.sp), + placeholderTextStyle: TextStyle = MaterialTheme.typography.labelMedium.copy( + color = Color.Gray, + fontSize = 16.sp, + ), + countriesTextStyle: TextStyle = TextStyle(), + showFullScreenDialog: Boolean = false, + onDismissRequest: () -> Unit +) { + var searchValue by rememberSaveable { mutableStateOf("") } + val configuration = LocalConfiguration.current + val screenHeight = configuration.screenHeightDp.dp + val scope = rememberCoroutineScope() + val modifier = if (showFullScreenDialog) { + Modifier + .fillMaxSize() + .background(color = backgroundColor) + } else { + Modifier + .wrapContentHeight() + .heightIn(max = (screenHeight * 0.85f)) + .clip(shape = shape) + .background(color = backgroundColor, shape) + } + + Dialog( + onDismissRequest = onDismissRequest, + properties = DialogProperties( + usePlatformDefaultWidth = !showFullScreenDialog, + dismissOnBackPress = true, + dismissOnClickOutside = !showFullScreenDialog + ) + ) { + Column( + modifier = modifier + ) { + dialogTitle() + CountrySearchView(searchValue, searchFieldTextStyle, placeholderTextStyle) { + searchValue = it + } + + Countries(searchValue, countriesTextStyle) { + scope.launch { + onDismissRequest() + onItemSelected(it) + } + } + } + } +} \ No newline at end of file diff --git a/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountryPickerTextField.kt b/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountryPickerTextField.kt index d7a83e0..015d96f 100644 --- a/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountryPickerTextField.kt +++ b/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountryPickerTextField.kt @@ -22,9 +22,24 @@ import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.semantics.onClick import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.TextStyle import com.canopas.campose.countrypicker.model.Country -@OptIn(ExperimentalMaterial3Api::class) +/** + * Composable for displaying a text field with a country picker. + * + * @param label The label for the text field. + * @param isError Whether the text field should display an error state. + * @param modifier The modifier for the text field. + * @param shape The shape of the text field. + * @param selectedCountry The currently selected country. + * @param defaultCountry The default country to display if none is selected. + * @param colors The colors for the text field. + * @param textStyle The text style for the text field. + * @param labelTextStyle The text style for the label. + * @param isPickerVisible Whether the country picker bottom sheet is visible. + * @param onShowCountryPicker Callback when the country picker is shown. + */ @Composable fun CountryTextField( label: String = "", @@ -33,8 +48,9 @@ fun CountryTextField( shape: Shape = MaterialTheme.shapes.small, selectedCountry: Country? = null, defaultCountry: Country? = null, - colors: TextFieldColors = OutlinedTextFieldDefaults.colors( - ), + colors: TextFieldColors = OutlinedTextFieldDefaults.colors(), + textStyle: TextStyle = TextStyle(), + labelTextStyle: TextStyle = TextStyle(), isPickerVisible: Boolean = false, onShowCountryPicker: () -> Unit ) { @@ -44,7 +60,6 @@ fun CountryTextField( defaultCountry ?: countryList(context).first() } - val countryValue = "${defaultSelectedCountry.dial_code} ${defaultSelectedCountry.name}" OutlinedTextField( @@ -54,7 +69,8 @@ fun CountryTextField( }), readOnly = true, isError = isError, - label = { Text(label) }, + textStyle = textStyle, + label = { Text(label, style = labelTextStyle) }, value = if (selectedCountry == null) countryValue else "${selectedCountry.dial_code} ${selectedCountry.name}", onValueChange = {}, colors = colors, @@ -71,9 +87,14 @@ fun CountryTextField( ) } -fun Modifier.expandable( +/** + * Modifier for making a composable expandable when clicked. + * + * @param onExpandedChange Callback when the expandable state changes. + */ +internal fun Modifier.expandable( onExpandedChange: () -> Unit -) = pointerInput(Unit) { +) = this.pointerInput(Unit) { awaitEachGesture { var event: PointerEvent do { diff --git a/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountryPickerView.kt b/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountryPickerView.kt new file mode 100644 index 0000000..1ae724d --- /dev/null +++ b/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountryPickerView.kt @@ -0,0 +1,103 @@ +package com.canopas.campose.countrypicker + +import androidx.compose.material3.BottomSheetDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SheetState +import androidx.compose.material3.contentColorFor +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.sp +import com.canopas.campose.countrypicker.model.Country +import com.canopas.campose.countrypicker.model.PickerType + +/** + * Composable for displaying a country picker. + * + * @param sheetState The state of the bottom sheet. + * @param shape The shape of the bottom sheet or dialog. + * @param containerColor The color of the bottom sheet or dialog container. + * @param contentColor The color of the bottom sheet items. + * **[For bottom sheet only].** + * @param tonalElevation The elevation of the bottom sheet + * **[For bottom sheet only].** + * @param scrimColor Color of the scrim that obscures content when the bottom sheet is open. + * **[For bottom sheet only].** + * @param pickerTitle The title composable for the bottom sheet or dialog. + * @param onItemSelected Callback when a country is selected. + * @param searchFieldTextStyle The text style for the search field. + * @param placeholderTextStyle The text style for the search field placeholder. + * @param countriesTextStyle The text style for the countries list. + * @param pickerType The type of picker to display (bottom sheet, full-screen dialog, or dialog). + * Also see [PickerType]. + * @param onDismissRequest Callback when the bottom sheet or dialog is dismissed. + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun CountryPickerView( + sheetState: SheetState = rememberModalBottomSheetState(skipPartiallyExpanded = false), + shape: Shape = MaterialTheme.shapes.medium, + containerColor: Color = MaterialTheme.colorScheme.background, + contentColor: Color = contentColorFor(containerColor), + tonalElevation: Dp = BottomSheetDefaults.Elevation, + scrimColor: Color = BottomSheetDefaults.ScrimColor, + pickerTitle: @Composable () -> Unit, + onItemSelected: (country: Country) -> Unit, + searchFieldTextStyle: TextStyle = LocalTextStyle.current.copy(fontSize = 14.sp), + placeholderTextStyle: TextStyle = MaterialTheme.typography.labelMedium.copy( + color = Color.Gray, + fontSize = 16.sp, + ), + countriesTextStyle: TextStyle = TextStyle(), + pickerType: PickerType = PickerType.BOTTOM_SHEET, + onDismissRequest: () -> Unit +) { + when(pickerType) { + PickerType.BOTTOM_SHEET -> { + CountryPickerBottomSheet( + sheetState = sheetState, + shape = shape, + containerColor = containerColor, + contentColor = contentColor, + tonalElevation = tonalElevation, + scrimColor = scrimColor, + bottomSheetTitle = pickerTitle, + onItemSelected = onItemSelected, + searchFieldTextStyle = searchFieldTextStyle, + placeholderTextStyle = placeholderTextStyle, + countriesTextStyle = countriesTextStyle, + onDismissRequest = onDismissRequest + ) + } + PickerType.FULL_SCREEN_DIALOG -> { + CountryPickerDialog( + shape = shape, + backgroundColor = containerColor, + onItemSelected = onItemSelected, + dialogTitle = pickerTitle, + searchFieldTextStyle = searchFieldTextStyle, + placeholderTextStyle = placeholderTextStyle, + countriesTextStyle = countriesTextStyle, + showFullScreenDialog = true, + onDismissRequest = onDismissRequest + ) + } + PickerType.DIALOG -> { + CountryPickerDialog( + shape = shape, + backgroundColor = containerColor, + onItemSelected = onItemSelected, + dialogTitle = pickerTitle, + searchFieldTextStyle = searchFieldTextStyle, + placeholderTextStyle = placeholderTextStyle, + countriesTextStyle = countriesTextStyle, + onDismissRequest = onDismissRequest + ) + } + } +} \ No newline at end of file diff --git a/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountrySearchView.kt b/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountrySearchView.kt index 7e8ba57..cf8a625 100644 --- a/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountrySearchView.kt +++ b/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountrySearchView.kt @@ -12,11 +12,8 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.rounded.Cancel -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton -import androidx.compose.material3.LocalTextStyle -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.material3.TextFieldDefaults @@ -25,16 +22,27 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -@OptIn(ExperimentalMaterial3Api::class) +/** + * Composable for displaying a search field. + * + * @param searchValue The current search value. + * @param searchFieldTextStyle The text style for the search field. + * @param placeholderTextStyle The text style for the placeholder. + * @param onSearch Callback when the search value changes. + */ @Composable -fun CountrySearchView(searchValue: String, onSearch: (searchValue: String) -> Unit) { - +internal fun CountrySearchView( + searchValue: String, + searchFieldTextStyle: TextStyle = TextStyle(), + placeholderTextStyle: TextStyle = TextStyle(), + onSearch: (searchValue: String) -> Unit +) { val focusManager = LocalFocusManager.current Row { @@ -48,14 +56,10 @@ fun CountrySearchView(searchValue: String, onSearch: (searchValue: String) -> Un Color.LightGray.copy(0.6f), shape = RoundedCornerShape(10.dp) ), value = searchValue, onValueChange = { onSearch(it) - }, textStyle = LocalTextStyle.current.copy( - fontSize = 14.sp - ), placeholder = { + }, textStyle = searchFieldTextStyle, placeholder = { Text( text = stringResource(R.string.search_text), - style = MaterialTheme.typography.labelMedium, - color = Color.Gray, - fontSize = 16.sp, + style = placeholderTextStyle ) }, singleLine = true, leadingIcon = { @@ -96,5 +100,5 @@ fun CountrySearchView(searchValue: String, onSearch: (searchValue: String) -> Un @Preview @Composable fun PreviewSearchView() { - CountrySearchView("search", {}) + CountrySearchView("search", onSearch = {}) } \ No newline at end of file diff --git a/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountyUtils.kt b/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountyUtils.kt index 2d4e4ff..1672d79 100644 --- a/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountyUtils.kt +++ b/countrypicker/src/main/java/com/canopas/campose/countrypicker/CountyUtils.kt @@ -8,6 +8,12 @@ import com.squareup.moshi.Types import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory import java.io.IOException +/** + * Retrieves a list of countries from a JSON file stored in the assets directory. + * + * @param context The context used to access the assets directory. + * @return A mutable list of countries parsed from the JSON file, or an empty list if parsing fails. + */ fun countryList(context: Context): MutableList { val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() @@ -16,16 +22,16 @@ fun countryList(context: Context): MutableList { val jsonAdapter: JsonAdapter> = moshi.adapter(personListType) val jsonFileString = getJsonDataFromAsset(context, "Countries.json") - return jsonAdapter.fromJson(jsonFileString) ?: mutableListOf() + return jsonFileString?.let { jsonAdapter.fromJson(it) } ?: mutableListOf() } -fun localeToEmoji(countryCode: String): String { +internal fun localeToEmoji(countryCode: String): String { val firstLetter = Character.codePointAt(countryCode, 0) - 0x41 + 0x1F1E6 val secondLetter = Character.codePointAt(countryCode, 1) - 0x41 + 0x1F1E6 return String(Character.toChars(firstLetter)) + String(Character.toChars(secondLetter)) } -fun getJsonDataFromAsset(context: Context, fileName: String): String? { +internal fun getJsonDataFromAsset(context: Context, fileName: String): String? { val jsonString: String try { jsonString = context.assets.open(fileName).bufferedReader().use { it.readText() } @@ -36,7 +42,7 @@ fun getJsonDataFromAsset(context: Context, fileName: String): String? { return jsonString } -fun List.searchCountryList(countryName: String): MutableList { +internal fun List.searchCountryList(countryName: String): MutableList { val countryList = mutableListOf() this.forEach { if (it.name.lowercase().contains(countryName.lowercase()) || diff --git a/countrypicker/src/main/java/com/canopas/campose/countrypicker/model/PickerType.kt b/countrypicker/src/main/java/com/canopas/campose/countrypicker/model/PickerType.kt new file mode 100644 index 0000000..adf0a0a --- /dev/null +++ b/countrypicker/src/main/java/com/canopas/campose/countrypicker/model/PickerType.kt @@ -0,0 +1,12 @@ +package com.canopas.campose.countrypicker.model + +import com.canopas.campose.countrypicker.R + +/** + * Enum class for the type of picker to display. + */ +enum class PickerType(val value: Int) { + DIALOG(R.string.picker_type_dialog), + FULL_SCREEN_DIALOG(R.string.picker_type_full_screen), + BOTTOM_SHEET(R.string.picker_type_bottom_sheet) +} \ No newline at end of file diff --git a/countrypicker/src/main/res/values/strings.xml b/countrypicker/src/main/res/values/strings.xml index 011989d..ba7bdd0 100644 --- a/countrypicker/src/main/res/values/strings.xml +++ b/countrypicker/src/main/res/values/strings.xml @@ -1,4 +1,7 @@ Search + Dialog + Full Screen Dialog + Bottom Sheet \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0638b3f..07c5c61 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Fri Aug 11 12:20:35 IST 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists