File: ImeInsetsSourceConsumer.java

package info (click to toggle)
android-platform-frameworks-base 1%3A10.0.0%2Br36-3
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 321,788 kB
  • sloc: java: 962,234; cpp: 274,314; xml: 242,770; python: 5,060; sh: 1,432; ansic: 494; makefile: 47; sed: 19
file content (178 lines) | stat: -rw-r--r-- 6,030 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package android.view;

import static android.view.InsetsState.TYPE_IME;

import android.inputmethodservice.InputMethodService;
import android.os.Parcel;
import android.text.TextUtils;
import android.view.SurfaceControl.Transaction;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;

import com.android.internal.annotations.VisibleForTesting;

import java.util.Arrays;
import java.util.function.Supplier;

/**
 * Controls the visibility and animations of IME window insets source.
 * @hide
 */
public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
    private EditorInfo mFocusedEditor;
    private EditorInfo mPreRenderedEditor;
    /**
     * Determines if IME would be shown next time IME is pre-rendered for currently focused
     * editor {@link #mFocusedEditor} if {@link #isServedEditorRendered} is {@code true}.
     */
    private boolean mShowOnNextImeRender;
    private boolean mHasWindowFocus;

    public ImeInsetsSourceConsumer(
            InsetsState state, Supplier<Transaction> transactionSupplier,
            InsetsController controller) {
        super(TYPE_IME, state, transactionSupplier, controller);
    }

    public void onPreRendered(EditorInfo info) {
        mPreRenderedEditor = info;
        if (mShowOnNextImeRender) {
            mShowOnNextImeRender = false;
            if (isServedEditorRendered()) {
                applyImeVisibility(true /* setVisible */);
            }
        }
    }

    public void onServedEditorChanged(EditorInfo info) {
        if (isDummyOrEmptyEditor(info)) {
            mShowOnNextImeRender = false;
        }
        mFocusedEditor = info;
    }

    public void applyImeVisibility(boolean setVisible) {
        if (!mHasWindowFocus) {
            // App window doesn't have focus, any visibility changes would be no-op.
            return;
        }

        mController.applyImeVisibility(setVisible);
    }

    @Override
    public void onWindowFocusGained() {
        mHasWindowFocus = true;
        getImm().registerImeConsumer(this);
    }

    @Override
    public void onWindowFocusLost() {
        mHasWindowFocus = false;
        getImm().unregisterImeConsumer(this);
    }

    /**
     * Request {@link InputMethodManager} to show the IME.
     * @return @see {@link android.view.InsetsSourceConsumer.ShowResult}.
     */
    @Override
    @ShowResult int requestShow(boolean fromIme) {
        // TODO: ResultReceiver for IME.
        // TODO: Set mShowOnNextImeRender to automatically show IME and guard it with a flag.
        if (fromIme) {
            return ShowResult.SHOW_IMMEDIATELY;
        }

        return getImm().requestImeShow(null /* resultReceiver */)
                ? ShowResult.SHOW_DELAYED : ShowResult.SHOW_FAILED;
    }

    /**
     * Notify {@link InputMethodService} that IME window is hidden.
     */
    @Override
    void notifyHidden() {
        getImm().notifyImeHidden();
    }

    private boolean isDummyOrEmptyEditor(EditorInfo info) {
        // TODO(b/123044812): Handle dummy input gracefully in IME Insets API
        return info == null || (info.fieldId <= 0 && info.inputType <= 0);
    }

    private boolean isServedEditorRendered() {
        if (mFocusedEditor == null || mPreRenderedEditor == null
                || isDummyOrEmptyEditor(mFocusedEditor)
                || isDummyOrEmptyEditor(mPreRenderedEditor)) {
            // No view is focused or ready.
            return false;
        }
        return areEditorsSimilar(mFocusedEditor, mPreRenderedEditor);
    }

    @VisibleForTesting
    public static boolean areEditorsSimilar(EditorInfo info1, EditorInfo info2) {
        // We don't need to compare EditorInfo.fieldId (View#id) since that shouldn't change
        // IME views.
        boolean areOptionsSimilar =
                info1.imeOptions == info2.imeOptions
                && info1.inputType == info2.inputType
                && TextUtils.equals(info1.packageName, info2.packageName);
        areOptionsSimilar &= info1.privateImeOptions != null
                ? info1.privateImeOptions.equals(info2.privateImeOptions) : true;

        if (!areOptionsSimilar) {
            return false;
        }

        // compare bundle extras.
        if ((info1.extras == null && info2.extras == null) || info1.extras == info2.extras) {
            return true;
        }
        if ((info1.extras == null && info2.extras != null)
                || (info1.extras == null && info2.extras != null)) {
            return false;
        }
        if (info1.extras.hashCode() == info2.extras.hashCode()
                || info1.extras.equals(info1)) {
            return true;
        }
        if (info1.extras.size() != info2.extras.size()) {
            return false;
        }
        if (info1.extras.toString().equals(info2.extras.toString())) {
            return true;
        }

        // Compare bytes
        Parcel parcel1 = Parcel.obtain();
        info1.extras.writeToParcel(parcel1, 0);
        parcel1.setDataPosition(0);
        Parcel parcel2 = Parcel.obtain();
        info2.extras.writeToParcel(parcel2, 0);
        parcel2.setDataPosition(0);

        return Arrays.equals(parcel1.createByteArray(), parcel2.createByteArray());
    }

    private InputMethodManager getImm() {
        return mController.getViewRoot().mContext.getSystemService(InputMethodManager.class);
    }
}