Merge changes Ib786d55e,Ibcda3d72

* changes:
  Only add NonClientDecorView when needed
  Move management of BackdropFrameRenderer to DecorView
This commit is contained in:
Wale Ogunwale
2015-11-18 00:50:25 +00:00
committed by Android (Google) Code Review
3 changed files with 178 additions and 164 deletions

View File

@@ -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;
}

View File

@@ -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;

View File

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