blob: 23ba097f2b6d121e8af5f15660083f199734b872 [file] [log] [blame]
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08001/*
2 * Copyright (C) 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 android.view;
18
Tiger Huang332793b2019-10-29 23:21:27 +080019import static android.view.InsetsState.ITYPE_IME;
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -080020
Jorim Jaggiabaa9042020-01-29 15:02:50 +010021import android.annotation.Nullable;
Tarandeep Singh46d59f02019-01-29 18:09:15 -080022import android.inputmethodservice.InputMethodService;
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -080023import android.os.Parcel;
24import android.text.TextUtils;
25import android.view.SurfaceControl.Transaction;
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -080026import android.view.inputmethod.EditorInfo;
27import android.view.inputmethod.InputMethodManager;
28
29import com.android.internal.annotations.VisibleForTesting;
30
31import java.util.Arrays;
32import java.util.function.Supplier;
33
34/**
35 * Controls the visibility and animations of IME window insets source.
36 * @hide
37 */
38public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
39 private EditorInfo mFocusedEditor;
40 private EditorInfo mPreRenderedEditor;
41 /**
42 * Determines if IME would be shown next time IME is pre-rendered for currently focused
43 * editor {@link #mFocusedEditor} if {@link #isServedEditorRendered} is {@code true}.
44 */
45 private boolean mShowOnNextImeRender;
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -080046
47 public ImeInsetsSourceConsumer(
48 InsetsState state, Supplier<Transaction> transactionSupplier,
49 InsetsController controller) {
Tiger Huang332793b2019-10-29 23:21:27 +080050 super(ITYPE_IME, state, transactionSupplier, controller);
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -080051 }
52
53 public void onPreRendered(EditorInfo info) {
54 mPreRenderedEditor = info;
55 if (mShowOnNextImeRender) {
56 mShowOnNextImeRender = false;
57 if (isServedEditorRendered()) {
58 applyImeVisibility(true /* setVisible */);
59 }
60 }
61 }
62
63 public void onServedEditorChanged(EditorInfo info) {
64 if (isDummyOrEmptyEditor(info)) {
65 mShowOnNextImeRender = false;
66 }
67 mFocusedEditor = info;
68 }
69
70 public void applyImeVisibility(boolean setVisible) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -080071 mController.applyImeVisibility(setVisible);
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -080072 }
73
74 @Override
75 public void onWindowFocusGained() {
Tarandeep Singh92d2dd32019-08-07 14:45:01 -070076 super.onWindowFocusGained();
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -080077 getImm().registerImeConsumer(this);
78 }
79
80 @Override
81 public void onWindowFocusLost() {
Tarandeep Singh92d2dd32019-08-07 14:45:01 -070082 super.onWindowFocusLost();
Louis Chang3d4a7d12019-04-12 16:18:30 +080083 getImm().unregisterImeConsumer(this);
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -080084 }
85
Jorim Jaggiabaa9042020-01-29 15:02:50 +010086 @Override
87 public void setControl(@Nullable InsetsSourceControl control) {
88 super.setControl(control);
89 if (control == null) {
90 hide();
91 }
92 }
93
Tarandeep Singh46d59f02019-01-29 18:09:15 -080094 /**
95 * Request {@link InputMethodManager} to show the IME.
96 * @return @see {@link android.view.InsetsSourceConsumer.ShowResult}.
97 */
98 @Override
Jorim Jaggid7f10ed2020-01-08 21:41:55 +010099 public @ShowResult int requestShow(boolean fromIme) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800100 // TODO: ResultReceiver for IME.
101 // TODO: Set mShowOnNextImeRender to automatically show IME and guard it with a flag.
102 if (fromIme) {
103 return ShowResult.SHOW_IMMEDIATELY;
104 }
105
106 return getImm().requestImeShow(null /* resultReceiver */)
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100107 ? ShowResult.IME_SHOW_DELAYED : ShowResult.IME_SHOW_FAILED;
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800108 }
109
110 /**
111 * Notify {@link InputMethodService} that IME window is hidden.
112 */
113 @Override
114 void notifyHidden() {
115 getImm().notifyImeHidden();
116 }
117
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -0800118 private boolean isDummyOrEmptyEditor(EditorInfo info) {
119 // TODO(b/123044812): Handle dummy input gracefully in IME Insets API
120 return info == null || (info.fieldId <= 0 && info.inputType <= 0);
121 }
122
123 private boolean isServedEditorRendered() {
124 if (mFocusedEditor == null || mPreRenderedEditor == null
125 || isDummyOrEmptyEditor(mFocusedEditor)
126 || isDummyOrEmptyEditor(mPreRenderedEditor)) {
127 // No view is focused or ready.
128 return false;
129 }
130 return areEditorsSimilar(mFocusedEditor, mPreRenderedEditor);
131 }
132
133 @VisibleForTesting
134 public static boolean areEditorsSimilar(EditorInfo info1, EditorInfo info2) {
135 // We don't need to compare EditorInfo.fieldId (View#id) since that shouldn't change
136 // IME views.
137 boolean areOptionsSimilar =
138 info1.imeOptions == info2.imeOptions
139 && info1.inputType == info2.inputType
140 && TextUtils.equals(info1.packageName, info2.packageName);
141 areOptionsSimilar &= info1.privateImeOptions != null
142 ? info1.privateImeOptions.equals(info2.privateImeOptions) : true;
143
144 if (!areOptionsSimilar) {
145 return false;
146 }
147
148 // compare bundle extras.
149 if ((info1.extras == null && info2.extras == null) || info1.extras == info2.extras) {
150 return true;
151 }
152 if ((info1.extras == null && info2.extras != null)
153 || (info1.extras == null && info2.extras != null)) {
154 return false;
155 }
156 if (info1.extras.hashCode() == info2.extras.hashCode()
157 || info1.extras.equals(info1)) {
158 return true;
159 }
160 if (info1.extras.size() != info2.extras.size()) {
161 return false;
162 }
163 if (info1.extras.toString().equals(info2.extras.toString())) {
164 return true;
165 }
166
167 // Compare bytes
168 Parcel parcel1 = Parcel.obtain();
169 info1.extras.writeToParcel(parcel1, 0);
170 parcel1.setDataPosition(0);
171 Parcel parcel2 = Parcel.obtain();
172 info2.extras.writeToParcel(parcel2, 0);
173 parcel2.setDataPosition(0);
174
175 return Arrays.equals(parcel1.createByteArray(), parcel2.createByteArray());
176 }
177
178 private InputMethodManager getImm() {
Charles Chen5299ad02019-04-18 13:09:53 +0000179 return mController.getViewRoot().mContext.getSystemService(InputMethodManager.class);
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -0800180 }
181}