diff --git a/rich-html-editor/src/main/java/com/infomaniak/lib/richhtmleditor/Extensions.kt b/rich-html-editor/src/main/java/com/infomaniak/lib/richhtmleditor/Extensions.kt
index b5ec284..1b8c23b 100644
--- a/rich-html-editor/src/main/java/com/infomaniak/lib/richhtmleditor/Extensions.kt
+++ b/rich-html-editor/src/main/java/com/infomaniak/lib/richhtmleditor/Extensions.kt
@@ -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
@@ -33,3 +36,11 @@ internal fun WebView.injectCss(css: String) {
evaluateJavascript(addCssJs, null)
}
+
+fun Bundle.getParcelableCompat(key: String, clazz: Class): AbsSavedState? {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ getParcelable(key, clazz)
+ } else {
+ getParcelable(key)
+ }
+}
diff --git a/rich-html-editor/src/main/java/com/infomaniak/lib/richhtmleditor/RichHtmlEditorWebView.kt b/rich-html-editor/src/main/java/com/infomaniak/lib/richhtmleditor/RichHtmlEditorWebView.kt
index 847a222..6a73d5d 100644
--- a/rich-html-editor/src/main/java/com/infomaniak/lib/richhtmleditor/RichHtmlEditorWebView.kt
+++ b/rich-html-editor/src/main/java/com/infomaniak/lib/richhtmleditor/RichHtmlEditorWebView.kt
@@ -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
@@ -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)
@@ -135,6 +141,7 @@ class RichHtmlEditorWebView @JvmOverloads constructor(
}
fun requestFocusAndOpenKeyboard() {
+ keepKeyboardOpenedOnConfigurationChanged = true
keyboardOpener.executeWhenDomIsLoaded(Unit)
}
@@ -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()
}
@@ -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"
+ }
}
diff --git a/rich-html-editor/src/main/java/com/infomaniak/lib/richhtmleditor/executor/KeyboardOpener.kt b/rich-html-editor/src/main/java/com/infomaniak/lib/richhtmleditor/executor/KeyboardOpener.kt
index 7402f79..774ffa7 100644
--- a/rich-html-editor/src/main/java/com/infomaniak/lib/richhtmleditor/executor/KeyboardOpener.kt
+++ b/rich-html-editor/src/main/java/com/infomaniak/lib/richhtmleditor/executor/KeyboardOpener.kt
@@ -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() {
+
+ 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)
+ }
}