Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0d7e912b authored by Wale Ogunwale's avatar Wale Ogunwale
Browse files

Moved management of NonClientDecorView from PhoneWindow to DecorView

Bug: 24810450
Change-Id: I682afe1b15cb8ec1f98b38b88a499243d4c6c8a3
parent 8804af2b
Loading
Loading
Loading
Loading
+161 −5
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import com.android.internal.view.menu.MenuPopupHelper;
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.PixelFormat;
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.ContextThemeWrapper;
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.view.animation.Interpolator;
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_DRAWS_SYSTEM_BAR_BACK
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 @@ class DecorView extends FrameLayout implements RootViewSurfaceTaker {
    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 @@ class DecorView extends FrameLayout implements RootViewSurfaceTaker {
    @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 @@ class DecorView extends FrameLayout implements RootViewSurfaceTaker {
     * @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();
        }
    }

+11 −142
Original line number Diff line number Diff line
@@ -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.MenuPopupHelper;
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.Configuration;
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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
    // 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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {

    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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
        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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
        }
    }

    @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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
                }
            }
        }
        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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
                            + 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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
        }

        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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
        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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
        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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
            }
        }
    }

    /**
     * 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;
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -208,7 +208,7 @@ public class NonClientDecorView extends LinearLayout
     * @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) {