Browse Source

[Rich text editor] Ensure keyboard opens for reply and text formatting modes (#1337)

pull/1354/head
jonnyandrew 1 year ago committed by GitHub
parent
commit
519464fe5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      changelog.d/1337.bugfix
  2. 24
      libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/ui/View.kt
  3. 55
      libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/SoftKeyboardEffect.kt
  4. 17
      libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt

1
changelog.d/1337.bugfix

@ -0,0 +1 @@
[Rich text editor] Ensure keyboard opens for reply and text formatting modes

24
libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/ui/View.kt

@ -17,8 +17,11 @@
package io.element.android.libraries.androidutils.ui package io.element.android.libraries.androidutils.ui
import android.view.View import android.view.View
import android.view.ViewTreeObserver
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
fun View.hideKeyboard() { fun View.hideKeyboard() {
val imm = context?.getSystemService<InputMethodManager>() val imm = context?.getSystemService<InputMethodManager>()
@ -41,3 +44,24 @@ fun View.setHorizontalPadding(padding: Int) {
paddingBottom paddingBottom
) )
} }
suspend fun View.awaitWindowFocus() = suspendCancellableCoroutine { continuation ->
if (hasWindowFocus()) {
continuation.resume(Unit)
} else {
val listener = object : ViewTreeObserver.OnWindowFocusChangeListener {
override fun onWindowFocusChanged(hasFocus: Boolean) {
if (hasFocus) {
viewTreeObserver.removeOnWindowFocusChangeListener(this)
continuation.resume(Unit)
}
}
}
viewTreeObserver.addOnWindowFocusChangeListener(listener)
continuation.invokeOnCancellation {
viewTreeObserver.removeOnWindowFocusChangeListener(listener)
}
}
}

55
libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/SoftKeyboardEffect.kt

@ -0,0 +1,55 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* 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 io.element.android.libraries.textcomposer
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.viewinterop.AndroidView
import io.element.android.libraries.androidutils.ui.awaitWindowFocus
import io.element.android.libraries.androidutils.ui.showKeyboard
/**
* Shows the soft keyboard when a given key changes to meet the required condition.
*
* Uses [showKeyboard] to show the keyboard for compatibility with [AndroidView].
*
* @param T
* @param key The key to watch for changes.
* @param onRequestFocus A callback to request focus to the view that will receive the keyboard input.
* @param predicate The predicate that [key] must meet before showing the keyboard.
*/
@Composable
internal fun <T> SoftKeyboardEffect(
key: T,
onRequestFocus: () -> Unit,
predicate: (T) -> Boolean,
) {
val view = LocalView.current
LaunchedEffect(key) {
if (predicate(key)) {
// Await window focus in case returning from a dialog
view.awaitWindowFocus()
// Show the keyboard, temporarily using the root view for focus
view.showKeyboard(andRequestFocus = true)
// Refocus to the correct view
onRequestFocus()
}
}
}

17
libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt

@ -43,7 +43,6 @@ import androidx.compose.material.icons.filled.Close
import androidx.compose.material.ripple.rememberRipple import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -52,7 +51,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
@ -84,7 +82,6 @@ import io.element.android.wysiwyg.compose.RichTextEditor
import io.element.android.wysiwyg.compose.RichTextEditorDefaults import io.element.android.wysiwyg.compose.RichTextEditorDefaults
import io.element.android.wysiwyg.compose.RichTextEditorState import io.element.android.wysiwyg.compose.RichTextEditorState
import io.element.android.wysiwyg.view.models.InlineFormat import io.element.android.wysiwyg.view.models.InlineFormat
import kotlinx.coroutines.android.awaitFrame
import uniffi.wysiwyg_composer.ActionState import uniffi.wysiwyg_composer.ActionState
import uniffi.wysiwyg_composer.ComposerAction import uniffi.wysiwyg_composer.ComposerAction
@ -223,17 +220,11 @@ fun TextComposer(
} }
} }
// Request focus when changing mode, and show keyboard. SoftKeyboardEffect(composerMode, onRequestFocus) {
val keyboard = LocalSoftwareKeyboardController.current it is MessageComposerMode.Special
LaunchedEffect(composerMode) {
if (composerMode is MessageComposerMode.Special) {
onRequestFocus()
keyboard?.let {
awaitFrame()
it.show()
}
}
} }
SoftKeyboardEffect(showTextFormatting, onRequestFocus) { it }
} }
@Composable @Composable

Loading…
Cancel
Save