summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Wale Ogunwale <ogunwale@google.com> 2015-11-17 11:26:56 -0800
committer Wale Ogunwale <ogunwale@google.com> 2015-11-17 12:19:45 -0800
commit0b3562db3e01abce88f20bf2faeba61cce00d438 (patch)
treea4e38aeb7844fe87b5433dfd3e5e01a3ff5dd5e7
parent0d7e912b001838748bfb61b92b81d2f493ef4a6b (diff)
Moved BackdropFrameRenderer from NonClientDecorView to its own class file
Bug: 24810450 Change-Id: Id86e97733161499bbc59617433792f5ddc4e7f9e
-rw-r--r--core/java/com/android/internal/policy/BackdropFrameRenderer.java289
-rw-r--r--core/java/com/android/internal/widget/NonClientDecorView.java294
2 files changed, 308 insertions, 275 deletions
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
new file mode 100644
index 000000000000..1eeb36310086
--- /dev/null
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -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);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java
index 6ae1b983042f..48420dae2265 100644
--- a/core/java/com/android/internal/widget/NonClientDecorView.java
+++ b/core/java/com/android/internal/widget/NonClientDecorView.java
@@ -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);
- }
- }
- }
}