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());
     }
 
     /**