diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java index 1eeb36310086d..b101733a1a14a 100644 --- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java +++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java @@ -16,8 +16,6 @@ package com.android.internal.policy; -import com.android.internal.widget.NonClientDecorView; - import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Looper; @@ -37,7 +35,7 @@ import android.view.View; */ public class BackdropFrameRenderer extends Thread implements Choreographer.FrameCallback { - private NonClientDecorView mNonClientDecorView; + private DecorView mDecorView; // This is containing the last requested size by a resize command. Note that this size might // or might not have been applied to the output already. @@ -62,12 +60,15 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame // Whether to report when next frame is drawn or not. private boolean mReportNextDraw; - public BackdropFrameRenderer(NonClientDecorView nonClientDecorView, - ThreadedRenderer renderer, - Rect initialBounds) { - mNonClientDecorView = nonClientDecorView; + private Drawable mCaptionBackgroundDrawable; + private Drawable mResizingBackgroundDrawable; + + public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds, + Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable) { setName("ResizeFrame"); + mRenderer = renderer; + onResourcesLoaded(decorView, resizingBackgroundDrawable, captionBackgroundDrawable); // Create a render node for the content and frame backdrop // which can be resized independently from the content. @@ -85,6 +86,13 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame start(); } + void onResourcesLoaded(DecorView decorView, Drawable resizingBackgroundDrawable, + Drawable captionBackgroundDrawableDrawable) { + mDecorView = decorView; + mResizingBackgroundDrawable = resizingBackgroundDrawable; + mCaptionBackgroundDrawable = captionBackgroundDrawableDrawable; + } + /** * Call this function asynchronously when the window size has been changed. The change will * be picked up once per frame and the frame will be re-rendered accordingly. @@ -201,7 +209,8 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame mLastYOffset + mLastCaptionHeight + mLastContentHeight); // If this was the first call and changeWindowSizeLocked got already called prior // to us, we should re-issue a changeWindowSizeLocked now. - return firstCall && (mLastCaptionHeight != 0 || !mNonClientDecorView.mShowDecor); + return firstCall + && (mLastCaptionHeight != 0 || !mDecorView.isShowingCaption()); } } @@ -218,25 +227,25 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame * @param newBounds The window bounds which needs to be drawn. */ private void changeWindowSizeLocked(Rect newBounds) { + // While a configuration change is taking place the view hierarchy might become // inaccessible. For that case we remember the previous metrics to avoid flashes. // Note that even when there is no visible caption, the caption child will exist. - View caption = mNonClientDecorView.getChildAt(0); - if (caption != null) { - final int captionHeight = caption.getHeight(); - // The caption height will probably never dynamically change while we are resizing. - // Once set to something other then 0 it should be kept that way. - if (captionHeight != 0) { - // Remember the height of the caption. - mLastCaptionHeight = captionHeight; - } + final int captionHeight = mDecorView.getCaptionHeight(); + // The caption height will probably never dynamically change while we are resizing. + // Once set to something other then 0 it should be kept that way. + if (captionHeight != 0) { + // Remember the height of the caption. + mLastCaptionHeight = captionHeight; } + // Make sure that the other thread has already prepared the render draw calls for the // content. If any size is 0, we have to wait for it to be drawn first. - if ((mLastCaptionHeight == 0 && mNonClientDecorView.mShowDecor) || + if ((mLastCaptionHeight == 0 && mDecorView.isShowingCaption()) || mLastContentWidth == 0 || mLastContentHeight == 0) { return; } + // Since the surface is spanning the entire screen, we have to add the start offset of // the bounds to get to the surface location. final int left = mLastXOffset + newBounds.left; @@ -247,15 +256,13 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame mFrameAndBackdropNode.setLeftTopRightBottom(left, top, left + width, top + height); // Draw the caption and content backdrops in to our render node. - DisplayListCanvas canvas = mFrameAndBackdropNode.start(width, height); - mNonClientDecorView.mCaptionBackgroundDrawable.setBounds( - 0, 0, left + width, top + mLastCaptionHeight); - mNonClientDecorView.mCaptionBackgroundDrawable.draw(canvas); + final DisplayListCanvas canvas = mFrameAndBackdropNode.start(width, height); + mCaptionBackgroundDrawable.setBounds(0, 0, left + width, top + mLastCaptionHeight); + mCaptionBackgroundDrawable.draw(canvas); // The backdrop: clear everything with the background. Clipping is done elsewhere. - mNonClientDecorView.mResizingBackgroundDrawable.setBounds( - 0, mLastCaptionHeight, left + width, top + height); - mNonClientDecorView.mResizingBackgroundDrawable.draw(canvas); + mResizingBackgroundDrawable.setBounds(0, mLastCaptionHeight, left + width, top + height); + mResizingBackgroundDrawable.draw(canvas); mFrameAndBackdropNode.end(canvas); // We need to render the node explicitly @@ -264,13 +271,11 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame reportDrawIfNeeded(); } - /** - * Notify view root that a frame has been drawn by us, if it has requested so. - */ + /** Notify view root that a frame has been drawn by us, if it has requested so. */ private void reportDrawIfNeeded() { if (mReportNextDraw) { - if (mNonClientDecorView.isAttachedToWindow()) { - mNonClientDecorView.getViewRootImpl().reportDrawFinish(); + if (mDecorView.isAttachedToWindow()) { + mDecorView.getViewRootImpl().reportDrawFinish(); } mReportNextDraw = false; } diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 49ceced379cc0..077cebc4ebf93 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -52,11 +52,13 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; +import android.view.ThreadedRenderer; import android.view.View; import android.view.ViewGroup; import android.view.ViewStub; import android.view.ViewTreeObserver; import android.view.Window; +import android.view.WindowCallbacks; import android.view.WindowInsets; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; @@ -80,7 +82,8 @@ 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 { +/** @hide */ +public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks { private static final String TAG = "DecorView"; private static final boolean SWEEP_OPEN_MENU = false; @@ -167,6 +170,12 @@ class DecorView extends FrameLayout implements RootViewSurfaceTaker { // workspace is expensive, this is the workspace value the window is currently set up for. int mWorkspaceId; + private boolean mWindowResizeCallbacksAdded = false; + + public BackdropFrameRenderer mBackdropFrameRenderer = null; + private Drawable mResizingBackgroundDrawable; + private Drawable mCaptionBackgroundDrawable; + DecorView(Context context, int featureId, PhoneWindow window) { super(context); mFeatureId = featureId; @@ -1225,6 +1234,17 @@ class DecorView extends FrameLayout implements RootViewSurfaceTaker { */ mWindow.openPanelsAfterRestore(); } + + if (!mWindowResizeCallbacksAdded) { + // If there is no window callback installed there was no window set before. Set it now. + // Note that our ViewRootImpl object will not change. + getViewRootImpl().addWindowCallbacks(this); + mWindowResizeCallbacksAdded = true; + } else if (mBackdropFrameRenderer != null) { + // We are resizing and this call happened due to a configuration change. Tell the + // renderer about it. + mBackdropFrameRenderer.onConfigurationChange(); + } } @Override @@ -1256,6 +1276,11 @@ class DecorView extends FrameLayout implements RootViewSurfaceTaker { if (st != null && st.menu != null && mFeatureId < 0) { st.menu.close(); } + + if (mWindowResizeCallbacksAdded) { + getViewRootImpl().removeWindowCallbacks(this); + mWindowResizeCallbacksAdded = false; + } } @Override @@ -1526,6 +1551,18 @@ class DecorView extends FrameLayout implements RootViewSurfaceTaker { } View onResourcesLoaded(LayoutInflater inflater, int layoutResource) { + mWorkspaceId = getWorkspaceId(); + + mResizingBackgroundDrawable = getResizingBackgroundDrawable( + mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource); + mCaptionBackgroundDrawable = + getContext().getDrawable(R.drawable.non_client_decor_title_focused); + + if (mBackdropFrameRenderer != null) { + mBackdropFrameRenderer.onResourcesLoaded( + this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable); + } + mNonClientDecorView = createNonClientDecorView(inflater); final View root = inflater.inflate(layoutResource, null); if (mNonClientDecorView != null) { @@ -1556,12 +1593,10 @@ class DecorView extends FrameLayout implements RootViewSurfaceTaker { 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)) { + final boolean hasNonClientDecor = ActivityManager.StackId.hasWindowDecor(mWorkspaceId); + if (!mWindow.isFloating() && isApplication && hasNonClientDecor) { // Dependent on the brightness of the used title we either use the // dark or the light button frame. if (nonClientDecorView == null) { @@ -1579,34 +1614,32 @@ class DecorView extends FrameLayout implements RootViewSurfaceTaker { } nonClientDecorView.setPhoneWindow(mWindow, ActivityManager.StackId.hasWindowDecor(mWorkspaceId), - ActivityManager.StackId.hasWindowShadow(mWorkspaceId), - getResizingBackgroundDrawable(), - getContext().getDrawable(R.drawable.non_client_decor_title_focused)); + ActivityManager.StackId.hasWindowShadow(mWorkspaceId)); + } else { + nonClientDecorView = null; } - // Tell the decor if it has a visible non client decor. - enableNonClientDecor( - nonClientDecorView != null && ActivityManager.StackId.hasWindowDecor(mWorkspaceId)); + // Tell the decor if it has a visible non client decor. + enableNonClientDecor(nonClientDecorView != null && hasNonClientDecor); 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() { + * 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(int backgroundRes, int backgroundFallbackRes) { final Context context = getContext(); - if (mWindow.mBackgroundResource != 0) { - final Drawable drawable = context.getDrawable(mWindow.mBackgroundResource); + if (backgroundRes != 0) { + final Drawable drawable = context.getDrawable(backgroundRes); if (drawable != null) { return drawable; } } - if (mWindow.mBackgroundFallbackResource != 0) { - final Drawable fallbackDrawable = - context.getDrawable(mWindow.mBackgroundFallbackResource); + if (backgroundFallbackRes != 0) { + final Drawable fallbackDrawable = context.getDrawable(backgroundFallbackRes); if (fallbackDrawable != null) { return fallbackDrawable; } @@ -1652,6 +1685,84 @@ class DecorView extends FrameLayout implements RootViewSurfaceTaker { } } + @Override + public void onWindowSizeIsChanging(Rect newBounds) { + if (mBackdropFrameRenderer != null) { + mBackdropFrameRenderer.setTargetRect(newBounds); + } + } + + @Override + public void onWindowDragResizeStart(Rect initialBounds) { + if (mWindow.isDestroyed()) { + // If the owner's window is gone, we should not be able to come here anymore. + releaseThreadedRenderer(); + return; + } + if (mBackdropFrameRenderer != null) { + return; + } + final ThreadedRenderer renderer = (ThreadedRenderer) getHardwareRenderer(); + if (renderer != null) { + mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer, + initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable); + + // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time. + // If we want to get the shadow shown while resizing, we would need to elevate a new + // element which owns the caption and has the elevation. + updateElevation(); + } + } + + @Override + public void onWindowDragResizeEnd() { + releaseThreadedRenderer(); + } + + @Override + public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) { + if (mBackdropFrameRenderer == null) { + return false; + } + return mBackdropFrameRenderer.onContentDrawn(offsetX, offsetY, sizeX, sizeY); + } + + @Override + public void onRequestDraw(boolean reportNextDraw) { + if (mBackdropFrameRenderer != null) { + mBackdropFrameRenderer.onRequestDraw(reportNextDraw); + } else if (reportNextDraw) { + // If render thread is gone, just report immediately. + if (isAttachedToWindow()) { + getViewRootImpl().reportDrawFinish(); + } + } + } + + /** Release the renderer thread which is usually done when the user stops resizing. */ + private void releaseThreadedRenderer() { + if (mBackdropFrameRenderer != null) { + mBackdropFrameRenderer.releaseRenderer(); + mBackdropFrameRenderer = null; + // Bring the shadow back. + updateElevation(); + } + } + + private void updateElevation() { + if (mNonClientDecorView != null) { + mNonClientDecorView.updateElevation(); + } + } + + boolean isShowingCaption() { + return mNonClientDecorView != null && mNonClientDecorView.isShowingDecor(); + } + + int getCaptionHeight() { + return isShowingCaption() ? mNonClientDecorView.getDecorCaptionHeight() : 0; + } + 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 48420dae22658..33b8e0515ab07 100644 --- a/core/java/com/android/internal/widget/NonClientDecorView.java +++ b/core/java/com/android/internal/widget/NonClientDecorView.java @@ -19,23 +19,19 @@ package com.android.internal.widget; import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; import android.content.Context; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; import android.os.RemoteException; import android.util.AttributeSet; import android.view.MotionEvent; -import android.view.ThreadedRenderer; import android.view.View; import android.widget.LinearLayout; import android.view.ViewGroup; import android.view.ViewOutlineProvider; import android.view.Window; -import android.view.WindowCallbacks; import android.util.Log; import android.util.TypedValue; import com.android.internal.R; -import com.android.internal.policy.BackdropFrameRenderer; +import com.android.internal.policy.DecorView; import com.android.internal.policy.PhoneWindow; /** @@ -64,7 +60,7 @@ import com.android.internal.policy.PhoneWindow; * This will be mitigated once b/22527834 will be addressed. */ public class NonClientDecorView extends LinearLayout - implements View.OnClickListener, View.OnTouchListener, WindowCallbacks { + 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; @@ -72,9 +68,7 @@ public class NonClientDecorView extends LinearLayout private final int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5; private PhoneWindow mOwner = null; private boolean mWindowHasShadow = false; - public boolean mShowDecor = false; - // True when this object is listening for window size changes. - private boolean mAttachedCallbacksToRootViewImpl = false; + private boolean mShowDecor = false; // True if the window is being dragged. private boolean mDragging = false; @@ -93,11 +87,6 @@ public class NonClientDecorView extends LinearLayout // to max until the first layout command has been executed. private boolean mAllowUpdateElevation = false; - private BackdropFrameRenderer mBackdropFrameRenderer = null; - - public Drawable mResizingBackgroundDrawable; - public Drawable mCaptionBackgroundDrawable; - public NonClientDecorView(Context context) { super(context); } @@ -110,37 +99,10 @@ public class NonClientDecorView extends LinearLayout super(context, attrs, defStyle); } - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - if (!mAttachedCallbacksToRootViewImpl) { - // If there is no window callback installed there was no window set before. Set it now. - // Note that our ViewRootImpl object will not change. - getViewRootImpl().addWindowCallbacks(this); - mAttachedCallbacksToRootViewImpl = true; - } else if (mBackdropFrameRenderer != null) { - // We are resizing and this call happened due to a configuration change. Tell the - // renderer about it. - mBackdropFrameRenderer.onConfigurationChange(); - } - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - if (mAttachedCallbacksToRootViewImpl) { - getViewRootImpl().removeWindowCallbacks(this); - mAttachedCallbacksToRootViewImpl = false; - } - } - - public void setPhoneWindow(PhoneWindow owner, boolean showDecor, boolean windowHasShadow, - Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawableDrawable) { + public void setPhoneWindow(PhoneWindow owner, boolean showDecor, boolean windowHasShadow) { mOwner = owner; mWindowHasShadow = windowHasShadow; mShowDecor = showDecor; - mResizingBackgroundDrawable = resizingBackgroundDrawable; - mCaptionBackgroundDrawable = captionBackgroundDrawableDrawable; updateCaptionVisibility(); if (mWindowHasShadow) { initializeElevation(); @@ -290,11 +252,12 @@ public class NonClientDecorView extends LinearLayout * Note: Windows which have (temporarily) changed their attributes to cover the SystemUI * will get no shadow as they are expected to be "full screen". **/ - private void updateElevation() { + 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 && mBackdropFrameRenderer == null) { + if (mWindowHasShadow + && ((DecorView) mOwner.getDecorView()).mBackdropFrameRenderer == null) { boolean fill = isFillingScreen(); elevation = fill ? 0 : (mWindowHasFocus ? DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : @@ -336,77 +299,12 @@ public class NonClientDecorView extends LinearLayout } } - @Override - public void onWindowDragResizeStart(Rect initialBounds) { - if (mOwner.isDestroyed()) { - // If the owner's window is gone, we should not be able to come here anymore. - releaseResources(); - return; - } - if (mBackdropFrameRenderer != null) { - return; - } - final ThreadedRenderer renderer = - (ThreadedRenderer) mOwner.getDecorView().getHardwareRenderer(); - if (renderer != null) { - mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer, initialBounds); - // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time. - // If we want to get the shadow shown while resizing, we would need to elevate a new - // element which owns the caption and has the elevation. - updateElevation(); - } + public boolean isShowingDecor() { + return mShowDecor; } - @Override - public boolean onContentDrawn(int xOffset, int yOffset, int xSize, int ySize) { - if (mBackdropFrameRenderer == null) { - return false; - } - return mBackdropFrameRenderer.onContentDrawn(xOffset, yOffset, xSize, ySize); + public int getDecorCaptionHeight() { + final View caption = getChildAt(0); + return (caption != null) ? caption.getHeight() : 0; } - - @Override - public void onRequestDraw(boolean reportNextDraw) { - if (mBackdropFrameRenderer != null) { - mBackdropFrameRenderer.onRequestDraw(reportNextDraw); - } else if (reportNextDraw) { - // If render thread is gone, just report immediately. - if (isAttachedToWindow()) { - getViewRootImpl().reportDrawFinish(); - } - } - } - - @Override - public void onWindowDragResizeEnd() { - releaseThreadedRenderer(); - } - - @Override - public void onWindowSizeIsChanging(Rect newBounds) { - if (mBackdropFrameRenderer != null) { - mBackdropFrameRenderer.setTargetRect(newBounds); - } - } - - /** - * Release the renderer thread which is usually done when the user stops resizing. - */ - private void releaseThreadedRenderer() { - if (mBackdropFrameRenderer != null) { - mBackdropFrameRenderer.releaseRenderer(); - mBackdropFrameRenderer = null; - // Bring the shadow back. - updateElevation(); - } - } - - /** - * Called when the parent window is destroyed to release all resources. Note that this will also - * destroy the renderer thread. - */ - private void releaseResources() { - releaseThreadedRenderer(); - } - }