[go: nahoru, domu]

blob: 6ca80371cf17bf16179be34757b5cff99e2386ae [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 Nonaka4e45c462019-07-01 12:12:59 -070025import androidx.ui.core.TextRange
Seigo Nonaka615f32d2019-06-18 15:33:38 -070026import androidx.ui.input.EditOperation
Seigo Nonaka4b211592019-04-07 14:11:28 -070027import androidx.ui.input.EditorState
Seigo Nonaka615f32d2019-06-18 15:33:38 -070028import androidx.ui.input.InputEventListener
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -070029import androidx.ui.input.KeyboardType
Seigo Nonaka4b211592019-04-07 14:11:28 -070030import androidx.ui.input.TextInputService
31
32/**
33 * Provide Android specific input service with the Operating System.
34 */
35internal class TextInputServiceAndroid(val view: View) : TextInputService {
36 /** True if the currently editable widget has connected */
37 private var editorHasFocus = false
38
39 /**
40 * The following three observers are set when the editable widget has initiated the input
41 * session
42 */
Seigo Nonaka615f32d2019-06-18 15:33:38 -070043 private var onEditCommand: (List<EditOperation>) -> Unit = {}
Seigo Nonaka4b211592019-04-07 14:11:28 -070044 private var onEditorActionPerformed: (Any) -> Unit = {}
Seigo Nonaka4b211592019-04-07 14:11:28 -070045
Seigo Nonaka4e45c462019-07-01 12:12:59 -070046 private var state = InputState(text = "", selection = TextRange(0, 0))
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -070047 private var keyboardType = KeyboardType.Text
Seigo Nonaka4e45c462019-07-01 12:12:59 -070048 private var ic: RecordingInputConnection? = null
49
Seigo Nonaka4b211592019-04-07 14:11:28 -070050 /**
51 * The editable buffer used for BaseInputConnection.
52 */
Seigo Nonaka4b211592019-04-07 14:11:28 -070053 private val imm =
54 view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
55
56 /**
57 * Creates new input connection.
58 */
59 fun createInputConnection(outAttrs: EditorInfo): InputConnection? {
60 if (!editorHasFocus) {
61 return null
62 }
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -070063 fillEditorInfo(keyboardType, outAttrs)
Seigo Nonaka4b211592019-04-07 14:11:28 -070064
Seigo Nonaka4e45c462019-07-01 12:12:59 -070065 return RecordingInputConnection(
66 initState = state,
67 eventListener = object : InputEventListener {
68 override fun onEditOperations(editOps: List<EditOperation>) {
69 onEditCommand(editOps)
70 }
Seigo Nonaka615f32d2019-06-18 15:33:38 -070071 }
Seigo Nonaka4e45c462019-07-01 12:12:59 -070072 ).also { ic = it }
Seigo Nonaka4b211592019-04-07 14:11:28 -070073 }
74
75 /**
76 * Returns true if some editable component is focused.
77 */
78 fun isEditorFocused(): Boolean = editorHasFocus
79
80 override fun startInput(
81 initState: EditorState,
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -070082 keyboardType: KeyboardType,
Seigo Nonaka615f32d2019-06-18 15:33:38 -070083 onEditCommand: (List<EditOperation>) -> Unit,
Seigo Nonaka5eb38c02019-06-27 18:11:59 -070084 onEditorActionPerformed: (Any) -> Unit
Seigo Nonaka4b211592019-04-07 14:11:28 -070085 ) {
86 editorHasFocus = true
Seigo Nonaka4e45c462019-07-01 12:12:59 -070087 state = initState.toInputState()
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -070088 this.keyboardType = keyboardType
Seigo Nonaka615f32d2019-06-18 15:33:38 -070089 this.onEditCommand = onEditCommand
Seigo Nonaka4b211592019-04-07 14:11:28 -070090 this.onEditorActionPerformed = onEditorActionPerformed
Seigo Nonaka4b211592019-04-07 14:11:28 -070091
92 view.requestFocus()
93 view.post {
94 imm.restartInput(view)
95 imm.showSoftInput(view, 0)
96 }
97 }
98
99 override fun stopInput() {
100 editorHasFocus = false
Seigo Nonaka615f32d2019-06-18 15:33:38 -0700101 onEditCommand = {}
Seigo Nonaka4b211592019-04-07 14:11:28 -0700102 onEditorActionPerformed = {}
Seigo Nonaka4b211592019-04-07 14:11:28 -0700103
104 imm.restartInput(view)
105 }
Seigo Nonaka97c9f0b2019-07-01 14:22:07 -0700106
107 override fun showSoftwareKeyboard() {
108 imm.showSoftInput(view, 0)
109 }
Seigo Nonaka4e45c462019-07-01 12:12:59 -0700110
111 override fun onStateUpdated(state: EditorState) {
112 this.state = state.toInputState()
113 ic?.updateInputState(this.state, imm, view)
114 }
Seigo Nonaka6e6e2ee2019-07-01 17:18:41 -0700115
116 /**
117 * Fills necessary info of EditorInfo.
118 */
119 private fun fillEditorInfo(keyboardType: KeyboardType, outInfo: EditorInfo) {
120 when (keyboardType) {
121 KeyboardType.Text -> outInfo.inputType = InputType.TYPE_CLASS_TEXT
122 KeyboardType.ASCII -> {
123 outInfo.inputType = InputType.TYPE_CLASS_TEXT
124 outInfo.imeOptions = EditorInfo.IME_FLAG_FORCE_ASCII
125 }
126 KeyboardType.Number -> outInfo.inputType = InputType.TYPE_CLASS_NUMBER
127 KeyboardType.Phone -> outInfo.inputType = InputType.TYPE_CLASS_PHONE
128 KeyboardType.URI ->
129 outInfo.inputType = InputType.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_URI
130 KeyboardType.Email ->
131 outInfo.inputType =
132 InputType.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
133 else -> throw IllegalArgumentException("Unknown KeyboardType: $keyboardType")
134 }
135 outInfo.imeOptions = outInfo.imeOptions or EditorInfo.IME_FLAG_NO_FULLSCREEN
136 }
Seigo Nonaka4e45c462019-07-01 12:12:59 -0700137}
138
139private fun EditorState.toInputState(): InputState =
140 InputState(
141 text = text, // TODO(nona): call toString once AnnotatedString is in use.
142 selection = selection,
143 composition = composition)