blob: 7c9f1f7f4db0b00c2db5c8fdca541e57e19ea3be [file] [log] [blame]
/*
* Copyright (C) 2020 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.View.SYSTEM_UI_FLAG_VISIBLE;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import android.app.ResourcesManager;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.view.Display.Mode;
import android.widget.FrameLayout;
import com.android.ide.common.rendering.api.ILayoutLog;
import com.android.internal.R;
import com.android.layoutlib.bridge.Bridge;
public class WindowManagerImpl implements WindowManager {
private final Context mContext;
private final DisplayMetrics mMetrics;
private final Display mDisplay;
/**
* Root view of the base window, new windows will be added on top of this.
*/
private ViewGroup mBaseRootView;
/**
* Root view of the current window at the top of the display,
* null if there is only the base window present.
*/
private ViewGroup mCurrentRootView;
public WindowManagerImpl(Context context, DisplayMetrics metrics) {
mContext = context;
mMetrics = metrics;
DisplayInfo info = new DisplayInfo();
info.logicalHeight = mMetrics.heightPixels;
info.logicalWidth = mMetrics.widthPixels;
info.supportedModes = new Mode[] {
new Mode(0, mMetrics.widthPixels, mMetrics.heightPixels, 60f)
};
info.logicalDensityDpi = mMetrics.densityDpi;
mDisplay = new Display(null, Display.DEFAULT_DISPLAY, info,
DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
}
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return this;
}
public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
"The preview does not fully support multiple windows.",
null, null, null);
return this;
}
/**
* Sets the window token to assign when none is specified by the client or
* available from the parent window.
*
* @param token The default token to assign.
*/
public void setDefaultToken(IBinder token) {
}
@Override
public Display getDefaultDisplay() {
return mDisplay;
}
@Override
public void addView(View arg0, android.view.ViewGroup.LayoutParams arg1) {
if (mBaseRootView == null) {
return;
}
if (mCurrentRootView == null) {
FrameLayout layout = new FrameLayout(mContext) {
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
float offsetX = - getX();
float offsetY = - getY();
View baseRootParent = (View)mBaseRootView.getParent();
if (baseRootParent != null) {
offsetX -= baseRootParent.getX();
offsetY -= baseRootParent.getY();
}
ev.offsetLocation(offsetX, offsetY);
return super.dispatchTouchEvent(ev);
}
@Override
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec,
int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
// This reproduces ViewRootImpl#measureHierarchy as this FrameLayout should
// be treated as a ViewRoot.
ViewGroup.LayoutParams lp = child.getLayoutParams();
int parentWidth = MeasureSpec.getSize(parentWidthMeasureSpec);
int parentHeight = MeasureSpec.getSize(parentHeightMeasureSpec);
int childWidthMeasureSpec = 0;
int childHeightMeasureSpec = ViewRootImpl.getRootMeasureSpec(parentHeight,
lp.height, 0);
if (lp.width == WRAP_CONTENT) {
int baseSize =
mContext.getResources().getDimensionPixelSize(R.dimen.config_prefDialogWidth);
if (baseSize != 0 && baseSize < parentWidth) {
childWidthMeasureSpec = ViewRootImpl.getRootMeasureSpec(baseSize,
lp.width, 0);
}
}
if (childWidthMeasureSpec == 0) {
childWidthMeasureSpec = ViewRootImpl.getRootMeasureSpec(parentWidth,
lp.width, 0);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
};
// The window root view should not handle touch events.
// Events need to be dispatched to the base view inside the window,
// with coordinates shifted accordingly.
layout.setOnTouchListener((v, event) -> {
event.offsetLocation(-arg0.getX(), -arg0.getY());
return arg0.dispatchTouchEvent(event);
});
mBaseRootView.addView(layout, new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
mCurrentRootView = layout;
}
FrameLayout.LayoutParams frameLayoutParams = new FrameLayout.LayoutParams(arg1);
if (arg1 instanceof WindowManager.LayoutParams) {
LayoutParams params = (LayoutParams) arg1;
frameLayoutParams.gravity = params.gravity;
if ((params.flags & LayoutParams.FLAG_DIM_BEHIND) != 0) {
mCurrentRootView.setBackgroundColor(Color.argb(params.dimAmount, 0, 0, 0));
} else {
int backgroundColor = Color.WHITE;
Drawable background = mBaseRootView.getBackground();
if (background == null) {
background = mBaseRootView.getRootView().getBackground();
}
if (background instanceof ColorDrawable) {
backgroundColor = ((ColorDrawable) background).getColor();
}
mCurrentRootView.setBackgroundColor(backgroundColor);
}
}
mCurrentRootView.addView(arg0, frameLayoutParams);
}
@Override
public void removeView(View arg0) {
if (mCurrentRootView != null) {
mCurrentRootView.removeView(arg0);
if (mBaseRootView != null && mCurrentRootView.getChildCount() == 0) {
mBaseRootView.removeView(mCurrentRootView);
mCurrentRootView = null;
}
}
}
@Override
public void updateViewLayout(View view, android.view.ViewGroup.LayoutParams params) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
FrameLayout.LayoutParams lparams = new FrameLayout.LayoutParams(params);
view.setLayoutParams(lparams);
if (mCurrentRootView != null) {
Rect bounds = new Rect();
mBaseRootView.getBoundsOnScreen(bounds);
mCurrentRootView.setX(wparams.x - bounds.left);
mCurrentRootView.setY(wparams.y - bounds.top);
mCurrentRootView.setLayoutParams(lparams);
mCurrentRootView.setElevation(view.getElevation());
}
}
@Override
public void removeViewImmediate(View arg0) {
removeView(arg0);
}
@Override
public void requestAppKeyboardShortcuts(
KeyboardShortcutsReceiver receiver, int deviceId) {
}
@Override
public Region getCurrentImeTouchRegion() {
return null;
}
@Override
public void setShouldShowWithInsecureKeyguard(int displayId, boolean shouldShow) {
// pass
}
@Override
public void setShouldShowSystemDecors(int displayId, boolean shouldShow) {
// pass
}
@Override
public void setDisplayImePolicy(int displayId, int imePolicy) {
// pass
}
@Override
public WindowMetrics getCurrentWindowMetrics() {
final Rect bound = getCurrentBounds(mContext);
return new WindowMetrics(bound, computeWindowInsets());
}
private static Rect getCurrentBounds(Context context) {
synchronized (ResourcesManager.getInstance()) {
return context.getResources().getConfiguration().windowConfiguration.getBounds();
}
}
@Override
public WindowMetrics getMaximumWindowMetrics() {
return new WindowMetrics(getMaximumBounds(), computeWindowInsets());
}
private Rect getMaximumBounds() {
final Point displaySize = new Point();
mDisplay.getRealSize(displaySize);
return new Rect(0, 0, displaySize.x, displaySize.y);
}
private WindowInsets computeWindowInsets() {
try {
final InsetsState insetsState = new InsetsState();
final boolean alwaysConsumeSystemBars =
WindowManagerGlobal.getWindowManagerService().getWindowInsets(
new WindowManager.LayoutParams(), mContext.getDisplayId(), insetsState);
final Configuration config = mContext.getResources().getConfiguration();
final boolean isScreenRound = config.isScreenRound();
final int windowingMode = config.windowConfiguration.getWindowingMode();
return insetsState.calculateInsets(getCurrentBounds(mContext),
null /* ignoringVisibilityState*/, isScreenRound, alwaysConsumeSystemBars,
SOFT_INPUT_ADJUST_NOTHING, 0, SYSTEM_UI_FLAG_VISIBLE, TYPE_APPLICATION,
windowingMode, null /* typeSideMap */);
} catch (RemoteException ignore) {
}
return null;
}
// ---- Extra methods for layoutlib ----
public void setBaseRootView(ViewGroup baseRootView) {
// If used within Compose Preview, use the ComposeViewAdapter as the root
// so that the preview attributes are handled correctly.
ViewGroup composableRoot = findComposableRoot(baseRootView);
mBaseRootView = composableRoot != null ? composableRoot : baseRootView;
}
private ViewGroup findComposableRoot(ViewGroup baseRootView) {
if (baseRootView.getClass().getName().endsWith("ComposeViewAdapter")) {
return baseRootView;
}
for (int i = 0; i < baseRootView.getChildCount(); i++) {
View child = baseRootView.getChildAt(i);
if (child instanceof ViewGroup) {
ViewGroup composableRoot = findComposableRoot((ViewGroup)child);
if (composableRoot != null) {
return composableRoot;
}
}
}
return null;
}
public ViewGroup getCurrentRootView() {
return mCurrentRootView;
}
}