Skip to content

Commit

Permalink
Merge pull request #14 from Infomaniak/on-config-changed
Browse files Browse the repository at this point in the history
Keep the keyboard opened on configuration changed
  • Loading branch information
LunarX authored Jun 19, 2024
2 parents 8f2a9ed + da7a566 commit f72a1c3
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 3 deletions.
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)
}
}

0 comments on commit f72a1c3

Please sign in to comment.