blob: 3bf44ac92a58e0534ab6d34657ddc4cbfe37610a [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
19import static android.view.InsetsState.TYPE_IME;
20
Tarandeep Singh46d59f02019-01-29 18:09:15 -080021import android.inputmethodservice.InputMethodService;
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -080022import android.os.Parcel;
23import android.text.TextUtils;
24import android.view.SurfaceControl.Transaction;
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -080025import android.view.inputmethod.EditorInfo;
26import android.view.inputmethod.InputMethodManager;
27
28import com.android.internal.annotations.VisibleForTesting;
29
30import java.util.Arrays;
31import java.util.function.Supplier;
32
33/**
34 * Controls the visibility and animations of IME window insets source.
35 * @hide
36 */
37public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
38 private EditorInfo mFocusedEditor;
39 private EditorInfo mPreRenderedEditor;
40 /**
41 * Determines if IME would be shown next time IME is pre-rendered for currently focused
42 * editor {@link #mFocusedEditor} if {@link #isServedEditorRendered} is {@code true}.
43 */
44 private boolean mShowOnNextImeRender;
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -080045
46 public ImeInsetsSourceConsumer(
47 InsetsState state, Supplier<Transaction> transactionSupplier,
48 InsetsController controller) {
49 super(TYPE_IME, state, transactionSupplier, controller);
50 }
51
52 public void onPreRendered(EditorInfo info) {
53 mPreRenderedEditor = info;
54 if (mShowOnNextImeRender) {
55 mShowOnNextImeRender = false;
56 if (isServedEditorRendered()) {
57 applyImeVisibility(true /* setVisible */);
58 }
59 }
60 }
61
62 public void onServedEditorChanged(EditorInfo info) {
63 if (isDummyOrEmptyEditor(info)) {
64 mShowOnNextImeRender = false;
65 }
66 mFocusedEditor = info;
67 }
68
69 public void applyImeVisibility(boolean setVisible) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -080070 mController.applyImeVisibility(setVisible);
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -080071 }
72
73 @Override
74 public void onWindowFocusGained() {
Tarandeep Singh92d2dd32019-08-07 14:45:01 -070075 super.onWindowFocusGained();
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -080076 getImm().registerImeConsumer(this);
77 }
78
79 @Override
80 public void onWindowFocusLost() {
Tarandeep Singh92d2dd32019-08-07 14:45:01 -070081 super.onWindowFocusLost();
Louis Chang3d4a7d12019-04-12 16:18:30 +080082 getImm().unregisterImeConsumer(this);
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -080083 }
84
Tarandeep Singh46d59f02019-01-29 18:09:15 -080085 /**
86 * Request {@link InputMethodManager} to show the IME.
87 * @return @see {@link android.view.InsetsSourceConsumer.ShowResult}.
88 */
89 @Override
90 @ShowResult int requestShow(boolean fromIme) {
91 // TODO: ResultReceiver for IME.
92 // TODO: Set mShowOnNextImeRender to automatically show IME and guard it with a flag.
93 if (fromIme) {
94 return ShowResult.SHOW_IMMEDIATELY;
95 }
96
97 return getImm().requestImeShow(null /* resultReceiver */)
98 ? ShowResult.SHOW_DELAYED : ShowResult.SHOW_FAILED;
99 }
100
101 /**
102 * Notify {@link InputMethodService} that IME window is hidden.
103 */
104 @Override
105 void notifyHidden() {
106 getImm().notifyImeHidden();
107 }
108
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -0800109 private boolean isDummyOrEmptyEditor(EditorInfo info) {
110 // TODO(b/123044812): Handle dummy input gracefully in IME Insets API
111 return info == null || (info.fieldId <= 0 && info.inputType <= 0);
112 }
113
114 private boolean isServedEditorRendered() {
115 if (mFocusedEditor == null || mPreRenderedEditor == null
116 || isDummyOrEmptyEditor(mFocusedEditor)
117 || isDummyOrEmptyEditor(mPreRenderedEditor)) {
118 // No view is focused or ready.
119 return false;
120 }
121 return areEditorsSimilar(mFocusedEditor, mPreRenderedEditor);
122 }
123
124 @VisibleForTesting
125 public static boolean areEditorsSimilar(EditorInfo info1, EditorInfo info2) {
126 // We don't need to compare EditorInfo.fieldId (View#id) since that shouldn't change
127 // IME views.
128 boolean areOptionsSimilar =
129 info1.imeOptions == info2.imeOptions
130 && info1.inputType == info2.inputType
131 && TextUtils.equals(info1.packageName, info2.packageName);
132 areOptionsSimilar &= info1.privateImeOptions != null
133 ? info1.privateImeOptions.equals(info2.privateImeOptions) : true;
134
135 if (!areOptionsSimilar) {
136 return false;
137 }
138
139 // compare bundle extras.
140 if ((info1.extras == null && info2.extras == null) || info1.extras == info2.extras) {
141 return true;
142 }
143 if ((info1.extras == null && info2.extras != null)
144 || (info1.extras == null && info2.extras != null)) {
145 return false;
146 }
147 if (info1.extras.hashCode() == info2.extras.hashCode()
148 || info1.extras.equals(info1)) {
149 return true;
150 }
151 if (info1.extras.size() != info2.extras.size()) {
152 return false;
153 }
154 if (info1.extras.toString().equals(info2.extras.toString())) {
155 return true;
156 }
157
158 // Compare bytes
159 Parcel parcel1 = Parcel.obtain();
160 info1.extras.writeToParcel(parcel1, 0);
161 parcel1.setDataPosition(0);
162 Parcel parcel2 = Parcel.obtain();
163 info2.extras.writeToParcel(parcel2, 0);
164 parcel2.setDataPosition(0);
165
166 return Arrays.equals(parcel1.createByteArray(), parcel2.createByteArray());
167 }
168
169 private InputMethodManager getImm() {
Charles Chen5299ad02019-04-18 13:09:53 +0000170 return mController.getViewRoot().mContext.getSystemService(InputMethodManager.class);
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -0800171 }
172}