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