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