cannot back from Activity after request focus and setonkeyEvent using Jetpack Compose - android-jetpack-compose

val requester = FocusRequester()
Box(
Modifier
.onKeyEvent {
if (it.isCtrlPressed && it.key == Key.A){
println("Ctrl + A is pressed")
true
} else {
false
}
}
.focusRequester(requester)
.focusable()
.size(10.dp)
)
LaunchedEffect(Unit) {
requester.requestFocus()
}
after launchedEffect and request focus, I need backpressed twice to leave page

Related

Jetpack Compose - How to manipulate the paste behaviour in a TextField

I want to be able to manipulate the paste behaviour of an TextField, something along the lines of -
override fun onPaste(pastedText: String){
}
Like how an EditText has
#Override
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo)item.getMenuInfo();
switch (item.getItemId()) {
case R.id.paste:
break;
}
return true;
}
I thought of a workaround. When the paste happens, your value usually typically changes by more than 1 symbol, so maybe something like this will work. I know it is hacky, and I would rather write this as a comment, but comment limits will not let me describe it completely.
TextField(
value = textValue,
onValueChange = { newValue ->
textValue = if (newValue.text.length > 1) {
doSomething()
newValue
} else {
newValue
}
}
)
UPD:
Oh I forgot that you can set up a modifier!
TextField(
value = textValue,
onValueChange = {...},
modifier = Modifier
.onKeyEvent { event: KeyEvent ->
if (
event.type == KeyEventType.KeyDown
&& event.key == Key.Paste
) {
// DO SOMETHING
return#onKeyEvent true
}
false
}
)

Manually move accessibility focus in Jetpack Compose

With Android View, I'm able to move the focus to a View like so:
fun View.requestAccessibilityFocus() {
requestFocus()
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
}
How do I achieve this in Jetpack Compose?
I tried using FocusRequester but it doesn't seem to do anything:
val lifecycleOwner = LocalLifecycleOwner.current
val requester = FocusRequester()
Box {
...
Image(
...
contentDescription = "My heading",
modifier = Modifier
...
.focusRequester(requester)
)
}
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
requester.requestFocus()
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
}
I had to add .focusable() and must do so after .focusRequester(focusRequester).
Box {
...
Image(
...
contentDescription = "My heading",
modifier = Modifier
...
.focusRequester(requester)
.focusable()
)
}
This happens because DiposableEffect might be calling during recomposition. You should not request focus during composition. The solution I have found is to request a focus right after the composition. Like this
LaunchedEffect(Unit) {
this.coroutineContext.job.invokeOnCompletion {
focusRequester.requestFocus()
}
}
This makes sure your code will run when the LauchedEffect leaves the composition because it(the coroutine) either got canceled or completed.

Jetpack compose manage soft keyboard events

so I have a OutlinedTextField and I want to have a undeletable prefix in it.
here is my OutlinedTextField:
OutlinedTextField(
modifier = Modifier
.focusRequester(focusRequester),
value = viewModel.phoneNumber.collectAsState().value,
onValueChange = { viewModel.onPhoneNumberChange(it) },
label = { Text("phone number") },
singleLine = true
)
and here is my viewModel:
private val _phoneNumber = MutableStateFlow("+")
val phoneNumber: StateFlow<String> = _phoneNumber
fun onPhoneNumberChange(
value: String
) {
viewModelScope.launch {
if (_phoneNumber.value.length == 1 && value == "") {
return#launch
} else
_phoneNumber.emit(value.take(phoneNumberMaxLength))
}
}
as you can see I managed to not emit new value when its delete event and it works fine.
but the problem is the coursor goes behind of the prefix when I press back space button
sothe question is how can i subscribe on soft keyboard with jetpack compose and manage back space button event?

How to make the first item in a lazy column clickable and make the rest unclickable

How to make the first item in a lazy column clickable and make the rest unclickable.
for example if I have a list of tasks to complete, enable first item only which is not completed
you have to complete tasks one at a time
LazyColumn(
modifier.padding(top = 40.dp),
) {
itemsIndexed(
items = todayRoute.sortedBy { it.sequence },
) { index, item ->
Row(
modifier = modifier
.fillMaxWidth()
.height(90.dp)
.padding(12.dp)
.clickable(
enabled = item.completed == "0" &&
item.arrived == "0" &&
item.missed == "0"
) {
})
{
}
}
}
Use Hashmap like this
As soon as you get all the item call replaceAll.
When the item is completed, call taskCompleted
class TaskViewModel {
private var _taskStatus = MutableStateFlow<HashMap<String, Boolean>>(hashMapOf())
val taskStatus = _taskStatus.asStateFlow()
fun taskCompleted(item: Task) {
_taskStatus.replace(item.id, true)
}
fun replaceAll(tasks: List<Task>) {
_taskStatus.value.clear()
tasks.forEach {
_taskStatus.value[it.id] = it.completed
}
}
private fun MutableStateFlow<HashMap<String, Boolean>>.replace(key: String, value: Boolean) {
val helper = HashMap<String, Boolean>(this.value)
helper[key] = value
this.value = helper
}
}
On UI, Adjust according to your need
val taskStatus by remember { viewModel.taskStatus }.collectAsState()
LazyColumn {
itemsIndexed(todayRoute) { item, index ->
Row(modifier = Modifier.clickable(
enabled = if (index > 0)
taskStatus[index - 1] ?: false
else
true
)
) {}
}
}

Hide/Show CircularProgressIndicator onclick Jetpack Compose

I want to show CircularProgressIndicator onclick and then hide !the CircularProgressIndicator when the function is executed see code below
var loader by remember {mutableStateOf(false)}
if(loader){
CircularProgressIndicator()
}
Button (onclick {
loader = !loader // show CircularProgressIndicator
// Function which is to be executed
// After function is executed which usually takes 2 sec then after that CircularProgressIndicator should be hidden
loader = loader // Not hidding
}){
Text("Toggle")
}
Basically you need to check that in any case at the end of your processing firebase = false should be called. I'd advise you at least to move the logic into a separate function, so I'll be harder to make a mistake, but basically it should look something like this:
OutlinedButton(onClick = {
keyboardController?.hide()
if (emailState.value.isEmpty() && secureState.value.isEmpty())
return#OutlinedButton
firebase = true
FirebaseAuth.getInstance()
.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener { task ->
val exception = task.exception?.localizedMessage.toString()
if (task.isSuccessful)
FirebaseAuth.getInstance().currentUser?.sendEmailVerification()
?.addOnCompleteListener { task ->
firebase = true
if (task.isSuccessful)
snackbarCoroutineScope.launch {
scaffoldState.snackbarHostState.showSnackbar(
"Check your Email for Verification")
}
}
else
snackbarCoroutineScope.launch {
scaffoldState.snackbarHostState.showSnackbar(exception)
firebase = true
}
}
}) {
Text(text = "Sign Up")
}
Spacer(modifier = Modifier.size(16.dp))
TextButton(onClick = {
keyboardController?.hide()
if (emailState.value.isEmpty())
return#TextButton
FirebaseAuth.getInstance().sendPasswordResetEmail(email)
.addOnCompleteListener { task ->
val exception = task.exception?.localizedMessage.toString()
firebase = false
if (task.isSuccessful)
snackbarCoroutineScope.launch {
scaffoldState.snackbarHostState.showSnackbar("Check your Email for Password reset")
firebase = true
}
else
snackbarCoroutineScope.launch {
scaffoldState.snackbarHostState.showSnackbar(exception)
firebase = true
}
}
}) {
Text(text = "Forgot password?")
}

Resources