Moved BackdropFrameRenderer from NonClientDecorView to its own class file
Bug: 24810450 Change-Id: Id86e97733161499bbc59617433792f5ddc4e7f9e
This commit is contained in:
289
core/java/com/android/internal/policy/BackdropFrameRenderer.java
Normal file
289
core/java/com/android/internal/policy/BackdropFrameRenderer.java
Normal file
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package com.android.internal.policy;
|
||||
|
||||
import com.android.internal.widget.NonClientDecorView;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Looper;
|
||||
import android.view.Choreographer;
|
||||
import android.view.DisplayListCanvas;
|
||||
import android.view.RenderNode;
|
||||
import android.view.ThreadedRenderer;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* The thread which draws a fill in background while the app is resizing in areas where the app
|
||||
* content draw is lagging behind the resize operation.
|
||||
* It starts with the creation and it ends once someone calls destroy().
|
||||
* Any size changes can be passed by a call to setTargetRect will passed to the thread and
|
||||
* executed via the Choreographer.
|
||||
* @hide
|
||||
*/
|
||||
public class BackdropFrameRenderer extends Thread implements Choreographer.FrameCallback {
|
||||
|
||||
private NonClientDecorView mNonClientDecorView;
|
||||
|
||||
// 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.
|
||||
private final Rect mTargetRect = new Rect();
|
||||
|
||||
// The render nodes for the multi threaded renderer.
|
||||
private ThreadedRenderer mRenderer;
|
||||
private RenderNode mFrameAndBackdropNode;
|
||||
|
||||
private final Rect mOldTargetRect = new Rect();
|
||||
private final Rect mNewTargetRect = new Rect();
|
||||
private Choreographer mChoreographer;
|
||||
|
||||
// Cached size values from the last render for the case that the view hierarchy is gone
|
||||
// during a configuration change.
|
||||
private int mLastContentWidth;
|
||||
private int mLastContentHeight;
|
||||
private int mLastCaptionHeight;
|
||||
private int mLastXOffset;
|
||||
private int mLastYOffset;
|
||||
|
||||
// Whether to report when next frame is drawn or not.
|
||||
private boolean mReportNextDraw;
|
||||
|
||||
public BackdropFrameRenderer(NonClientDecorView nonClientDecorView,
|
||||
ThreadedRenderer renderer,
|
||||
Rect initialBounds) {
|
||||
mNonClientDecorView = nonClientDecorView;
|
||||
setName("ResizeFrame");
|
||||
mRenderer = renderer;
|
||||
|
||||
// Create a render node for the content and frame backdrop
|
||||
// which can be resized independently from the content.
|
||||
mFrameAndBackdropNode = RenderNode.create("FrameAndBackdropNode", null);
|
||||
|
||||
mRenderer.addRenderNode(mFrameAndBackdropNode, true);
|
||||
|
||||
// Set the initial bounds and draw once so that we do not get a broken frame.
|
||||
mTargetRect.set(initialBounds);
|
||||
synchronized (this) {
|
||||
changeWindowSizeLocked(initialBounds);
|
||||
}
|
||||
|
||||
// Kick off our draw thread.
|
||||
start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param newTargetBounds The new target bounds.
|
||||
*/
|
||||
public void setTargetRect(Rect newTargetBounds) {
|
||||
synchronized (this) {
|
||||
mTargetRect.set(newTargetBounds);
|
||||
// Notify of a bounds change.
|
||||
pingRenderLocked();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The window got replaced due to a configuration change.
|
||||
*/
|
||||
public void onConfigurationChange() {
|
||||
synchronized (this) {
|
||||
if (mRenderer != null) {
|
||||
// Enforce a window redraw.
|
||||
mOldTargetRect.set(0, 0, 0, 0);
|
||||
pingRenderLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All resources of the renderer will be released. This function can be called from the
|
||||
* the UI thread as well as the renderer thread.
|
||||
*/
|
||||
public void releaseRenderer() {
|
||||
synchronized (this) {
|
||||
if (mRenderer != null) {
|
||||
// Invalidate the current content bounds.
|
||||
mRenderer.setContentDrawBounds(0, 0, 0, 0);
|
||||
|
||||
// Remove the render node again
|
||||
// (see comment above - better to do that only once).
|
||||
mRenderer.removeRenderNode(mFrameAndBackdropNode);
|
||||
|
||||
mRenderer = null;
|
||||
|
||||
// Exit the renderer loop.
|
||||
pingRenderLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Looper.prepare();
|
||||
synchronized (this) {
|
||||
mChoreographer = Choreographer.getInstance();
|
||||
|
||||
// Draw at least once.
|
||||
mChoreographer.postFrameCallback(this);
|
||||
}
|
||||
Looper.loop();
|
||||
} finally {
|
||||
releaseRenderer();
|
||||
}
|
||||
synchronized (this) {
|
||||
// Make sure no more messages are being sent.
|
||||
mChoreographer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of the FrameCallback.
|
||||
* @param frameTimeNanos The time in nanoseconds when the frame started being rendered,
|
||||
* in the {@link System#nanoTime()} timebase. Divide this value by {@code 1000000}
|
||||
*/
|
||||
@Override
|
||||
public void doFrame(long frameTimeNanos) {
|
||||
synchronized (this) {
|
||||
if (mRenderer == null) {
|
||||
reportDrawIfNeeded();
|
||||
// Tell the looper to stop. We are done.
|
||||
Looper.myLooper().quit();
|
||||
return;
|
||||
}
|
||||
mNewTargetRect.set(mTargetRect);
|
||||
if (!mNewTargetRect.equals(mOldTargetRect) || mReportNextDraw) {
|
||||
mOldTargetRect.set(mNewTargetRect);
|
||||
changeWindowSizeLocked(mNewTargetRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The content is about to be drawn and we got the location of where it will be shown.
|
||||
* If a "changeWindowSizeLocked" call has already been processed, we will re-issue the call
|
||||
* if the previous call was ignored since the size was unknown.
|
||||
* @param xOffset The x offset where the content is drawn to.
|
||||
* @param yOffset The y offset where the content is drawn to.
|
||||
* @param xSize The width size of the content. This should not be 0.
|
||||
* @param ySize The height of the content.
|
||||
* @return true if a frame should be requested after the content is drawn; false otherwise.
|
||||
*/
|
||||
public boolean onContentDrawn(int xOffset, int yOffset, int xSize, int ySize) {
|
||||
synchronized (this) {
|
||||
final boolean firstCall = mLastContentWidth == 0;
|
||||
// The current content buffer is drawn here.
|
||||
mLastContentWidth = xSize;
|
||||
mLastContentHeight = ySize - mLastCaptionHeight;
|
||||
mLastXOffset = xOffset;
|
||||
mLastYOffset = yOffset;
|
||||
|
||||
mRenderer.setContentDrawBounds(
|
||||
mLastXOffset,
|
||||
mLastYOffset,
|
||||
mLastXOffset + mLastContentWidth,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public void onRequestDraw(boolean reportNextDraw) {
|
||||
synchronized (this) {
|
||||
mReportNextDraw = reportNextDraw;
|
||||
mOldTargetRect.set(0, 0, 0, 0);
|
||||
pingRenderLocked();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizing the frame to fit the new window size.
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
// 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) ||
|
||||
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;
|
||||
final int top = mLastYOffset + newBounds.top;
|
||||
final int width = newBounds.width();
|
||||
final int height = newBounds.height();
|
||||
|
||||
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);
|
||||
|
||||
// The backdrop: clear everything with the background. Clipping is done elsewhere.
|
||||
mNonClientDecorView.mResizingBackgroundDrawable.setBounds(
|
||||
0, mLastCaptionHeight, left + width, top + height);
|
||||
mNonClientDecorView.mResizingBackgroundDrawable.draw(canvas);
|
||||
mFrameAndBackdropNode.end(canvas);
|
||||
|
||||
// We need to render the node explicitly
|
||||
mRenderer.drawRenderNode(mFrameAndBackdropNode);
|
||||
|
||||
reportDrawIfNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
mReportNextDraw = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the renderer to wake up and perform the next action which can be
|
||||
* either the next rendering or the self destruction if mRenderer is null.
|
||||
* Note: This call must be synchronized.
|
||||
*/
|
||||
private void pingRenderLocked() {
|
||||
if (mChoreographer != null) {
|
||||
mChoreographer.postFrameCallback(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,13 +21,9 @@ 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.Looper;
|
||||
import android.os.RemoteException;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Choreographer;
|
||||
import android.view.DisplayListCanvas;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.RenderNode;
|
||||
import android.view.ThreadedRenderer;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
@@ -39,6 +35,7 @@ import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.policy.BackdropFrameRenderer;
|
||||
import com.android.internal.policy.PhoneWindow;
|
||||
|
||||
/**
|
||||
@@ -75,7 +72,7 @@ public class NonClientDecorView extends LinearLayout
|
||||
private final int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
|
||||
private PhoneWindow mOwner = null;
|
||||
private boolean mWindowHasShadow = false;
|
||||
private boolean mShowDecor = false;
|
||||
public boolean mShowDecor = false;
|
||||
// True when this object is listening for window size changes.
|
||||
private boolean mAttachedCallbacksToRootViewImpl = false;
|
||||
|
||||
@@ -96,11 +93,10 @@ public class NonClientDecorView extends LinearLayout
|
||||
// to max until the first layout command has been executed.
|
||||
private boolean mAllowUpdateElevation = false;
|
||||
|
||||
// The resize frame renderer.
|
||||
private ResizeFrameThread mFrameRendererThread = null;
|
||||
private BackdropFrameRenderer mBackdropFrameRenderer = null;
|
||||
|
||||
private Drawable mResizingBackgroundDrawable;
|
||||
private Drawable mCaptionBackgroundDrawable;
|
||||
public Drawable mResizingBackgroundDrawable;
|
||||
public Drawable mCaptionBackgroundDrawable;
|
||||
|
||||
public NonClientDecorView(Context context) {
|
||||
super(context);
|
||||
@@ -122,10 +118,10 @@ public class NonClientDecorView extends LinearLayout
|
||||
// Note that our ViewRootImpl object will not change.
|
||||
getViewRootImpl().addWindowCallbacks(this);
|
||||
mAttachedCallbacksToRootViewImpl = true;
|
||||
} else if (mFrameRendererThread != null) {
|
||||
} else if (mBackdropFrameRenderer != null) {
|
||||
// We are resizing and this call happened due to a configuration change. Tell the
|
||||
// renderer about it.
|
||||
mFrameRendererThread.onConfigurationChange();
|
||||
mBackdropFrameRenderer.onConfigurationChange();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,7 +294,7 @@ public class NonClientDecorView extends LinearLayout
|
||||
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 && mFrameRendererThread == null) {
|
||||
if (mWindowHasShadow && mBackdropFrameRenderer == null) {
|
||||
boolean fill = isFillingScreen();
|
||||
elevation = fill ? 0 :
|
||||
(mWindowHasFocus ? DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP :
|
||||
@@ -347,13 +343,13 @@ public class NonClientDecorView extends LinearLayout
|
||||
releaseResources();
|
||||
return;
|
||||
}
|
||||
if (mFrameRendererThread != null) {
|
||||
if (mBackdropFrameRenderer != null) {
|
||||
return;
|
||||
}
|
||||
final ThreadedRenderer renderer =
|
||||
(ThreadedRenderer) mOwner.getDecorView().getHardwareRenderer();
|
||||
if (renderer != null) {
|
||||
mFrameRendererThread = new ResizeFrameThread(renderer, initialBounds);
|
||||
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.
|
||||
@@ -363,16 +359,16 @@ public class NonClientDecorView extends LinearLayout
|
||||
|
||||
@Override
|
||||
public boolean onContentDrawn(int xOffset, int yOffset, int xSize, int ySize) {
|
||||
if (mFrameRendererThread == null) {
|
||||
if (mBackdropFrameRenderer == null) {
|
||||
return false;
|
||||
}
|
||||
return mFrameRendererThread.onContentDrawn(xOffset, yOffset, xSize, ySize);
|
||||
return mBackdropFrameRenderer.onContentDrawn(xOffset, yOffset, xSize, ySize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestDraw(boolean reportNextDraw) {
|
||||
if (mFrameRendererThread != null) {
|
||||
mFrameRendererThread.onRequestDraw(reportNextDraw);
|
||||
if (mBackdropFrameRenderer != null) {
|
||||
mBackdropFrameRenderer.onRequestDraw(reportNextDraw);
|
||||
} else if (reportNextDraw) {
|
||||
// If render thread is gone, just report immediately.
|
||||
if (isAttachedToWindow()) {
|
||||
@@ -388,8 +384,8 @@ public class NonClientDecorView extends LinearLayout
|
||||
|
||||
@Override
|
||||
public void onWindowSizeIsChanging(Rect newBounds) {
|
||||
if (mFrameRendererThread != null) {
|
||||
mFrameRendererThread.setTargetRect(newBounds);
|
||||
if (mBackdropFrameRenderer != null) {
|
||||
mBackdropFrameRenderer.setTargetRect(newBounds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,9 +393,9 @@ public class NonClientDecorView extends LinearLayout
|
||||
* Release the renderer thread which is usually done when the user stops resizing.
|
||||
*/
|
||||
private void releaseThreadedRenderer() {
|
||||
if (mFrameRendererThread != null) {
|
||||
mFrameRendererThread.releaseRenderer();
|
||||
mFrameRendererThread = null;
|
||||
if (mBackdropFrameRenderer != null) {
|
||||
mBackdropFrameRenderer.releaseRenderer();
|
||||
mBackdropFrameRenderer = null;
|
||||
// Bring the shadow back.
|
||||
updateElevation();
|
||||
}
|
||||
@@ -413,256 +409,4 @@ public class NonClientDecorView extends LinearLayout
|
||||
releaseThreadedRenderer();
|
||||
}
|
||||
|
||||
/**
|
||||
* The thread which draws the chrome while we are resizing.
|
||||
* It starts with the creation and it ends once someone calls destroy().
|
||||
* Any size changes can be passed by a call to setTargetRect will passed to the thread and
|
||||
* executed via the Choreographer.
|
||||
* TODO(b/24810450): Separate functionality from non-client-decor so that it can be used
|
||||
* independently.
|
||||
*/
|
||||
private class ResizeFrameThread extends Thread implements Choreographer.FrameCallback {
|
||||
// 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.
|
||||
private final Rect mTargetRect = new Rect();
|
||||
|
||||
// The render nodes for the multi threaded renderer.
|
||||
private ThreadedRenderer mRenderer;
|
||||
private RenderNode mFrameAndBackdropNode;
|
||||
|
||||
private final Rect mOldTargetRect = new Rect();
|
||||
private final Rect mNewTargetRect = new Rect();
|
||||
private Choreographer mChoreographer;
|
||||
|
||||
// Cached size values from the last render for the case that the view hierarchy is gone
|
||||
// during a configuration change.
|
||||
private int mLastContentWidth;
|
||||
private int mLastContentHeight;
|
||||
private int mLastCaptionHeight;
|
||||
private int mLastXOffset;
|
||||
private int mLastYOffset;
|
||||
|
||||
// Whether to report when next frame is drawn or not.
|
||||
private boolean mReportNextDraw;
|
||||
|
||||
ResizeFrameThread(ThreadedRenderer renderer, Rect initialBounds) {
|
||||
setName("ResizeFrame");
|
||||
mRenderer = renderer;
|
||||
|
||||
// Create a render node for the content and frame backdrop
|
||||
// which can be resized independently from the content.
|
||||
mFrameAndBackdropNode = RenderNode.create("FrameAndBackdropNode", null);
|
||||
|
||||
mRenderer.addRenderNode(mFrameAndBackdropNode, true);
|
||||
|
||||
// Set the initial bounds and draw once so that we do not get a broken frame.
|
||||
mTargetRect.set(initialBounds);
|
||||
synchronized (this) {
|
||||
changeWindowSizeLocked(initialBounds);
|
||||
}
|
||||
|
||||
// Kick off our draw thread.
|
||||
start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param newTargetBounds The new target bounds.
|
||||
*/
|
||||
public void setTargetRect(Rect newTargetBounds) {
|
||||
synchronized (this) {
|
||||
mTargetRect.set(newTargetBounds);
|
||||
// Notify of a bounds change.
|
||||
pingRenderLocked();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The window got replaced due to a configuration change.
|
||||
*/
|
||||
public void onConfigurationChange() {
|
||||
synchronized (this) {
|
||||
if (mRenderer != null) {
|
||||
// Enforce a window redraw.
|
||||
mOldTargetRect.set(0, 0, 0, 0);
|
||||
pingRenderLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All resources of the renderer will be released. This function can be called from the
|
||||
* the UI thread as well as the renderer thread.
|
||||
*/
|
||||
public void releaseRenderer() {
|
||||
synchronized (this) {
|
||||
if (mRenderer != null) {
|
||||
// Invalidate the current content bounds.
|
||||
mRenderer.setContentDrawBounds(0, 0, 0, 0);
|
||||
|
||||
// Remove the render node again
|
||||
// (see comment above - better to do that only once).
|
||||
mRenderer.removeRenderNode(mFrameAndBackdropNode);
|
||||
|
||||
mRenderer = null;
|
||||
|
||||
// Exit the renderer loop.
|
||||
pingRenderLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Looper.prepare();
|
||||
synchronized (this) {
|
||||
mChoreographer = Choreographer.getInstance();
|
||||
|
||||
// Draw at least once.
|
||||
mChoreographer.postFrameCallback(this);
|
||||
}
|
||||
Looper.loop();
|
||||
} finally {
|
||||
releaseRenderer();
|
||||
}
|
||||
synchronized (this) {
|
||||
// Make sure no more messages are being sent.
|
||||
mChoreographer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of the FrameCallback.
|
||||
* @param frameTimeNanos The time in nanoseconds when the frame started being rendered,
|
||||
* in the {@link System#nanoTime()} timebase. Divide this value by {@code 1000000}
|
||||
*/
|
||||
@Override
|
||||
public void doFrame(long frameTimeNanos) {
|
||||
synchronized (this) {
|
||||
if (mRenderer == null) {
|
||||
reportDrawIfNeeded();
|
||||
// Tell the looper to stop. We are done.
|
||||
Looper.myLooper().quit();
|
||||
return;
|
||||
}
|
||||
mNewTargetRect.set(mTargetRect);
|
||||
if (!mNewTargetRect.equals(mOldTargetRect) || mReportNextDraw) {
|
||||
mOldTargetRect.set(mNewTargetRect);
|
||||
changeWindowSizeLocked(mNewTargetRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The content is about to be drawn and we got the location of where it will be shown.
|
||||
* If a "changeWindowSizeLocked" call has already been processed, we will re-issue the call
|
||||
* if the previous call was ignored since the size was unknown.
|
||||
* @param xOffset The x offset where the content is drawn to.
|
||||
* @param yOffset The y offset where the content is drawn to.
|
||||
* @param xSize The width size of the content. This should not be 0.
|
||||
* @param ySize The height of the content.
|
||||
* @return true if a frame should be requested after the content is drawn; false otherwise.
|
||||
*/
|
||||
public boolean onContentDrawn(int xOffset, int yOffset, int xSize, int ySize) {
|
||||
synchronized (this) {
|
||||
final boolean firstCall = mLastContentWidth == 0;
|
||||
// The current content buffer is drawn here.
|
||||
mLastContentWidth = xSize;
|
||||
mLastContentHeight = ySize - mLastCaptionHeight;
|
||||
mLastXOffset = xOffset;
|
||||
mLastYOffset = yOffset;
|
||||
|
||||
mRenderer.setContentDrawBounds(
|
||||
mLastXOffset,
|
||||
mLastYOffset,
|
||||
mLastXOffset + mLastContentWidth,
|
||||
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 || !mShowDecor);
|
||||
}
|
||||
}
|
||||
|
||||
public void onRequestDraw(boolean reportNextDraw) {
|
||||
synchronized (this) {
|
||||
mReportNextDraw = reportNextDraw;
|
||||
mOldTargetRect.set(0, 0, 0, 0);
|
||||
pingRenderLocked();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizing the frame to fit the new window size.
|
||||
* @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 = 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;
|
||||
}
|
||||
}
|
||||
// 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 && mShowDecor) ||
|
||||
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;
|
||||
final int top = mLastYOffset + newBounds.top;
|
||||
final int width = newBounds.width();
|
||||
final int height = newBounds.height();
|
||||
|
||||
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);
|
||||
mCaptionBackgroundDrawable.setBounds(0, 0, left + width, top + mLastCaptionHeight);
|
||||
mCaptionBackgroundDrawable.draw(canvas);
|
||||
|
||||
// The backdrop: clear everything with the background. Clipping is done elsewhere.
|
||||
mResizingBackgroundDrawable.setBounds(0, mLastCaptionHeight, left + width, top + height);
|
||||
mResizingBackgroundDrawable.draw(canvas);
|
||||
mFrameAndBackdropNode.end(canvas);
|
||||
|
||||
// We need to render the node explicitly
|
||||
mRenderer.drawRenderNode(mFrameAndBackdropNode);
|
||||
|
||||
reportDrawIfNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify view root that a frame has been drawn by us, if it has requested so.
|
||||
*/
|
||||
private void reportDrawIfNeeded() {
|
||||
if (mReportNextDraw) {
|
||||
if (isAttachedToWindow()) {
|
||||
getViewRootImpl().reportDrawFinish();
|
||||
}
|
||||
mReportNextDraw = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the renderer to wake up and perform the next action which can be
|
||||
* either the next rendering or the self destruction if mRenderer is null.
|
||||
* Note: This call must be synchronized.
|
||||
*/
|
||||
private void pingRenderLocked() {
|
||||
if (mChoreographer != null) {
|
||||
mChoreographer.postFrameCallback(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user