• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 package android.view;
17 
18 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
19 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
20 import static com.android.layoutlib.bridge.util.InsetUtil.getCurrentBounds;
21 
22 import android.content.Context;
23 import android.graphics.Color;
24 import android.graphics.Point;
25 import android.graphics.Rect;
26 import android.graphics.Region;
27 import android.graphics.drawable.ColorDrawable;
28 import android.graphics.drawable.Drawable;
29 import android.os.IBinder;
30 import android.util.DisplayMetrics;
31 import android.view.Display.Mode;
32 import android.widget.FrameLayout;
33 
34 import com.android.ide.common.rendering.api.ILayoutLog;
35 import com.android.internal.R;
36 import com.android.internal.policy.DecorView;
37 import com.android.layoutlib.bridge.Bridge;
38 import com.android.layoutlib.bridge.android.BridgeContext;
39 import com.android.server.wm.DisplayFrames;
40 
41 import java.util.ArrayList;
42 
43 public class WindowManagerImpl implements WindowManager {
44     private static final PrivacyIndicatorBounds sPrivacyIndicatorBounds =
45             new PrivacyIndicatorBounds();
46     private final Context mContext;
47     private final DisplayMetrics mMetrics;
48     private final DisplayInfo mDisplayInfo;
49     private Display mDisplay;
50     /**
51      * Root view of the base window, new windows will be added on top of this.
52      */
53     private ViewGroup mBaseRootView;
54     /**
55      * Root view of the current window at the top of the display,
56      * null if there is only the base window present.
57      */
58     private ViewGroup mCurrentRootView;
59     private DisplayFrames mDisplayFrames;
60 
WindowManagerImpl(BridgeContext context, DisplayMetrics metrics)61     public WindowManagerImpl(BridgeContext context, DisplayMetrics metrics) {
62         mContext = context;
63         mMetrics = metrics;
64 
65         mDisplayInfo = new DisplayInfo();
66         mDisplayInfo.logicalHeight = mMetrics.heightPixels;
67         mDisplayInfo.logicalWidth = mMetrics.widthPixels;
68         mDisplayInfo.supportedModes = new Mode[] {
69                 new Mode(0, mMetrics.widthPixels, mMetrics.heightPixels, 60f)
70         };
71         mDisplayInfo.logicalDensityDpi = mMetrics.densityDpi;
72         mDisplayInfo.displayCutout = DisplayCutout.NO_CUTOUT;
73     }
74 
createLocalWindowManager(Window parentWindow)75     public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
76         return this;
77     }
78 
createPresentationWindowManager(Context displayContext)79     public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
80         Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
81                 "The preview does not fully support multiple windows.",
82                 null, null, null);
83         return this;
84     }
85 
86     /**
87      * Sets the window token to assign when none is specified by the client or
88      * available from the parent window.
89      *
90      * @param token The default token to assign.
91      */
setDefaultToken(IBinder token)92     public void setDefaultToken(IBinder token) {
93 
94     }
95 
96     @Override
getDefaultDisplay()97     public Display getDefaultDisplay() {
98         if (mDisplay == null) {
99             mDisplay = new Display(null, Display.DEFAULT_DISPLAY, mDisplayInfo,
100                     mContext.getResources());
101         }
102         return mDisplay;
103     }
104 
105 
106     @Override
addView(View arg0, android.view.ViewGroup.LayoutParams arg1)107     public void addView(View arg0, android.view.ViewGroup.LayoutParams arg1) {
108         if (mBaseRootView == null) {
109             return;
110         }
111         if (mCurrentRootView == null) {
112             FrameLayout layout = new FrameLayout(mContext) {
113                 @Override
114                 public boolean dispatchTouchEvent(MotionEvent ev) {
115                     float offsetX = - getX();
116                     float offsetY = - getY();
117                     View baseRootParent = (View)mBaseRootView.getParent();
118                     if (baseRootParent != null) {
119                         offsetX -= baseRootParent.getX();
120                         offsetY -= baseRootParent.getY();
121                     }
122                     ev.offsetLocation(offsetX, offsetY);
123                     return super.dispatchTouchEvent(ev);
124                 }
125 
126                 @Override
127                 protected void measureChildWithMargins(View child, int parentWidthMeasureSpec,
128                         int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
129                     // This reproduces ViewRootImpl#measureHierarchy as this FrameLayout should
130                     // be treated as a ViewRoot.
131                     ViewGroup.LayoutParams lp = child.getLayoutParams();
132                     int parentWidth = MeasureSpec.getSize(parentWidthMeasureSpec);
133                     int parentHeight = MeasureSpec.getSize(parentHeightMeasureSpec);
134                     int childWidthMeasureSpec = 0;
135                     int childHeightMeasureSpec = ViewRootImpl.getRootMeasureSpec(parentHeight,
136                             lp.height, 0);
137                     if (lp.width == WRAP_CONTENT) {
138                         int baseSize =
139                                 mContext.getResources().getDimensionPixelSize(R.dimen.config_prefDialogWidth);
140                         if (baseSize != 0 && baseSize < parentWidth) {
141                             childWidthMeasureSpec = ViewRootImpl.getRootMeasureSpec(baseSize,
142                                     lp.width, 0);
143                         }
144                     }
145                     if (childWidthMeasureSpec == 0) {
146                         childWidthMeasureSpec = ViewRootImpl.getRootMeasureSpec(parentWidth,
147                                 lp.width, 0);
148                     }
149                     child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
150                 }
151             };
152             // The window root view should not handle touch events.
153             // Events need to be dispatched to the base view inside the window,
154             // with coordinates shifted accordingly.
155             layout.setOnTouchListener((v, event) -> {
156                 event.offsetLocation(-arg0.getX(), -arg0.getY());
157                 return arg0.dispatchTouchEvent(event);
158             });
159             int layoutMode = WRAP_CONTENT;
160             if (arg0 instanceof DecorView) {
161                 // DecorView background should cover the entire screen
162                 layoutMode = MATCH_PARENT;
163             }
164             mBaseRootView.addView(layout, new FrameLayout.LayoutParams(layoutMode, layoutMode));
165             mCurrentRootView = layout;
166         }
167 
168         FrameLayout.LayoutParams frameLayoutParams = new FrameLayout.LayoutParams(arg1);
169         if (arg1 instanceof LayoutParams params) {
170             frameLayoutParams.gravity = params.gravity;
171             if ((params.flags & LayoutParams.FLAG_DIM_BEHIND) != 0) {
172                 mCurrentRootView.setBackgroundColor(Color.argb(params.dimAmount, 0, 0, 0));
173             } else {
174                 int backgroundColor = Color.WHITE;
175                 Drawable background = mBaseRootView.getBackground();
176                 if (background == null) {
177                     background = mBaseRootView.getRootView().getBackground();
178                 }
179                 if (background instanceof ColorDrawable) {
180                     backgroundColor = ((ColorDrawable) background).getColor();
181                 }
182                 mCurrentRootView.setBackgroundColor(backgroundColor);
183             }
184         }
185         mCurrentRootView.addView(arg0, frameLayoutParams);
186         ViewRootImpl_Accessor.setChild(mBaseRootView.getViewRootImpl(), arg0);
187     }
188 
189     @Override
removeView(View arg0)190     public void removeView(View arg0) {
191         ViewRootImpl viewRootImpl = arg0.getViewRootImpl();
192         if (mCurrentRootView != null) {
193             mCurrentRootView.removeView(arg0);
194             if (mBaseRootView != null && mCurrentRootView.getChildCount() == 0) {
195                 mBaseRootView.removeView(mCurrentRootView);
196                 mCurrentRootView = null;
197             }
198         }
199         if (viewRootImpl != null && viewRootImpl.getView() == arg0) {
200             View newRoot = null;
201             if (mCurrentRootView != null && mCurrentRootView.getChildCount() > 0) {
202                 ArrayList<View> childrenList = mCurrentRootView.buildOrderedChildList();
203                 newRoot = childrenList.get(childrenList.size() - 1);
204             } else if (mBaseRootView != null) {
205                 View root = mBaseRootView;
206                 while (root.getParent() instanceof View) {
207                     root = (View)root.getParent();
208                 }
209                 newRoot = root;
210             }
211             ViewRootImpl_Accessor.setChild(viewRootImpl, newRoot);
212         }
213     }
214 
215     @Override
updateViewLayout(View view, android.view.ViewGroup.LayoutParams params)216     public void updateViewLayout(View view, android.view.ViewGroup.LayoutParams params) {
217         if (view == null) {
218             throw new IllegalArgumentException("view must not be null");
219         }
220         if (!(params instanceof LayoutParams wparams)) {
221             throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
222         }
223 
224         FrameLayout.LayoutParams lparams = new FrameLayout.LayoutParams(params);
225         lparams.gravity = wparams.gravity;
226         view.setLayoutParams(lparams);
227         if (mCurrentRootView != null) {
228             Rect bounds = new Rect();
229             mBaseRootView.getBoundsOnScreen(bounds);
230             mCurrentRootView.setX(wparams.x - bounds.left);
231             mCurrentRootView.setY(wparams.y - bounds.top);
232             mCurrentRootView.setElevation(view.getElevation());
233         }
234     }
235 
236 
237     @Override
removeViewImmediate(View arg0)238     public void removeViewImmediate(View arg0) {
239         removeView(arg0);
240     }
241 
242     @Override
getApplicationLaunchKeyboardShortcuts(int deviceId)243     public KeyboardShortcutGroup getApplicationLaunchKeyboardShortcuts(int deviceId) {
244         return new KeyboardShortcutGroup("", new ArrayList<>());
245     }
246 
247     @Override
requestAppKeyboardShortcuts( KeyboardShortcutsReceiver receiver, int deviceId)248     public void requestAppKeyboardShortcuts(
249             KeyboardShortcutsReceiver receiver, int deviceId) {
250     }
251 
252     @Override
getCurrentImeTouchRegion()253     public Region getCurrentImeTouchRegion() {
254         return null;
255     }
256 
257     @Override
setShouldShowWithInsecureKeyguard(int displayId, boolean shouldShow)258     public void setShouldShowWithInsecureKeyguard(int displayId, boolean shouldShow) {
259         // pass
260     }
261 
262     @Override
setShouldShowSystemDecors(int displayId, boolean shouldShow)263     public void setShouldShowSystemDecors(int displayId, boolean shouldShow) {
264         // pass
265     }
266 
267     @Override
setDisplayImePolicy(int displayId, int imePolicy)268     public void setDisplayImePolicy(int displayId, int imePolicy) {
269         // pass
270     }
271 
272     @Override
getCurrentWindowMetrics()273     public WindowMetrics getCurrentWindowMetrics() {
274         final Rect bound = getCurrentBounds(mContext);
275 
276         return new WindowMetrics(bound, computeWindowInsets());
277     }
278 
279     @Override
getMaximumWindowMetrics()280     public WindowMetrics getMaximumWindowMetrics() {
281         return new WindowMetrics(getMaximumBounds(), computeWindowInsets());
282     }
283 
getMaximumBounds()284     private Rect getMaximumBounds() {
285         final Point displaySize = new Point();
286         getDefaultDisplay().getRealSize(displaySize);
287         return new Rect(0, 0, displaySize.x, displaySize.y);
288     }
289 
computeWindowInsets()290     private WindowInsets computeWindowInsets() {
291         if (mBaseRootView == null) {
292             return null;
293         }
294         return mBaseRootView.getViewRootImpl().getWindowInsets(true);
295     }
296 
297     // ---- Extra methods for layoutlib ----
298 
setBaseRootView(ViewGroup baseRootView)299     public void setBaseRootView(ViewGroup baseRootView) {
300         // If used within Compose Preview, use the ComposeViewAdapter as the root
301         // so that the preview attributes are handled correctly.
302         ViewGroup composableRoot = findComposableRoot(baseRootView);
303         mBaseRootView = composableRoot != null ? composableRoot : baseRootView;
304     }
305 
findComposableRoot(ViewGroup baseRootView)306     private ViewGroup findComposableRoot(ViewGroup baseRootView) {
307         if (baseRootView.getClass().getName().endsWith("ComposeViewAdapter")) {
308             return baseRootView;
309         }
310         for (int i = 0; i < baseRootView.getChildCount(); i++) {
311             View child = baseRootView.getChildAt(i);
312             if (child instanceof ViewGroup) {
313                 ViewGroup composableRoot = findComposableRoot((ViewGroup)child);
314                 if (composableRoot != null) {
315                     return composableRoot;
316                 }
317             }
318         }
319         return null;
320     }
321 
getCurrentRootView()322     public ViewGroup getCurrentRootView() {
323         return mCurrentRootView;
324     }
325 
createOrUpdateDisplayFrames(InsetsState insetsState)326     public void createOrUpdateDisplayFrames(InsetsState insetsState) {
327         if (mDisplayFrames == null) {
328             mDisplayFrames = new DisplayFrames(insetsState, mDisplayInfo,
329                     mDisplayInfo.displayCutout, RoundedCorners.NO_ROUNDED_CORNERS,
330                     sPrivacyIndicatorBounds, DisplayShape.NONE);
331         } else {
332             mDisplayFrames.update(mDisplayInfo.rotation, mDisplayInfo.logicalWidth,
333                     mDisplayInfo.logicalHeight, mDisplayInfo.displayCutout,
334                     RoundedCorners.NO_ROUNDED_CORNERS, sPrivacyIndicatorBounds, DisplayShape.NONE);
335         }
336     }
337 
setupDisplayCutout()338     public void setupDisplayCutout() {
339         DisplayCutout displayCutout =
340                 DisplayCutout.fromResourcesRectApproximation(mContext.getResources(), null,
341                         mMetrics.widthPixels, mMetrics.heightPixels, mMetrics.widthPixels,
342                         mMetrics.heightPixels);
343         if (displayCutout != null) {
344             mDisplayInfo.displayCutout = displayCutout.getRotated(mDisplayInfo.logicalWidth,
345                     mDisplayInfo.logicalHeight, mDisplayInfo.rotation,
346                     getDefaultDisplay().getRotation());
347         }
348     }
349 }
350