• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 
17 package android.inputmethodservice;
18 
19 import static android.view.WindowManager.LayoutParams;
20 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
21 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
22 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
23 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
24 
25 import android.annotation.NonNull;
26 import android.content.Context;
27 import android.os.IBinder;
28 import android.util.Slog;
29 import android.view.View;
30 import android.view.ViewGroup;
31 import android.view.ViewTreeObserver;
32 import android.view.WindowManager;
33 
34 import com.android.internal.policy.PhoneWindow;
35 
36 /**
37  * Window of type {@code LayoutParams.TYPE_INPUT_METHOD_DIALOG} for drawing
38  * Handwriting Ink on screen.
39  * @hide
40  */
41 final class InkWindow extends PhoneWindow {
42 
43     private final WindowManager mWindowManager;
44     private boolean mIsViewAdded;
45     private View mInkView;
46     private InkVisibilityListener mInkViewVisibilityListener;
47     private ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener;
48 
InkWindow(@onNull Context context)49     public InkWindow(@NonNull Context context) {
50         super(context);
51 
52         setType(LayoutParams.TYPE_INPUT_METHOD);
53         final LayoutParams attrs = getAttributes();
54         attrs.layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
55         attrs.setFitInsetsTypes(0);
56         // TODO(b/210039666): use INPUT_FEATURE_NO_INPUT_CHANNEL once b/216179339 is fixed.
57         setAttributes(attrs);
58         // Ink window is not touchable with finger.
59         addFlags(FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_NO_LIMITS | FLAG_NOT_TOUCHABLE
60                 | FLAG_NOT_FOCUSABLE);
61         setBackgroundDrawableResource(android.R.color.transparent);
62         setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
63         mWindowManager = context.getSystemService(WindowManager.class);
64     }
65 
66     /**
67      * Initialize InkWindow if we only want to create and draw surface but not show it.
68      */
initOnly()69     void initOnly() {
70         show(true /* keepInvisible */);
71     }
72 
73     /**
74      * Method to show InkWindow on screen.
75      * Emulates internal behavior similar to Dialog.show().
76      */
show()77     void show() {
78         show(false /* keepInvisible */);
79     }
80 
show(boolean keepInvisible)81     private void show(boolean keepInvisible) {
82         if (getDecorView() == null) {
83             Slog.i(InputMethodService.TAG, "DecorView is not set for InkWindow. show() failed.");
84             return;
85         }
86         getDecorView().setVisibility(keepInvisible ? View.INVISIBLE : View.VISIBLE);
87         if (!mIsViewAdded) {
88             mWindowManager.addView(getDecorView(), getAttributes());
89             mIsViewAdded = true;
90         }
91     }
92 
93     /**
94      * Method to hide InkWindow from screen.
95      * Emulates internal behavior similar to Dialog.hide().
96      * @param remove set {@code true} to remove InkWindow surface completely.
97      */
hide(boolean remove)98     void hide(boolean remove) {
99         if (getDecorView() != null) {
100             getDecorView().setVisibility(remove ? View.GONE : View.INVISIBLE);
101         }
102         //TODO(b/210039666): remove window from WM after a delay. Delay amount TBD.
103     }
104 
setToken(@onNull IBinder token)105     void setToken(@NonNull IBinder token) {
106         WindowManager.LayoutParams lp = getAttributes();
107         lp.token = token;
108         setAttributes(lp);
109     }
110 
111     @Override
addContentView(View view, ViewGroup.LayoutParams params)112     public void addContentView(View view, ViewGroup.LayoutParams params) {
113         if (mInkView == null) {
114             mInkView = view;
115         } else if (mInkView != view) {
116             throw new IllegalStateException("Only one Child Inking view is permitted.");
117         }
118         super.addContentView(view, params);
119         initInkViewVisibilityListener();
120     }
121 
122     @Override
setContentView(View view, ViewGroup.LayoutParams params)123     public void setContentView(View view, ViewGroup.LayoutParams params) {
124         mInkView = view;
125         super.setContentView(view, params);
126         initInkViewVisibilityListener();
127     }
128 
129     @Override
setContentView(View view)130     public void setContentView(View view) {
131         mInkView = view;
132         super.setContentView(view);
133         initInkViewVisibilityListener();
134     }
135 
136     @Override
clearContentView()137     public void clearContentView() {
138         if (mGlobalLayoutListener != null && mInkView != null) {
139             mInkView.getViewTreeObserver().removeOnGlobalLayoutListener(mGlobalLayoutListener);
140         }
141         mGlobalLayoutListener = null;
142         mInkView = null;
143         super.clearContentView();
144     }
145 
146     /**
147     * Listener used by InkWindow to time the dispatching of {@link MotionEvent}s to Ink view, once
148     * it is visible to user.
149     */
150     interface InkVisibilityListener {
onInkViewVisible()151         void onInkViewVisible();
152     }
153 
setInkViewVisibilityListener(InkVisibilityListener listener)154     void setInkViewVisibilityListener(InkVisibilityListener listener) {
155         mInkViewVisibilityListener = listener;
156         initInkViewVisibilityListener();
157     }
158 
initInkViewVisibilityListener()159     void initInkViewVisibilityListener() {
160         if (mInkView == null || mInkViewVisibilityListener == null
161                 || mGlobalLayoutListener != null) {
162             return;
163         }
164         mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
165             @Override
166             public void onGlobalLayout() {
167                 if (mInkView.isVisibleToUser()) {
168                     if (mInkViewVisibilityListener != null) {
169                         mInkViewVisibilityListener.onInkViewVisible();
170                     }
171                     mInkView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
172                     mGlobalLayoutListener = null;
173                 }
174             }
175         };
176         mInkView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
177     }
178 
isInkViewVisible()179     boolean isInkViewVisible() {
180         return getDecorView().getVisibility() == View.VISIBLE
181                 && mInkView != null && mInkView.isVisibleToUser();
182     }
183 }
184