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