Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ sealed class Destination(val route: String) {
object SignatureView : Destination("signatureViewScreen")
object MarqueeText : Destination("marqueeText")
object Autofill : Destination("autofill")
object AutoComplete : Destination("autoComplete")
object DogFeed : Destination("dogFeed")
object DogDetails : Destination("dogDetails/{dogId}") {
fun createRoute(dogId: String) = "dogDetails/$dogId"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.skyyo.samples.application.Destination
import com.skyyo.samples.application.ProfileGraph
import com.skyyo.samples.extensions.rememberBackStackEntry
import com.skyyo.samples.features.appBarElevation.AppBarElevation
import com.skyyo.samples.features.autoComplete.AutoCompleteScreen
import com.skyyo.samples.features.autofill.AutofillScreen
import com.skyyo.samples.features.autoscroll.AutoScrollScreen
import com.skyyo.samples.features.bottomSheets.BottomSheetScaffoldScreen
Expand Down Expand Up @@ -166,6 +167,7 @@ fun PopulatedNavHost(
composable(Destination.SignatureView.route) { SignatureViewScreen() }
composable(Destination.MarqueeText.route) { MarqueeTextScreen() }
composable(Destination.Autofill.route) { AutofillScreen() }
composable(Destination.AutoComplete.route) { AutoCompleteScreen() }
navigation(
route = ProfileGraph.route,
startDestination = ProfileGraph.Profile.route
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.skyyo.samples.features.autoComplete

import android.widget.ArrayAdapter
import android.widget.AutoCompleteTextView
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import com.google.android.material.textfield.TextInputLayout
import com.skyyo.samples.R

@Composable
fun AndroidViewAutocompleteDropdownWithOutsideFiltering(
modifier: Modifier = Modifier,
suggestions: List<String>,
selectedValue: String = "",
onSelect: (Int) -> Unit = {},
) {
val context = LocalContext.current
val adapter = remember(suggestions) {
ArrayAdapter(context, android.R.layout.simple_list_item_1, suggestions)
}
val textInputLayout = remember {
(
TextInputLayout.inflate(
context,
R.layout.text_input_field,
null
) as TextInputLayout
).also { til ->
(til.editText as AutoCompleteTextView).setOnItemClickListener { _, _, index, _ ->
onSelect(index)
}
}
}
val autoCompleteTextView = remember { textInputLayout.editText as AutoCompleteTextView }

AndroidView(
modifier = modifier,
factory = { textInputLayout },
update = {
autoCompleteTextView.setAdapter(adapter)
autoCompleteTextView.setText(selectedValue, false)
},
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.skyyo.samples.features.autoComplete

import androidx.compose.foundation.layout.*
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel

@Composable
fun AutoCompleteScreen(
viewModel: AutoCompleteViewModel = hiltViewModel()
) {
val query = viewModel.query.collectAsState()
val isExpanded = viewModel.isExpanded.collectAsState()
val suggestions = viewModel.suggestions.collectAsState()

Column(
Modifier
.fillMaxSize()
.padding(top = 50.dp, start = 16.dp, end = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.height(16.dp))
Text(text = "AndroidView")
AndroidViewAutocompleteDropdownWithOutsideFiltering(
modifier = Modifier.fillMaxWidth(),
suggestions = viewModel.countries,
selectedValue = "",
)
Spacer(modifier = Modifier.height(16.dp))
Text(text = "Native exposed dropdown menu")
AutocompleteDropdownWithFilteringInside(
modifier = Modifier.fillMaxWidth(),
countries = viewModel.countries,
)
Spacer(modifier = Modifier.height(60.dp))
Text(text = "Custom exposed dropdown menu")
AutocompleteDropdownWithOutsideFiltering(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 32.dp),
onSuggestionSelected = viewModel::onCountrySelected,
onExpandedChange = viewModel::onExpandedChange,
onValueChange = viewModel::onCountryEntered,
onClick = viewModel::onExpandedFieldClick,
suggestions = suggestions.value,
expanded = isExpanded.value,
query = query.value,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.skyyo.samples.features.autoComplete

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.util.*
import javax.inject.Inject

private const val QUERY = "query"
private const val SUGGESTIONS = "suggestions"
private const val IS_EXPANDED = "isExpanded"

@HiltViewModel
class AutoCompleteViewModel @Inject constructor(
private val handle: SavedStateHandle,
) : ViewModel() {

val countries = provideCountries()
val query = handle.getStateFlow(QUERY, "")
val suggestions = handle.getStateFlow(SUGGESTIONS, countries)
val isExpanded = handle.getStateFlow(IS_EXPANDED, false)

fun onCountryEntered(input: String) {
handle[QUERY] = input
viewModelScope.launch(Dispatchers.Default) {
handle[SUGGESTIONS] = when {
input.isEmpty() -> countries
else -> {
countries.filter { country ->
country
.lowercase()
.startsWith(input.lowercase()) && country != input
}
}
}
onExpandedChange(true)
}
}

fun onExpandedChange(value: Boolean) {
handle[IS_EXPANDED] = value
}

fun onCountrySelected(value: String) {
handle[QUERY] = value
}

fun onExpandedFieldClick() {
onExpandedChange(!isExpanded.value)
}

private fun provideCountries(): List<String> {
val locales = Locale.getAvailableLocales()
val countries = ArrayList<String>()
for (locale in locales) {
val country: String = locale.displayCountry
if (country.trim { it <= ' ' }.isNotEmpty() && !countries.contains(country)) {
countries.add(country)
}
}
countries.sort()

return countries
}
}
Loading