Refactors androidx.compose.ui:ui-text to the androidx.compose.ui.text package name
Bug: b/160233169
Test: ./gradlew checkApi
Relnote: N/A
Change-Id: Ide9b7d12c9f46dac717a602592bd168312253ce6
diff --git a/ui/ui-core/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.kt b/ui/ui-core/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.kt
new file mode 100644
index 0000000..172194a
--- /dev/null
+++ b/ui/ui-core/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.kt
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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 androidx.compose.ui.text.input
+
+import android.content.Context
+import android.text.InputType
+import android.view.View
+import android.view.ViewTreeObserver
+import android.view.inputmethod.EditorInfo
+import android.view.inputmethod.InputConnection
+import android.view.inputmethod.InputMethodManager
+import androidx.ui.geometry.Rect
+import androidx.compose.ui.text.TextRange
+import kotlin.math.roundToInt
+
+/**
+ * Provide Android specific input service with the Operating System.
+ */
+internal class TextInputServiceAndroid(val view: View) : PlatformTextInputService {
+ /** True if the currently editable composable has connected */
+ private var editorHasFocus = false
+
+ /**
+ * The following three observers are set when the editable composable has initiated the input
+ * session
+ */
+ private var onEditCommand: (List<EditOperation>) -> Unit = {}
+ private var onImeActionPerformed: (ImeAction) -> Unit = {}
+
+ private var state = TextFieldValue(text = "", selection = TextRange.Zero)
+ private var keyboardType = KeyboardType.Text
+ private var imeAction = ImeAction.Unspecified
+ private var ic: RecordingInputConnection? = null
+
+ private var focusedRect: android.graphics.Rect? = null
+
+ /**
+ * The editable buffer used for BaseInputConnection.
+ */
+ private lateinit var imm: InputMethodManager
+
+ private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
+ // focusedRect is null if there is not ongoing text input session. So safe to request
+ // latest focused rectangle whenever global layout has changed.
+ focusedRect?.let { view.requestRectangleOnScreen(it) }
+ }
+
+ init {
+ view.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
+ override fun onViewDetachedFromWindow(v: View?) {
+ v?.rootView?.viewTreeObserver?.removeOnGlobalLayoutListener(layoutListener)
+ }
+
+ override fun onViewAttachedToWindow(v: View?) {
+ v?.rootView?.viewTreeObserver?.addOnGlobalLayoutListener(layoutListener)
+ }
+ })
+ }
+
+ /**
+ * Creates new input connection.
+ */
+ fun createInputConnection(outAttrs: EditorInfo): InputConnection? {
+ if (!editorHasFocus) {
+ return null
+ }
+ fillEditorInfo(keyboardType, imeAction, outAttrs)
+
+ return RecordingInputConnection(
+ initState = state,
+ eventListener = object : InputEventListener {
+ override fun onEditOperations(editOps: List<EditOperation>) {
+ onEditCommand(editOps)
+ }
+
+ override fun onImeAction(imeAction: ImeAction) {
+ onImeActionPerformed(imeAction)
+ }
+ }
+ ).also { ic = it }
+ }
+
+ /**
+ * Returns true if some editable component is focused.
+ */
+ fun isEditorFocused(): Boolean = editorHasFocus
+
+ override fun startInput(
+ value: TextFieldValue,
+ keyboardType: KeyboardType,
+ imeAction: ImeAction,
+ onEditCommand: (List<EditOperation>) -> Unit,
+ onImeActionPerformed: (ImeAction) -> Unit
+ ) {
+ imm = view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+ editorHasFocus = true
+ state = value
+ this.keyboardType = keyboardType
+ this.imeAction = imeAction
+ this.>
+ this.>
+
+ view.requestFocus()
+ view.post {
+ imm.restartInput(view)
+ imm.showSoftInput(view, 0)
+ }
+ }
+
+ override fun stopInput() {
+ editorHasFocus = false
+ >
+ >
+ focusedRect = null
+
+ imm.restartInput(view)
+ editorHasFocus = false
+ }
+
+ override fun showSoftwareKeyboard() {
+ imm.showSoftInput(view, 0)
+ }
+
+ override fun hideSoftwareKeyboard() {
+ imm.hideSoftInputFromWindow(view.windowToken, 0)
+ }
+
+ override fun onStateUpdated(value: TextFieldValue) {
+ this.state = value
+ ic?.updateInputState(this.state, imm, view)
+ }
+
+ override fun notifyFocusedRect(rect: Rect) {
+ focusedRect = android.graphics.Rect(
+ rect.left.roundToInt(),
+ rect.top.roundToInt(),
+ rect.right.roundToInt(),
+ rect.bottom.roundToInt()
+ )
+
+ // Requesting rectangle too early after obtaining focus may bring view into wrong place
+ // probably due to transient IME inset change. We don't know the correct timing of calling
+ // requestRectangleOnScreen API, so try to call this API only after the IME is ready to
+ // use, i.e. InputConnection has created.
+ // Even if we miss all the timing of requesting rectangle during initial text field focus,
+ // focused rectangle will be requested when software keyboard has shown.
+ if (ic == null) {
+ view.requestRectangleOnScreen(focusedRect)
+ }
+ }
+
+ /**
+ * Fills necessary info of EditorInfo.
+ */
+ private fun fillEditorInfo(
+ keyboardType: KeyboardType,
+ imeAction: ImeAction,
+ outInfo: EditorInfo
+ ) {
+ outInfo.imeOptions = when (imeAction) {
+ ImeAction.Unspecified -> EditorInfo.IME_ACTION_UNSPECIFIED
+ ImeAction.NoAction -> EditorInfo.IME_ACTION_NONE
+ ImeAction.Go -> EditorInfo.IME_ACTION_GO
+ ImeAction.Next -> EditorInfo.IME_ACTION_NEXT
+ ImeAction.Previous -> EditorInfo.IME_ACTION_PREVIOUS
+ ImeAction.Search -> EditorInfo.IME_ACTION_SEARCH
+ ImeAction.Send -> EditorInfo.IME_ACTION_SEND
+ ImeAction.Done -> EditorInfo.IME_ACTION_DONE
+ else -> throw IllegalArgumentException("Unknown ImeAction: $imeAction")
+ }
+ when (keyboardType) {
+ KeyboardType.Text -> outInfo.inputType = InputType.TYPE_CLASS_TEXT
+ KeyboardType.Ascii -> {
+ outInfo.inputType = InputType.TYPE_CLASS_TEXT
+ outInfo.imeOptions = outInfo.imeOptions or EditorInfo.IME_FLAG_FORCE_ASCII
+ }
+ KeyboardType.Number -> outInfo.inputType = InputType.TYPE_CLASS_NUMBER
+ KeyboardType.Phone -> outInfo.inputType = InputType.TYPE_CLASS_PHONE
+ KeyboardType.Uri ->
+ outInfo.inputType = InputType.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_URI
+ KeyboardType.Email ->
+ outInfo.inputType =
+ InputType.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
+ KeyboardType.Password -> {
+ outInfo.inputType =
+ InputType.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_PASSWORD
+ }
+ KeyboardType.NumberPassword -> {
+ outInfo.inputType =
+ InputType.TYPE_CLASS_NUMBER or EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD
+ }
+ else -> throw IllegalArgumentException("Unknown KeyboardType: $keyboardType")
+ }
+ outInfo.imeOptions =
+ outInfo.imeOptions or outInfo.imeOptions or EditorInfo.IME_FLAG_NO_FULLSCREEN
+ }
+}
\ No newline at end of file