Moved management of NonClientDecorView from PhoneWindow to DecorView

Bug: 24810450
Change-Id: I682afe1b15cb8ec1f98b38b88a499243d4c6c8a3
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 4f38ff3..49ceced 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -26,6 +26,7 @@
 import com.android.internal.widget.ActionBarContextView;
 import com.android.internal.widget.BackgroundFallback;
 import com.android.internal.widget.FloatingToolbar;
+import com.android.internal.widget.NonClientDecorView;
 
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
@@ -38,6 +39,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.os.RemoteException;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
@@ -46,6 +48,7 @@
 import android.view.Gravity;
 import android.view.InputQueue;
 import android.view.KeyEvent;
+import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.MotionEvent;
@@ -63,6 +66,8 @@
 import android.widget.FrameLayout;
 import android.widget.PopupWindow;
 
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.view.View.MeasureSpec.AT_MOST;
 import static android.view.View.MeasureSpec.EXACTLY;
 import static android.view.View.MeasureSpec.getMode;
@@ -72,6 +77,8 @@
 import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
 class DecorView extends FrameLayout implements RootViewSurfaceTaker {
     private static final String TAG = "DecorView";
@@ -151,6 +158,15 @@
     private Rect mTempRect;
     private Rect mOutsets = new Rect();
 
+    // This is the non client decor view for the window, containing the caption and window control
+    // buttons. The visibility of this decor depends on the workspace and the window type.
+    // If the window type does not require such a view, this member might be null.
+    NonClientDecorView mNonClientDecorView;
+
+    // The non client decor needs to adapt to the used workspace. Since querying and changing the
+    // workspace is expensive, this is the workspace value the window is currently set up for.
+    int mWorkspaceId;
+
     DecorView(Context context, int featureId, PhoneWindow window) {
         super(context);
         mFeatureId = featureId;
@@ -326,7 +342,7 @@
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
         int action = event.getAction();
-        if (mHasNonClientDecor && mWindow.mNonClientDecorView.mVisible) {
+        if (mHasNonClientDecor && mNonClientDecorView.mVisible) {
             // 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.
@@ -1483,16 +1499,156 @@
      * @return Returns true when the window has a shadow created by the non client decor.
      **/
     private boolean windowHasShadow() {
-        return windowHasNonClientDecor() && ActivityManager.StackId
-                .hasWindowShadow(mWindow.mWorkspaceId);
+        return windowHasNonClientDecor() && ActivityManager.StackId.hasWindowShadow(mWorkspaceId);
     }
 
     void setWindow(PhoneWindow phoneWindow) {
         mWindow = phoneWindow;
         Context context = getContext();
         if (context instanceof DecorContext) {
-            DecorContext decorContex = (DecorContext) context;
-            decorContex.setPhoneWindow(mWindow);
+            DecorContext decorContext = (DecorContext) context;
+            decorContext.setPhoneWindow(mWindow);
+        }
+    }
+
+    void onConfigurationChanged() {
+        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));
+                enableNonClientDecor(ActivityManager.StackId.hasWindowDecor(workspaceId));
+            }
+        }
+    }
+
+    View onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
+        mNonClientDecorView = createNonClientDecorView(inflater);
+        final View root = inflater.inflate(layoutResource, null);
+        if (mNonClientDecorView != null) {
+            if (mNonClientDecorView.getParent() == null) {
+                addView(mNonClientDecorView,
+                        new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+            }
+            mNonClientDecorView.addView(root,
+                    new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+        } else {
+            addView(root, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+        }
+        mContentRoot = (ViewGroup) root;
+        return root;
+    }
+
+    // Free floating overlapping windows require a non client decor with a caption and shadow..
+    private NonClientDecorView createNonClientDecorView(LayoutInflater inflater) {
+        NonClientDecorView nonClientDecorView = null;
+        for (int i = getChildCount() - 1; i >= 0 && nonClientDecorView == null; i--) {
+            View view = getChildAt(i);
+            if (view instanceof NonClientDecorView) {
+                // The decor was most likely saved from a relaunch - so reuse it.
+                nonClientDecorView = (NonClientDecorView) view;
+                removeViewAt(i);
+            }
+        }
+        final WindowManager.LayoutParams attrs = mWindow.getAttributes();
+        boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
+                attrs.type == TYPE_APPLICATION;
+        mWorkspaceId = getWorkspaceId();
+        // Only a non floating application window on one of the allowed workspaces can get a non
+        // client decor.
+        if (!mWindow.isFloating()
+                && isApplication
+                && ActivityManager.StackId.isStaticStack(mWorkspaceId)) {
+            // Dependent on the brightness of the used title we either use the
+            // dark or the light button frame.
+            if (nonClientDecorView == null) {
+                Context context = getContext();
+                TypedValue value = new TypedValue();
+                context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
+                inflater = inflater.from(context);
+                if (Color.luminance(value.data) < 0.5) {
+                    nonClientDecorView = (NonClientDecorView) inflater.inflate(
+                            R.layout.non_client_decor_dark, null);
+                } else {
+                    nonClientDecorView = (NonClientDecorView) inflater.inflate(
+                            R.layout.non_client_decor_light, null);
+                }
+            }
+            nonClientDecorView.setPhoneWindow(mWindow,
+                    ActivityManager.StackId.hasWindowDecor(mWorkspaceId),
+                    ActivityManager.StackId.hasWindowShadow(mWorkspaceId),
+                    getResizingBackgroundDrawable(),
+                    getContext().getDrawable(R.drawable.non_client_decor_title_focused));
+        }
+        // Tell the decor if it has a visible non client decor.
+        enableNonClientDecor(
+                nonClientDecorView != null && ActivityManager.StackId.hasWindowDecor(mWorkspaceId));
+
+        return nonClientDecorView;
+    }
+
+    /**
+     * Returns the color used to fill areas the app has not rendered content to yet when the user
+     * is resizing the window of an activity in multi-window mode.
+     * */
+    private Drawable getResizingBackgroundDrawable() {
+        final Context context = getContext();
+
+        if (mWindow.mBackgroundResource != 0) {
+            final Drawable drawable = context.getDrawable(mWindow.mBackgroundResource);
+            if (drawable != null) {
+                return drawable;
+            }
+        }
+
+        if (mWindow.mBackgroundFallbackResource != 0) {
+            final Drawable fallbackDrawable =
+                    context.getDrawable(mWindow.mBackgroundFallbackResource);
+            if (fallbackDrawable != null) {
+                return fallbackDrawable;
+            }
+        }
+
+        // We shouldn't really get here as the background fallback should be always available since
+        // it is defaulted by the system.
+        Log.w(TAG, "Failed to find background drawable for PhoneWindow=" + mWindow);
+        return null;
+    }
+
+    /**
+     * Returns the Id of the workspace which contains this window.
+     * Note that if no workspace can be determined - which usually means that it was not
+     * created for an activity - the fullscreen workspace ID will be returned.
+     * @return Returns the workspace stack id which contains this window.
+     **/
+    private int getWorkspaceId() {
+        int workspaceId = INVALID_STACK_ID;
+        final Window.WindowControllerCallback callback = mWindow.getWindowControllerCallback();
+        if (callback != null) {
+            try {
+                workspaceId = callback.getWindowStackId();
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Failed to get the workspace ID of a PhoneWindow.");
+            }
+        }
+        if (workspaceId == INVALID_STACK_ID) {
+            return FULLSCREEN_WORKSPACE_STACK_ID;
+        }
+        return workspaceId;
+    }
+
+    void clearContentView() {
+        if (mNonClientDecorView != null) {
+            if (mNonClientDecorView.getChildCount() > 1) {
+                mNonClientDecorView.removeViewAt(1);
+            }
+        } else {
+            // This window doesn't have non client decor, so we need to just remove the
+            // children of the decor view.
+            removeAllViews();
         }
     }
 
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index c427149..6e7e5cf 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -16,13 +16,10 @@
 
 package com.android.internal.policy;
 
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import static android.view.WindowManager.LayoutParams.*;
 
-import android.app.ActivityManager.StackId;
 import android.app.ActivityManagerNative;
 import android.app.SearchManager;
 import android.os.UserHandle;
@@ -60,7 +57,6 @@
 import com.android.internal.view.menu.MenuPresenter;
 import com.android.internal.view.menu.MenuView;
 import com.android.internal.widget.DecorContentParent;
-import com.android.internal.widget.NonClientDecorView;
 import com.android.internal.widget.SwipeDismissLayout;
 
 import android.app.ActivityManager;
@@ -72,7 +68,6 @@
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
 import android.graphics.Color;
-import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
 import android.media.session.MediaController;
@@ -149,15 +144,6 @@
     // view is requested, so we need to force the recreating without introducing an infinite loop.
     private boolean mForceDecorInstall = false;
 
-    // This is the non client decor view for the window, containing the caption and window control
-    // buttons. The visibility of this decor depends on the workspace and the window type.
-    // If the window type does not require such a view, this member might be null.
-    NonClientDecorView mNonClientDecorView;
-
-    // The non client decor needs to adapt to the used workspace. Since querying and changing the
-    // workspace is expensive, this is the workspace value the window is currently set up for.
-    int mWorkspaceId;
-
     // This is the view in which the window contents are placed. It is either
     // mDecor itself, or a child of mDecor where the contents go.
     ViewGroup mContentParent;
@@ -220,12 +206,12 @@
 
     private ProgressBar mHorizontalProgressBar;
 
-    private int mBackgroundResource = 0;
-    private int mBackgroundFallbackResource = 0;
+    int mBackgroundResource = 0;
+    int mBackgroundFallbackResource = 0;
 
     private Drawable mBackgroundDrawable;
 
-    private boolean mLoadEleveation = true;
+    private boolean mLoadElevation = true;
     private float mElevation;
 
     /** Whether window content should be clipped to the background outline. */
@@ -305,7 +291,7 @@
         if (preservedWindow != null) {
             mDecor = (DecorView) preservedWindow.getDecorView();
             mElevation = preservedWindow.getElevation();
-            mLoadEleveation = false;
+            mLoadElevation = false;
             mForceDecorInstall = true;
             // If we're preserving window, carry over the app token from the preserved
             // window, as we'll be skipping the addView in handleResumeActivity(), and
@@ -478,15 +464,10 @@
         }
     }
 
+    @Override
     public void clearContentView() {
-        if (mNonClientDecorView != null) {
-            if (mNonClientDecorView.getChildCount() > 1) {
-                mNonClientDecorView.removeViewAt(1);
-            }
-        } else {
-            // This window doesn't have non client decor, so we need to just remove the children
-            // of the decor view.
-            mDecor.removeAllViews();
+        if (mDecor != null) {
+            mDecor.clearContentView();
         }
     }
 
@@ -700,15 +681,8 @@
                 }
             }
         }
