[go: nahoru, domu]

blob: ebd08266de654d9c8ec67750fa487c074499454a [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
17package androidx.ui.core.input
18
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
Seigo Nonakadd60a5c2019-07-10 11:23:36 -070025import androidx.ui.engine.geometry.Rect
Seigo Nonaka615f32d2019-06-18 15:33:38 -070026import androidx.ui.input.EditOperation
Seigo Nonaka92bc31a2019-07-30 15:40:52 -070027import androidx.ui.input.EditorModel
Seigo Nonakac04f629d2019-07-11 13:20:30 -070028import androidx.ui.input.ImeAction
Seigo Nonaka615f32d2019-06-18 15:33:38 -070029import androidx.ui.input.InputEventListener
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -070030import androidx.ui.input.KeyboardType
Seigo Nonaka4b211592019-04-07 14:11:28 -070031import androidx.ui.input.TextInputService
Seigo Nonakadd60a5c2019-07-10 11:23:36 -070032import androidx.ui.text.TextRange
33import kotlin.math.roundToInt
Seigo Nonaka4b211592019-04-07 14:11:28 -070034
35/**
36 * Provide Android specific input service with the Operating System.
37 */
38internal class TextInputServiceAndroid(val view: View) : TextInputService {
39 /** True if the currently editable widget has connected */
40 private var editorHasFocus = false
41
42 /**
43 * The following three observers are set when the editable widget has initiated the input
44 * session
45 */
Seigo Nonaka615f32d2019-06-18 15:33:38 -070046 private var onEditCommand: (List<EditOperation>) -> Unit = {}
Seigo Nonakac04f629d2019-07-11 13:20:30 -070047 private var onImeActionPerformed: (ImeAction) -> Unit = {}
Seigo Nonaka4b211592019-04-07 14:11:28 -070048
Seigo Nonaka4e45c462019-07-01 12:12:59 -070049 private var state = InputState(text = "", selection = TextRange(0, 0))
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -070050 private var keyboardType = KeyboardType.Text
Seigo Nonakac04f629d2019-07-11 13:20:30 -070051 private var imeAction = ImeAction.Unspecified
Seigo Nonaka4e45c462019-07-01 12:12:59 -070052 private var ic: RecordingInputConnection? = null
53
Seigo Nonaka4b211592019-04-07 14:11:28 -070054 /**
55 * The editable buffer used for BaseInputConnection.
56 */
Ralston Da Silvadff6b622019-08-07 15:29:06 -070057 private lateinit var imm: InputMethodManager
Seigo Nonaka4b211592019-04-07 14:11:28 -070058
59 /**
60 * Creates new input connection.
61 */
62 fun createInputConnection(outAttrs: EditorInfo): InputConnection? {
63 if (!editorHasFocus) {
64 return null
65 }
Seigo Nonakac04f629d2019-07-11 13:20:30 -070066 fillEditorInfo(keyboardType, imeAction, outAttrs)
Seigo Nonaka4b211592019-04-07 14:11:28 -070067
Seigo Nonaka4e45c462019-07-01 12:12:59 -070068 return RecordingInputConnection(
69 initState = state,
70 eventListener = object : InputEventListener {
71 override fun onEditOperations(editOps: List<EditOperation>) {
72 onEditCommand(editOps)
73 }
Seigo Nonakac04f629d2019-07-11 13:20:30 -070074
75 override fun onImeAction(imeAction: ImeAction) {
76 onImeActionPerformed(imeAction)
77 }
Seigo Nonaka615f32d2019-06-18 15:33:38 -070078 }
Seigo Nonaka4e45c462019-07-01 12:12:59 -070079 ).also { ic = it }
Seigo Nonaka4b211592019-04-07 14:11:28 -070080 }
81
82 /**
83 * Returns true if some editable component is focused.
84 */
85 fun isEditorFocused(): Boolean = editorHasFocus
86
87 override fun startInput(
Seigo Nonaka92bc31a2019-07-30 15:40:52 -070088 initModel: EditorModel,
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -070089 keyboardType: KeyboardType,
Seigo Nonakac04f629d2019-07-11 13:20:30 -070090 imeAction: ImeAction,
Seigo Nonaka615f32d2019-06-18 15:33:38 -070091 onEditCommand: (List<EditOperation>) -> Unit,
Seigo Nonakac04f629d2019-07-11 13:20:30 -070092 onImeActionPerformed: (ImeAction) -> Unit
Seigo Nonaka4b211592019-04-07 14:11:28 -070093 ) {
Ralston Da Silvadff6b622019-08-07 15:29:06 -070094 imm = view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
Seigo Nonaka4b211592019-04-07 14:11:28 -070095 editorHasFocus = true
Seigo Nonaka92bc31a2019-07-30 15:40:52 -070096 state = initModel.toInputState()
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -070097 this.keyboardType = keyboardType
Seigo Nonakac04f629d2019-07-11 13:20:30 -070098 this.imeAction = imeAction
Seigo Nonaka615f32d2019-06-18 15:33:38 -070099 this.onEditCommand = onEditCommand
Seigo Nonakac04f629d2019-07-11 13:20:30 -0700100 this.onImeActionPerformed = onImeActionPerformed
Seigo Nonaka4b211592019-04-07 14:11:28 -0700101
102 view.requestFocus()
103 view.post {
104 imm.restartInput(view)
105 imm.showSoftInput(view, 0)
106 }
107 }
108
109 override fun stopInput() {
110 editorHasFocus = false
Seigo Nonaka615f32d2019-06-18 15:33:38 -0700111 onEditCommand = {}
Seigo Nonakac04f629d2019-07-11 13:20:30 -0700112 onImeActionPerformed = {}
Seigo Nonaka4b211592019-04-07 14:11:28 -0700113
114 imm.restartInput(view)
115 }
Seigo Nonaka97c9f0b2019-07-01 14:22:07 -0700116
117 override fun showSoftwareKeyboard() {
118 imm.showSoftInput(view, 0)
119 }
Seigo Nonaka4e45c462019-07-01 12:12:59 -0700120
Seigo Nonaka92bc31a2019-07-30 15:40:52 -0700121 override fun onStateUpdated(model: EditorModel) {
122 this.state = model.toInputState()
Seigo Nonaka4e45c462019-07-01 12:12:59 -0700123 ic?.updateInputState(this.state, imm, view)
124 }
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -0700125
Seigo Nonakadd60a5c2019-07-10 11:23:36 -0700126 override fun notifyFocusedRect(rect: Rect) {
127 view.requestRectangleOnScreen(android.graphics.Rect(
128 rect.left.roundToInt(),
129 rect.top.roundToInt(),
130 rect.right.roundToInt(),
131 rect.bottom.roundToInt()))
132 }
133
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -0700134 /**
135 * Fills necessary info of EditorInfo.
136 */
Seigo Nonakac04f629d2019-07-11 13:20:30 -0700137 private fun fillEditorInfo(
138 keyboardType: KeyboardType,
139 imeAction: ImeAction,
140 outInfo: EditorInfo
141 ) {
142 outInfo.imeOptions = when (imeAction) {
143 ImeAction.Unspecified -> EditorInfo.IME_ACTION_UNSPECIFIED
144 ImeAction.NoAction -> EditorInfo.IME_ACTION_NONE
145 ImeAction.Go -> EditorInfo.IME_ACTION_GO
146 ImeAction.Next -> EditorInfo.IME_ACTION_NEXT
147 ImeAction.Previous -> EditorInfo.IME_ACTION_PREVIOUS
148 ImeAction.Search -> EditorInfo.IME_ACTION_SEARCH
149 ImeAction.Send -> EditorInfo.IME_ACTION_SEND
150 ImeAction.Done -> EditorInfo.IME_ACTION_DONE
151 else -> throw IllegalArgumentException("Unknown ImeAction: $imeAction")
152 }
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -0700153 when (keyboardType) {
154 KeyboardType.Text -> outInfo.inputType = InputType.TYPE_CLASS_TEXT
Siyamed Sinir3a952ea2019-07-08 16:50:50 -0700155 KeyboardType.Ascii -> {
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -0700156 outInfo.inputType = InputType.TYPE_CLASS_TEXT
Seigo Nonakac04f629d2019-07-11 13:20:30 -0700157 outInfo.imeOptions = outInfo.imeOptions or EditorInfo.IME_FLAG_FORCE_ASCII
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -0700158 }
159 KeyboardType.Number -> outInfo.inputType = InputType.TYPE_CLASS_NUMBER
160 KeyboardType.Phone -> outInfo.inputType = InputType.TYPE_CLASS_PHONE
Siyamed Sinir3a952ea2019-07-08 16:50:50 -0700161 KeyboardType.Uri ->
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -0700162 outInfo.inputType = InputType.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_URI
163 KeyboardType.Email ->
164 outInfo.inputType =
165 InputType.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
Seigo Nonaka813dc152019-07-15 17:15:05 -0700166 KeyboardType.Password -> {
167 outInfo.inputType =
168 InputType.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_PASSWORD
169 }
170 KeyboardType.NumberPassword -> {
171 outInfo.inputType =
172 InputType.TYPE_CLASS_NUMBER or EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD
173 }
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -0700174 else -> throw IllegalArgumentException("Unknown KeyboardType: $keyboardType")
175 }
Seigo Nonakac04f629d2019-07-11 13:20:30 -0700176 outInfo.imeOptions =
177 outInfo.imeOptions or outInfo.imeOptions or EditorInfo.IME_FLAG_NO_FULLSCREEN
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -0700178 }
Seigo Nonaka4e45c462019-07-01 12:12:59 -0700179}
180
Seigo Nonaka92bc31a2019-07-30 15:40:52 -0700181private fun EditorModel.toInputState(): InputState =
Seigo Nonaka4e45c462019-07-01 12:12:59 -0700182 InputState(
183 text = text, // TODO(nona): call toString once AnnotatedString is in use.
184 selection = selection,
185 composition = composition)