Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keep the keyboard opened on configuration changed #14

Merged
merged 2 commits into from
Jun 19, 2024
Merged
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
@@ -1,6 +1,9 @@
package com.infomaniak.lib.richhtmleditor

import android.content.Context
import android.os.Build
import android.os.Bundle
import android.view.AbsSavedState
import android.webkit.WebView
import java.io.BufferedReader

Expand Down Expand Up @@ -33,3 +36,11 @@ internal fun WebView.injectCss(css: String) {

evaluateJavascript(addCssJs, null)
}

fun Bundle.getParcelableCompat(key: String, clazz: Class<AbsSavedState>): AbsSavedState? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getParcelable(key, clazz)
} else {
getParcelable(key)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ package com.infomaniak.lib.richhtmleditor
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Rect
import android.os.Bundle
import android.os.Parcelable
import android.util.AttributeSet
import android.view.AbsSavedState
import android.view.ViewGroup
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.core.os.bundleOf
import androidx.core.view.updateLayoutParams
import com.infomaniak.lib.richhtmleditor.executor.JsExecutableMethod
import com.infomaniak.lib.richhtmleditor.executor.JsExecutor
Expand Down Expand Up @@ -38,6 +42,8 @@ class RichHtmlEditorWebView @JvmOverloads constructor(
defStyleAttr: Int = 0,
) : WebView(context, attrs, defStyleAttr) {

private var keepKeyboardOpenedOnConfigurationChanged: Boolean = false

private val documentInitializer = DocumentInitializer()
private val jsExecutor = JsExecutor(this)
private val scriptCssInjector = ScriptCssInjector(this)
Expand Down Expand Up @@ -135,6 +141,7 @@ class RichHtmlEditorWebView @JvmOverloads constructor(
}

fun requestFocusAndOpenKeyboard() {
keepKeyboardOpenedOnConfigurationChanged = true
keyboardOpener.executeWhenDomIsLoaded(Unit)
}

Expand All @@ -143,13 +150,32 @@ class RichHtmlEditorWebView @JvmOverloads constructor(
jsExecutor.executeWhenDomIsLoaded(JsExecutableMethod("exportHtml"))
}

override fun onSaveInstanceState(): Parcelable {
val superState = super.onSaveInstanceState()
return bundleOf(
KEYBOARD_SHOULD_REOPEN_KEY to keepKeyboardOpenedOnConfigurationChanged,
SUPER_STATE_KEY to superState,
)
}

override fun onRestoreInstanceState(state: Parcelable?) {
(state as Bundle?)?.getBoolean(KEYBOARD_SHOULD_REOPEN_KEY)?.let { keepKeyboardOpenedOnConfigurationChanged = it }
super.onRestoreInstanceState(state?.getParcelableCompat(SUPER_STATE_KEY, AbsSavedState::class.java))
}

override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
super.onFocusChanged(focused, direction, previouslyFocusedRect)
if (focused) jsExecutor.executeWhenDomIsLoaded(JsExecutableMethod("requestFocus"))
if (focused) {
jsExecutor.executeWhenDomIsLoaded(JsExecutableMethod("requestFocus"))
if (keepKeyboardOpenedOnConfigurationChanged) keyboardOpener.executeWhenDomIsLoaded(Unit)
} else {
keepKeyboardOpenedOnConfigurationChanged = false
}
}

override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
keyboardOpener.removePendingListener()
jsBridgeJob.cancel()
}

Expand Down Expand Up @@ -206,4 +232,9 @@ class RichHtmlEditorWebView @JvmOverloads constructor(
private fun unsupported() {
throw UnsupportedOperationException("Use setHtml() instead")
}

companion object {
private const val KEYBOARD_SHOULD_REOPEN_KEY = "keyboardShouldReopen"
private const val SUPER_STATE_KEY = "superState"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,42 @@ package com.infomaniak.lib.richhtmleditor.executor

import android.app.Activity
import android.view.View
import android.view.ViewTreeObserver
import android.view.inputmethod.InputMethodManager

internal class KeyboardOpener(private val view: View) : JsLifecycleAwareExecutor<Unit>() {

private var listener: ViewTreeObserver.OnWindowFocusChangeListener? = null

override fun executeImmediately(value: Unit) {
if (view.requestFocus()) {
val inputMethodManager = view.context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
if (view.hasWindowFocus()) {
openKeyboard()
} else {
// The window won't have the focus most of the time when the configuration changes and we want to reopen the
// keyboard right away. When this happen, we need to wait for the window to get the focus before opening the
// keyboard.
listener = object : ViewTreeObserver.OnWindowFocusChangeListener {
override fun onWindowFocusChanged(hasFocus: Boolean) {
if (hasFocus) {
openKeyboard()
view.viewTreeObserver.removeOnWindowFocusChangeListener(this)
}
}
}

view.viewTreeObserver.addOnWindowFocusChangeListener(listener)
}
}
}

fun removePendingListener() {
view.viewTreeObserver.removeOnWindowFocusChangeListener(listener)
listener = null
}

private fun openKeyboard() {
val inputMethodManager = view.context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
}
}
Loading