-        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.phoneWindowUpdated(StackId.hasWindowDecor(mWorkspaceId),
-                        StackId.hasWindowShadow(mWorkspaceId));
-                mDecor.enableNonClientDecor(StackId.hasWindowDecor(workspaceId));
-            }
+        if (mDecor != null) {
+            mDecor.onConfigurationChanged();
         }
     }
 
@@ -2511,7 +2485,7 @@
                             + Integer.toHexString(mFrameResource));
                 }
             }
-            if (mLoadEleveation) {
+            if (mLoadElevation) {
                 mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);
             }
             mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);
@@ -2581,19 +2555,7 @@
         }
 
         mDecor.startChanging();
-
-        mNonClientDecorView = createNonClientDecorView();
-        View in = mLayoutInflater.inflate(layoutResource, null);
-        if (mNonClientDecorView != null) {
-            if (mNonClientDecorView.getParent() == null) {
-                decor.addView(mNonClientDecorView,
-                        new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
-            }
-            mNonClientDecorView.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
-        } else {
-            decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
-        }
-        decor.mContentRoot = (ViewGroup) in;
+        final View in = mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
 
         ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
         if (contentParent == null) {
@@ -2648,50 +2610,6 @@
         return contentParent;
     }
 
-    // Free floating overlapping windows require a non client decor with a caption and shadow..
-    private NonClientDecorView createNonClientDecorView() {
-        NonClientDecorView nonClientDecorView = null;
-        for (int i = mDecor.getChildCount() - 1; i >= 0 && nonClientDecorView == null; i--) {
-            View view = mDecor.getChildAt(i);
-            if (view instanceof NonClientDecorView) {
-                // The decor was most likely saved from a relaunch - so reuse it.
-                nonClientDecorView = (NonClientDecorView) view;
-                mDecor.removeViewAt(i);
-            }
-        }
-        final WindowManager.LayoutParams attrs = getAttributes();
-        boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
-                attrs.type == TYPE_APPLICATION;
-        mWorkspaceId = getWorkspaceId();
-        // Only a non floating application window on one of the allowed workspaces can get a non
-        // client decor.
-        if (!isFloating() && isApplication && StackId.isStaticStack(mWorkspaceId)) {
-            // Dependent on the brightness of the used title we either use the
-            // dark or the light button frame.
-            if (nonClientDecorView == null) {
-                Context context = mDecor.getContext();
-                TypedValue value = new TypedValue();
-                context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
-                LayoutInflater inflater = mLayoutInflater.from(context);
-                if (Color.luminance(value.data) < 0.5) {
-                    nonClientDecorView = (NonClientDecorView) inflater.inflate(
-                            R.layout.non_client_decor_dark, null);
-                } else {
-                    nonClientDecorView = (NonClientDecorView) inflater.inflate(
-                            R.layout.non_client_decor_light, null);
-                }
-            }
-            nonClientDecorView.setPhoneWindow(this, StackId.hasWindowDecor(mWorkspaceId),
-                    StackId.hasWindowShadow(mWorkspaceId), getResizingBackgroundDrawable(),
-                    mDecor.getContext().getDrawable(R.drawable.non_client_decor_title_focused));
-        }
-        // Tell the decor if it has a visible non client decor.
-        mDecor.enableNonClientDecor(
-                nonClientDecorView != null&& StackId.hasWindowDecor(mWorkspaceId));
-
-        return nonClientDecorView;
-    }
-
     /** @hide */
     public void alwaysReadCloseOnTouchAttr() {
         mAlwaysReadCloseOnTouchAttr = true;
@@ -3819,28 +3737,6 @@
         mIsStartingWindow = isStartingWindow;
     }
 
-    /**
-     * Returns the Id of the workspace which contains this window.
-     * Note that if no workspace can be determined - which usually means that it was not
-     * created for an activity - the fullscreen workspace ID will be returned.
-     * @return Returns the workspace stack id which contains this window.
-     **/
-    private int getWorkspaceId() {
-        int workspaceId = INVALID_STACK_ID;
-        WindowControllerCallback callback = getWindowControllerCallback();
-        if (callback != null) {
-            try {
-                workspaceId = callback.getWindowStackId();
-            } catch (RemoteException ex) {
-                Log.e(TAG, "Failed to get the workspace ID of a PhoneWindow.");
-            }
-        }
-        if (workspaceId == INVALID_STACK_ID) {
-            return FULLSCREEN_WORKSPACE_STACK_ID;
-        }
-        return workspaceId;
-    }
-
     @Override
     public void setTheme(int resid) {
         mTheme = resid;
@@ -3851,31 +3747,4 @@
             }
         }
     }
