Control display of shadows for multi-window in DecorView
Allows us to display shadows without needing the NonClientDecorView.
For example, windows in pinned stack don't have a NonClientDecorView.
Bug: 25006507
Change-Id: Id573a30942a9bfcd002b86f0956d0b2a14ec2c2b
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 077cebc..4b88dc9 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -88,6 +88,18 @@
private static final boolean SWEEP_OPEN_MENU = false;
+ // The height of a window which has focus in DIP.
+ private final static int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20;
+ // The height of a window which has not in DIP.
+ private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
+
+ // Cludge to address b/22668382: Set the shadow size to the maximum so that the layer
+ // size calculation takes the shadow size into account. We set the elevation currently
+ // to max until the first layout command has been executed.
+ private boolean mAllowUpdateElevation = false;
+
+ private boolean mElevationAdjustedForStack = false;
+
int mDefaultOpacity = PixelFormat.OPAQUE;
/** The feature ID of the panel, or -1 if this is the application's DecorView */
@@ -351,7 +363,7 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
int action = event.getAction();
- if (mHasNonClientDecor && mNonClientDecorView.mVisible) {
+ if (mHasNonClientDecor && isShowingCaption()) {
// Don't dispatch ACTION_DOWN to the non client decor if the window is
// resizable and the event was (starting) outside the window.
// Window resizing events should be handled by WindowManager.
@@ -630,6 +642,11 @@
if (mOutsets.top > 0) {
offsetTopAndBottom(-mOutsets.top);
}
+
+ // If the application changed its SystemUI metrics, we might also have to adapt
+ // our shadow elevation.
+ updateElevation();
+ mAllowUpdateElevation = true;
}
@Override
@@ -1213,6 +1230,8 @@
if (mFloatingActionMode != null) {
mFloatingActionMode.onWindowFocusChanged(hasWindowFocus);
}
+
+ updateElevation();
}
@Override
@@ -1537,17 +1556,17 @@
}
void onConfigurationChanged() {
+ int workspaceId = getWorkspaceId();
if (mNonClientDecorView != null) {
- int workspaceId = getWorkspaceId();
if (mWorkspaceId != workspaceId) {
mWorkspaceId = workspaceId;
// We might have to change the kind of surface before we do anything else.
mNonClientDecorView.onConfigurationChanged(
- ActivityManager.StackId.hasWindowDecor(mWorkspaceId),
- ActivityManager.StackId.hasWindowShadow(mWorkspaceId));
+ ActivityManager.StackId.hasWindowDecor(mWorkspaceId));
enableNonClientDecor(ActivityManager.StackId.hasWindowDecor(workspaceId));
}
}
+ initializeElevation();
}
View onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
@@ -1576,6 +1595,7 @@
addView(root, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
+ initializeElevation();
return root;
}
@@ -1591,12 +1611,12 @@
}
}
final WindowManager.LayoutParams attrs = mWindow.getAttributes();
- boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
+ final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
attrs.type == TYPE_APPLICATION;
// Only a non floating application window on one of the allowed workspaces can get a non
// client decor.
- final boolean hasNonClientDecor = ActivityManager.StackId.hasWindowDecor(mWorkspaceId);
- if (!mWindow.isFloating() && isApplication && hasNonClientDecor) {
+ if (!mWindow.isFloating() && isApplication
+ && ActivityManager.StackId.hasWindowDecor(mWorkspaceId)) {
// Dependent on the brightness of the used title we either use the
// dark or the light button frame.
if (nonClientDecorView == null) {
@@ -1612,15 +1632,13 @@
R.layout.non_client_decor_light, null);
}
}
- nonClientDecorView.setPhoneWindow(mWindow,
- ActivityManager.StackId.hasWindowDecor(mWorkspaceId),
- ActivityManager.StackId.hasWindowShadow(mWorkspaceId));
+ nonClientDecorView.setPhoneWindow(mWindow, true /*showDecor*/);
} else {
nonClientDecorView = null;
}
// Tell the decor if it has a visible non client decor.
- enableNonClientDecor(nonClientDecorView != null && hasNonClientDecor);
+ enableNonClientDecor(nonClientDecorView != null);
return nonClientDecorView;
}
@@ -1749,9 +1767,41 @@
}
}
+ /**
+ * The elevation gets set for the first time and the framework needs to be informed that
+ * the surface layer gets created with the shadow size in mind.
+ */
+ private void initializeElevation() {
+ // TODO(skuhne): Call setMaxElevation here accordingly after b/22668382 got fixed.
+ mAllowUpdateElevation = false;
+ updateElevation();
+ }
+
private void updateElevation() {
- if (mNonClientDecorView != null) {
- mNonClientDecorView.updateElevation();
+ float elevation = 0;
+ final boolean wasAdjustedForStack = mElevationAdjustedForStack;
+ // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
+ // since the shadow is bound to the content size and not the target size.
+ if (ActivityManager.StackId.hasWindowShadow(mWorkspaceId)
+ && mBackdropFrameRenderer == null) {
+ elevation = hasWindowFocus() ?
+ DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
+ // TODO(skuhne): Remove this if clause once b/22668382 got fixed.
+ if (!mAllowUpdateElevation) {
+ elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
+ }
+ // Convert the DP elevation into physical pixels.
+ elevation = dipToPx(elevation);
+ mElevationAdjustedForStack = true;
+ } else {
+ mElevationAdjustedForStack = false;
+ }
+
+ // Don't change the elevation if we didn't previously adjust it for the stack it was in
+ // or it didn't change.
+ if ((wasAdjustedForStack || mElevationAdjustedForStack)
+ && getElevation() != elevation) {
+ mWindow.setElevation(elevation);
}
}
@@ -1763,6 +1813,16 @@
return isShowingCaption() ? mNonClientDecorView.getDecorCaptionHeight() : 0;
}
+ /**
+ * Converts a DIP measure into physical pixels.
+ * @param dip The dip value.
+ * @return Returns the number of pixels.
+ */
+ private float dipToPx(float dip) {
+ return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
+ getResources().getDisplayMetrics());
+ }
+
private static class ColorViewState {
View view = null;
int targetVisibility = View.INVISIBLE;
diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java
index 33b8e05..1f01759 100644
--- a/core/java/com/android/internal/widget/NonClientDecorView.java
+++ b/core/java/com/android/internal/widget/NonClientDecorView.java
@@ -28,24 +28,18 @@
import android.view.ViewOutlineProvider;
import android.view.Window;
import android.util.Log;
-import android.util.TypedValue;
import com.android.internal.R;
-import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneWindow;
/**
* This class represents the special screen elements to control a window on free form
- * environment. All thse screen elements are added in the "non client area" which is the area of
+ * environment. All these screen elements are added in the "non client area" which is the area of
* the window which is handled by the OS and not the application.
* As such this class handles the following things:
* <ul>
* <li>The caption, containing the system buttons like maximize, close and such as well as
* allowing the user to drag the window around.</li>
- * <li>The shadow - which is changing dependent on the window focus.</li>
- * <li>The border around the client area (if there is one).</li>
- * <li>The resize handles which allow to resize the window.</li>
- * </ul>
* After creating the view, the function
* {@link #setPhoneWindow} needs to be called to make
* the connection to it's owning PhoneWindow.
@@ -62,12 +56,7 @@
public class NonClientDecorView extends LinearLayout
implements View.OnClickListener, View.OnTouchListener {
private final static String TAG = "NonClientDecorView";
- // The height of a window which has focus in DIP.
- private final int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20;
- // The height of a window which has not in DIP.
- private final int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
private PhoneWindow mOwner = null;
- private boolean mWindowHasShadow = false;
private boolean mShowDecor = false;
// True if the window is being dragged.
@@ -76,17 +65,6 @@
// True when the left mouse button got released while dragging.
private boolean mLeftMouseButtonReleased;
- // True if this window is resizable (which is currently only true when the decor is shown).
- public boolean mVisible = false;
-
- // The current focus state of the window for updating the window elevation.
- private boolean mWindowHasFocus = true;
-
- // Cludge to address b/22668382: Set the shadow size to the maximum so that the layer
- // size calculation takes the shadow size into account. We set the elevation currently
- // to max until the first layout command has been executed.
- private boolean mAllowUpdateElevation = false;
-
public NonClientDecorView(Context context) {
super(context);
}
@@ -99,14 +77,10 @@
super(context, attrs, defStyle);
}
- public void setPhoneWindow(PhoneWindow owner, boolean showDecor, boolean windowHasShadow) {
+ public void setPhoneWindow(PhoneWindow owner, boolean showDecor) {
mOwner = owner;
- mWindowHasShadow = windowHasShadow;
mShowDecor = showDecor;
updateCaptionVisibility();
- if (mWindowHasShadow) {
- initializeElevation();
- }
// By changing the outline provider to BOUNDS, the window can remove its
// background without removing the shadow.
mOwner.getDecorView().setOutlineProvider(ViewOutlineProvider.BOUNDS);
@@ -164,15 +138,10 @@
/**
* The phone window configuration has changed and the decor needs to be updated.
* @param showDecor True if the decor should be shown.
- * @param windowHasShadow True when the window should show a shadow.
- **/
- public void onConfigurationChanged(boolean showDecor, boolean windowHasShadow) {
+ */
+ public void onConfigurationChanged(boolean showDecor) {
mShowDecor = showDecor;
updateCaptionVisibility();
- if (windowHasShadow != mWindowHasShadow) {
- mWindowHasShadow = windowHasShadow;
- initializeElevation();
- }
}
@Override
@@ -185,23 +154,6 @@
}
@Override
- public void onWindowFocusChanged(boolean hasWindowFocus) {
- mWindowHasFocus = hasWindowFocus;
- updateElevation();
- super.onWindowFocusChanged(hasWindowFocus);
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- // If the application changed its SystemUI metrics, we might also have to adapt
- // our shadow elevation.
- updateElevation();
- mAllowUpdateElevation = true;
-
- super.onLayout(changed, left, top, right, bottom);
- }
-
- @Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
// Make sure that we never get more then one client area in our view.
if (index >= 2 || getChildCount() >= 2) {
@@ -229,60 +181,6 @@
View caption = getChildAt(0);
caption.setVisibility(invisible ? GONE : VISIBLE);
caption.setOnTouchListener(this);
- mVisible = !invisible;
- }
-
- /**
- * The elevation gets set for the first time and the framework needs to be informed that
- * the surface layer gets created with the shadow size in mind.
- **/
- private void initializeElevation() {
- // TODO(skuhne): Call setMaxElevation here accordingly after b/22668382 got fixed.
- mAllowUpdateElevation = false;
- if (mWindowHasShadow) {
- updateElevation();
- } else {
- mOwner.setElevation(0);
- }
- }
-
- /**
- * The shadow height gets controlled by the focus to visualize highlighted windows.
- * Note: This will overwrite application elevation properties.
- * Note: Windows which have (temporarily) changed their attributes to cover the SystemUI
- * will get no shadow as they are expected to be "full screen".
- **/
- public void updateElevation() {
- float elevation = 0;
- // Do not use a shadow when we are in resizing mode (mRenderer not null) since the shadow
- // is bound to the content size and not the target size.
- if (mWindowHasShadow
- && ((DecorView) mOwner.getDecorView()).mBackdropFrameRenderer == null) {
- boolean fill = isFillingScreen();
- elevation = fill ? 0 :
- (mWindowHasFocus ? DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP :
- DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP);
- // TODO(skuhne): Remove this if clause once b/22668382 got fixed.
- if (!mAllowUpdateElevation && !fill) {
- elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
- }
- // Convert the DP elevation into physical pixels.
- elevation = dipToPx(elevation);
- }
- // Don't change the elevation if it didn't change since it can require some time.
- if (mOwner.getDecorView().getElevation() != elevation) {
- mOwner.setElevation(elevation);
- }
- }
-
- /**
- * Converts a DIP measure into physical pixels.
- * @param dip The dip value.
- * @return Returns the number of pixels.
- */
- private float dipToPx(float dip) {
- return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
- getResources().getDisplayMetrics());
}
/**