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) {