-
-    /**
-     * Returns the color used to fill areas the app has not rendered content to yet when the user
-     * is resizing the window of an activity in multi-window mode.
-     * */
-    private Drawable getResizingBackgroundDrawable() {
-        final Context context = mDecor.getContext();
-
-        if (mBackgroundResource != 0) {
-            final Drawable drawable = context.getDrawable(mBackgroundResource);
-            if (drawable != null) {
-                return drawable;
-            }
-        }
-
-        if (mBackgroundFallbackResource != 0) {
-            final Drawable fallbackDrawable = context.getDrawable(mBackgroundFallbackResource);
-            if (fallbackDrawable != null) {
-                return fallbackDrawable;
-            }
-        }
-
-        // We shouldn't really get here as the background fallback should be always available since
-        // it is defaulted by the system.
-        Log.w(TAG, "Failed to find background drawable for PhoneWindow=" + this);
-        return null;
-    }
 }
diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java
index de6e228..6ae1b98 100644
--- a/core/java/com/android/internal/widget/NonClientDecorView.java
+++ b/core/java/com/android/internal/widget/NonClientDecorView.java
@@ -208,7 +208,7 @@
      * @param showDecor True if the decor should be shown.
      * @param windowHasShadow True when the window should show a shadow.
      **/
-    public void phoneWindowUpdated(boolean showDecor, boolean windowHasShadow) {
+    public void onConfigurationChanged(boolean showDecor, boolean windowHasShadow) {
         mShowDecor = showDecor;
         updateCaptionVisibility();
         if (windowHasShadow != mWindowHasShadow) {