[go: nahoru, domu]

blob: 6f443a1f35a7e802ce3251bee431dda7621117f0 [file] [log] [blame]
Seigo Nonaka4b211592019-04-07 14:11:28 -07001/*
2 * Copyright 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Siyamed Sinire9b57be2019-11-02 01:25:03 -070017package androidx.ui.input
Seigo Nonaka4b211592019-04-07 14:11:28 -070018
19import android.content.Context
Seigo Nonaka4b211592019-04-07 14:11:28 -070020import android.text.InputType
21import android.view.View
22import android.view.inputmethod.EditorInfo
23import android.view.inputmethod.InputConnection
24import android.view.inputmethod.InputMethodManager
George Mount842c8c12020-01-08 16:03:42 -080025import androidx.ui.geometry.Rect
Seigo Nonakadd60a5c2019-07-10 11:23:36 -070026import androidx.ui.text.TextRange
27import kotlin.math.roundToInt
Seigo Nonaka4b211592019-04-07 14:11:28 -070028
29/**
30 * Provide Android specific input service with the Operating System.
31 */
Siyamed Sinir9da613b2019-11-15 20:39:19 -080032internal class TextInputServiceAndroid(val view: View) : PlatformTextInputService {
Louis Pullen-Freilich5da28bd2019-10-15 17:05:07 +010033 /** True if the currently editable composable has connected */
Seigo Nonaka4b211592019-04-07 14:11:28 -070034 private var editorHasFocus = false
35
36 /**
Louis Pullen-Freilich5da28bd2019-10-15 17:05:07 +010037 * The following three observers are set when the editable composable has initiated the input
Seigo Nonaka4b211592019-04-07 14:11:28 -070038 * session
39 */
Seigo Nonaka615f32d2019-06-18 15:33:38 -070040 private var onEditCommand: (List<EditOperation>) -> Unit = {}
Seigo Nonakac04f629d2019-07-11 13:20:30 -070041 private var onImeActionPerformed: (ImeAction) -> Unit = {}
Seigo Nonaka4b211592019-04-07 14:11:28 -070042
Seigo Nonaka4e45c462019-07-01 12:12:59 -070043 private var state = InputState(text = "", selection = TextRange(0, 0))
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -070044 private var keyboardType = KeyboardType.Text
Seigo Nonakac04f629d2019-07-11 13:20:30 -070045 private var imeAction = ImeAction.Unspecified
Seigo Nonaka4e45c462019-07-01 12:12:59 -070046 private var ic: RecordingInputConnection? = null
47
Seigo Nonaka4b211592019-04-07 14:11:28 -070048 /**
49 * The editable buffer used for BaseInputConnection.
50 */
Ralston Da Silvadff6b622019-08-07 15:29:06 -070051 private lateinit var imm: InputMethodManager
Seigo Nonaka4b211592019-04-07 14:11:28 -070052
53 /**
54 * Creates new input connection.
55 */
56 fun createInputConnection(outAttrs: EditorInfo): InputConnection? {
57 if (!editorHasFocus) {
58 return null
59 }
Seigo Nonakac04f629d2019-07-11 13:20:30 -070060 fillEditorInfo(keyboardType, imeAction, outAttrs)
Seigo Nonaka4b211592019-04-07 14:11:28 -070061
Seigo Nonaka4e45c462019-07-01 12:12:59 -070062 return RecordingInputConnection(
63 initState = state,
64 eventListener = object : InputEventListener {
65 override fun onEditOperations(editOps: List<EditOperation>) {
66 onEditCommand(editOps)
67 }
Seigo Nonakac04f629d2019-07-11 13:20:30 -070068
69 override fun onImeAction(imeAction: ImeAction) {
70 onImeActionPerformed(imeAction)
71 }
Seigo Nonaka615f32d2019-06-18 15:33:38 -070072 }
Seigo Nonaka4e45c462019-07-01 12:12:59 -070073 ).also { ic = it }
Seigo Nonaka4b211592019-04-07 14:11:28 -070074 }
75
76 /**
77 * Returns true if some editable component is focused.
78 */
79 fun isEditorFocused(): Boolean = editorHasFocus
80
81 override fun startInput(
Seigo Nonakaa7e05d72019-09-30 17:31:25 -070082 initModel: InputState,
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -070083 keyboardType: KeyboardType,
Seigo Nonakac04f629d2019-07-11 13:20:30 -070084 imeAction: ImeAction,
Seigo Nonaka615f32d2019-06-18 15:33:38 -070085 onEditCommand: (List<EditOperation>) -> Unit,
Seigo Nonakac04f629d2019-07-11 13:20:30 -070086 onImeActionPerformed: (ImeAction) -> Unit
Seigo Nonaka4b211592019-04-07 14:11:28 -070087 ) {
Ralston Da Silvadff6b622019-08-07 15:29:06 -070088 imm = view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
Seigo Nonaka4b211592019-04-07 14:11:28 -070089 editorHasFocus = true
Seigo Nonakaa7e05d72019-09-30 17:31:25 -070090 state = initModel
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -070091 this.keyboardType = keyboardType
Seigo Nonakac04f629d2019-07-11 13:20:30 -070092 this.imeAction = imeAction
Seigo Nonaka615f32d2019-06-18 15:33:38 -070093 this.onEditCommand = onEditCommand
Seigo Nonakac04f629d2019-07-11 13:20:30 -070094 this.onImeActionPerformed = onImeActionPerformed
Seigo Nonaka4b211592019-04-07 14:11:28 -070095
96 view.requestFocus()
97 view.post {
98 imm.restartInput(view)
99 imm.showSoftInput(view, 0)
100 }
101 }
102
103 override fun stopInput() {
104 editorHasFocus = false
Seigo Nonaka615f32d2019-06-18 15:33:38 -0700105 onEditCommand = {}
Seigo Nonakac04f629d2019-07-11 13:20:30 -0700106 onImeActionPerformed = {}
Seigo Nonaka4b211592019-04-07 14:11:28 -0700107
108 imm.restartInput(view)
109 }
Seigo Nonaka97c9f0b2019-07-01 14:22:07 -0700110
111 override fun showSoftwareKeyboard() {
112 imm.showSoftInput(view, 0)
113 }
Seigo Nonaka4e45c462019-07-01 12:12:59 -0700114
Seigo Nonakaa7e05d72019-09-30 17:31:25 -0700115 override fun onStateUpdated(model: InputState) {
116 this.state = model
Seigo Nonaka4e45c462019-07-01 12:12:59 -0700117 ic?.updateInputState(this.state, imm, view)
118 }
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -0700119
Seigo Nonakadd60a5c2019-07-10 11:23:36 -0700120 override fun notifyFocusedRect(rect: Rect) {
121 view.requestRectangleOnScreen(android.graphics.Rect(
122 rect.left.roundToInt(),
123 rect.top.roundToInt(),
124 rect.right.roundToInt(),
125 rect.bottom.roundToInt()))
126 }
127
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -0700128 /**
129 * Fills necessary info of EditorInfo.
130 */
Seigo Nonakac04f629d2019-07-11 13:20:30 -0700131 private fun fillEditorInfo(
132 keyboardType: KeyboardType,
133 imeAction: ImeAction,
134 outInfo: EditorInfo
135 ) {
136 outInfo.imeOptions = when (imeAction) {
137 ImeAction.Unspecified -> EditorInfo.IME_ACTION_UNSPECIFIED
138 ImeAction.NoAction -> EditorInfo.IME_ACTION_NONE
139 ImeAction.Go -> EditorInfo.IME_ACTION_GO
140 ImeAction.Next -> EditorInfo.IME_ACTION_NEXT
141 ImeAction.Previous -> EditorInfo.IME_ACTION_PREVIOUS
142 ImeAction.Search -> EditorInfo.IME_ACTION_SEARCH
143 ImeAction.Send -> EditorInfo.IME_ACTION_SEND
144 ImeAction.Done -> EditorInfo.IME_ACTION_DONE
145 else -> throw IllegalArgumentException("Unknown ImeAction: $imeAction")
146 }
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -0700147 when (keyboardType) {
148 KeyboardType.Text -> outInfo.inputType = InputType.TYPE_CLASS_TEXT
Siyamed Sinir3a952ea2019-07-08 16:50:50 -0700149 KeyboardType.Ascii -> {
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -0700150 outInfo.inputType = InputType.TYPE_CLASS_TEXT
Seigo Nonakac04f629d2019-07-11 13:20:30 -0700151 outInfo.imeOptions = outInfo.imeOptions or EditorInfo.IME_FLAG_FORCE_ASCII
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -0700152 }
153 KeyboardType.Number -> outInfo.inputType = InputType.TYPE_CLASS_NUMBER
154 KeyboardType.Phone -> outInfo.inputType = InputType.TYPE_CLASS_PHONE
Siyamed Sinir3a952ea2019-07-08 16:50:50 -0700155 KeyboardType.Uri ->
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -0700156 outInfo.inputType = InputType.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_URI
157 KeyboardType.Email ->
158 outInfo.inputType =
159 InputType.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
Seigo Nonaka813dc152019-07-15 17:15:05 -0700160 KeyboardType.Password -> {
161 outInfo.inputType =
162 InputType.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_PASSWORD
163 }
164 KeyboardType.NumberPassword -> {
165 outInfo.inputType =
166 InputType.TYPE_CLASS_NUMBER or EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD
167 }
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -0700168 else -> throw IllegalArgumentException("Unknown KeyboardType: $keyboardType")
169 }
Seigo Nonakac04f629d2019-07-11 13:20:30 -0700170 outInfo.imeOptions =
171 outInfo.imeOptions or outInfo.imeOptions or EditorInfo.IME_FLAG_NO_FULLSCREEN
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -0700172 }
Seigo Nonakaa7e05d72019-09-30 17:31:25 -0700173}