Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(271)

Unified Diff: remoting/android/java/src/org/chromium/chromoting/DesktopCanvas.java

Issue 2441723004: [Android][Client] Updating the calculations used for image overpanning (Closed)
Patch Set: Addressing feedback and merging with ToT Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: remoting/android/java/src/org/chromium/chromoting/DesktopCanvas.java
diff --git a/remoting/android/java/src/org/chromium/chromoting/DesktopCanvas.java b/remoting/android/java/src/org/chromium/chromoting/DesktopCanvas.java
index e93364f249bf980843f5ad4556cb0376ba473eeb..cf8353b58a1fd9d41f70a6ead29d1b6d5b517d89 100644
--- a/remoting/android/java/src/org/chromium/chromoting/DesktopCanvas.java
+++ b/remoting/android/java/src/org/chromium/chromoting/DesktopCanvas.java
@@ -8,67 +8,54 @@ import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.os.SystemClock;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
/**
* This class is responsible for transforming the desktop image matrix.
*/
public class DesktopCanvas {
- /**
- * Maximum allowed zoom level - see {@link #scaleAndRepositionImage()}.
- */
- private static final float MAX_ZOOM_FACTOR = 100.0f;
+ /** Used for floating point comparisons. */
+ private static final float EPSILON = 0.0001f;
- /**
- * Used to smoothly reduce the amount of padding while the user is zooming.
- */
- private static final float PADDING_REDUCTION_FACTOR = 0.85f;
+ /** Maximum allowed zoom level - see {@link #scaleAndRepositionImage()}. */
+ private static final float MAX_ZOOM_FACTOR = 100.0f;
private final RenderStub mRenderStub;
private final RenderData mRenderData;
/**
- * Represents the actual center of the viewport in image space. This value needs to be a pair
- * of floats so the desktop image can be positioned with sub-pixel accuracy for smoother panning
- * animations at high zoom levels.
- */
- // TODO(joedow): See if we can collapse Viewport and Cursor position members. They were needed
- // in the past due to how we calculated the center positions but may not be needed now.
- private PointF mViewportPosition = new PointF();
-
- /**
* Represents the desired center of the viewport in image space. This value may not represent
* the actual center of the viewport as adjustments are made to ensure as much of the desktop is
* visible as possible. This value needs to be a pair of floats so the desktop image can be
* positioned with sub-pixel accuracy for smoother panning animations at high zoom levels.
- * Note: The internal cursor position may be placed outside of the image boundary, however the
- * cursor position we inject on the host side is restricted to the actual image dimensions.
*/
- private PointF mCursorPosition = new PointF();
+ private PointF mDesiredCenter = new PointF();
/**
- * Represents the amount of space, in pixels, used by System UI on each edge of the screen.
+ * If System UI exists, this contains the area of the screen which is unobscured by it,
+ * otherwise it is empty.
*/
- // TODO(joedow): Update usage of this member so it is a true Rect instead of a set of offsets.
- private Rect mSystemUiScreenSize = new Rect();
+ private Rect mSystemUiScreenRect = new Rect();
/**
- * Represents the amount of padding, in screen pixels, added to each edge of the desktop image.
- * This extra space allows the user to reveal portions of the desktop image which are obscured
- * by System UI.
+ * Represents the amount of space, in pixels, to shift the image under the viewport. This value
+ * is used to allow panning the image further than would be possible when using the normal
+ * boundary values to account for System UI. This functionality ensures the user can view and
+ * interact with any area of the remote image, even when System UI might otherwise obscure it.
*/
- private RectF mVisibleImagePadding = new RectF();
+ private PointF mViewportOffset = new PointF();
/**
- * Tracks whether to adjust the viewport to account for System UI. If false, the viewport
- * center is the center of the screen. If true, then System UI offsets will be used to
- * adjust the position of the viewport to ensure the cursor is visible.
+ * Tracks whether to adjust the size of the viewport to account for System UI. If false, the
+ * viewport center is mapped to the center of the screen. If true, then System UI sizes will be
+ * used to determine the center of the viewport.
*/
- private boolean mAdjustViewportForSystemUi = false;
+ private boolean mAdjustViewportSizeForSystemUi = false;
- /**
- * Represents the amount of space, in pixels, to adjust the cursor center along the y-axis.
- */
- private float mCursorOffsetScreenY = 0.0f;
+ /* Used to perform per-frame rendering tasks. */
+ private Event.ParameterCallback<Boolean, Void> mFrameRenderedCallback;
public DesktopCanvas(RenderStub renderStub, RenderData renderData) {
mRenderStub = renderStub;
@@ -76,52 +63,47 @@ public class DesktopCanvas {
}
/**
- * Sets the desired center position of the viewport (a.k.a. the cursor position) and ensures
- * the viewport is updated to include the cursor within it.
+ * Sets the desired center position of the viewport (a.k.a. the cursor position).
*
* @param newX The new x coordinate value for the desired center position.
* @param newY The new y coordinate value for the desired center position.
*/
public void setCursorPosition(float newX, float newY) {
- // First set the cursor position since its potential values are a superset of the viewport.
- mCursorPosition.set(newX, newY);
- constrainPointToBounds(mCursorPosition, getImageBounds());
-
- // Now set the viewport position based on the cursor.
- mViewportPosition.set(mCursorPosition);
- constrainPointToBounds(mViewportPosition, getViewportBounds());
-
- repositionImage();
+ updateCursorPosition(newX, newY, getImageBounds());
}
/**
- * Shifts the viewport by the passed in values (in image coordinates).
+ * Sets the center of the viewport using an absolute position (in image coordinates).
*
- * @param deltaX The distance (in image coordinates) to move the viewport along the x-axis.
- * @param deltaY The distance (in image coordinates) to move the viewport along the y-axis.
+ * @param newX The new x coordinate value for the center position of the viewport.
+ * @param newY The new y coordinate value for the center position of the viewport.
*/
- public void moveViewportCenter(float deltaX, float deltaY) {
- // Offset and adjust the viewport center position to fit the screen.
- mViewportPosition.offset(deltaX, deltaY);
- constrainPointToBounds(mViewportPosition, getViewportBounds());
-
- // We don't need to constrain the cursor position as the viewport position is always within
- // the bounds of the potential cursor positions.
- mCursorPosition.set(mViewportPosition);
-
- repositionImage();
+ public void setViewportCenter(float newX, float newY) {
+ updateCursorPosition(newX, newY, getViewportBounds());
}
/**
- * Shifts the cursor by the passed in values (in image coordinates) and adjusts the viewport.
+ * Shifts the cursor by the passed in values (in image coordinates).
*
* @param deltaX The distance (in image coordinates) to move the cursor along the x-axis.
* @param deltaY The distance (in image coordinates) to move the cursor along the y-axis.
* @return A point representing the new cursor position.
*/
public PointF moveCursorPosition(float deltaX, float deltaY) {
- setCursorPosition(mCursorPosition.x + deltaX, mCursorPosition.y + deltaY);
- return new PointF(mCursorPosition.x, mCursorPosition.y);
+ updateCursorPosition(
+ mDesiredCenter.x + deltaX, mDesiredCenter.y + deltaY, getImageBounds());
+ return new PointF(mDesiredCenter.x, mDesiredCenter.y);
+ }
+
+ /**
+ * Shifts the viewport by the passed in values (in image coordinates).
+ *
+ * @param deltaX The distance (in image coordinates) to move the viewport along the x-axis.
+ * @param deltaY The distance (in image coordinates) to move the viewport along the y-axis.
+ */
+ public void moveViewportCenter(float deltaX, float deltaY) {
+ updateCursorPosition(
+ mDesiredCenter.x + deltaX, mDesiredCenter.y + deltaY, getViewportBounds());
}
/**
@@ -131,33 +113,23 @@ public class DesktopCanvas {
*/
public void onSystemUiVisibilityChanged(SystemUiVisibilityChangedEventParameter parameter) {
if (parameter.softInputMethodVisible) {
- mSystemUiScreenSize.set(parameter.left, parameter.top,
- mRenderData.screenWidth - parameter.right,
- mRenderData.screenHeight - parameter.bottom);
-
- if (mAdjustViewportForSystemUi) {
- // Adjust the cursor position to ensure it's visible when large System UI (1/3 or
- // more of the total screen size) is displayed (typically the Soft Keyboard).
- // Without this change, it is difficult for users to enter text into edit controls
- // which are located bottom of the screen and may not see the cursor at all.
- if (mSystemUiScreenSize.bottom > (mRenderData.screenHeight / 3)) {
- // Center the cursor within the viewable area (not obscured by System UI).
- mCursorOffsetScreenY = (float) parameter.bottom / 2.0f;
- } else {
- mCursorOffsetScreenY = 0.0f;
- }
+ mSystemUiScreenRect.set(
+ parameter.left, parameter.top, parameter.right, parameter.bottom);
- // Apply the cursor offset.
- setCursorPosition(mCursorPosition.x, mCursorPosition.y);
- }
+ stopOffsetReductionAnimation();
} else {
- mCursorOffsetScreenY = 0.0f;
- mSystemUiScreenSize.setEmpty();
+ mSystemUiScreenRect.setEmpty();
+ startOffsetReductionAnimation();
}
+
+ repositionImage();
}
public void adjustViewportForSystemUi(boolean adjustViewportForSystemUi) {
- mAdjustViewportForSystemUi = adjustViewportForSystemUi;
+ mAdjustViewportSizeForSystemUi = adjustViewportForSystemUi;
+
+ // The viewport center may have changed so reposition the image to reflect the new value.
+ repositionImage();
}
/** Resizes the image by zooming it such that the image is displayed without borders. */
@@ -180,7 +152,7 @@ public class DesktopCanvas {
/**
* Repositions the image by translating and zooming it, to keep the zoom level within sensible
- * limits. The minimum zoom level is chosen to avoid black space around all 4 sides. The
+ * limits. The minimum zoom level is chosen to avoid letterboxing on all 4 sides. The
* maximum zoom level is set arbitrarily, so that the user can zoom out again in a reasonable
* time, and to prevent arithmetic overflow problems from displaying the image.
*
@@ -217,47 +189,75 @@ public class DesktopCanvas {
mRenderData.transform.setScale(scale, scale);
}
- // Trim the image padding if the user is zooming out. This prevents cases where the image
- // pops to the center when it reaches its minimum size. Note that we do not need to do
- // anything when the user is zooming in as the canvas will expand and absorb the padding.
- if (scaleFactor < 1.0f) {
- mVisibleImagePadding.set(mVisibleImagePadding.left * PADDING_REDUCTION_FACTOR,
- mVisibleImagePadding.top * PADDING_REDUCTION_FACTOR,
- mVisibleImagePadding.right * PADDING_REDUCTION_FACTOR,
- mVisibleImagePadding.bottom * PADDING_REDUCTION_FACTOR);
- }
-
if (centerOnCursor) {
- setCursorPosition(mCursorPosition.x, mCursorPosition.y);
+ setCursorPosition(mDesiredCenter.x, mDesiredCenter.y);
} else {
- // Find the new screen center (it was probably changed during the zoom operation) and
- // update the viewport and cursor.
- float[] mappedPoints = {
- ((float) mRenderData.screenWidth / 2), ((float) mRenderData.screenHeight / 2)};
+ // Find the new screen center (it probably changed during the zoom operation) and update
+ // the viewport to smoothly track the zoom gesture.
+ float[] mappedPoints = {((float) mRenderData.screenWidth / 2) - mViewportOffset.x,
+ ((float) mRenderData.screenHeight / 2) - mViewportOffset.y};
Matrix screenToImage = new Matrix();
mRenderData.transform.invert(screenToImage);
screenToImage.mapPoints(mappedPoints);
// The cursor is mapped to the center of the viewport in this case.
- setCursorPosition(mappedPoints[0], mappedPoints[1]);
+ setViewportCenter(mappedPoints[0], mappedPoints[1]);
}
}
/**
+ * Sets the new cursor position, bounded by the given rect, and updates the image transform to
+ * reflect the new position.
+ */
+ private void updateCursorPosition(float newX, float newY, RectF bounds) {
+ mDesiredCenter.set(newX, newY);
+ constrainPointToBounds(mDesiredCenter, bounds);
+
+ calculateViewportOffset(newX - mDesiredCenter.x, newY - mDesiredCenter.y);
+
+ repositionImage();
+ }
+
+ /**
+ * Returns the height of the screen (in screen coordinates) for use in calculations involving
+ * viewport positioning.
+ */
+ private float getAdjustedScreenHeight() {
+ float adjustedScreenHeight;
+ if (mAdjustViewportSizeForSystemUi && !mSystemUiScreenRect.isEmpty()) {
+ // Find the center point of the viewport on the screen.
+ adjustedScreenHeight = mSystemUiScreenRect.bottom;
+ } else {
+ adjustedScreenHeight = ((float) mRenderData.screenHeight);
+ }
+
+ return adjustedScreenHeight;
+ }
+
+ /**
+ * Returns the center position of the viewport (in screen coordinates) taking System UI into
+ * account.
+ */
+ private PointF getViewportScreenCenter() {
+ return new PointF((float) mRenderData.screenWidth / 2, getAdjustedScreenHeight() / 2);
+ }
+
+ /**
* Repositions the image by translating it (without affecting the zoom level).
*/
private void repositionImage() {
- // Map the current viewport position to screen coordinates and adjust the image position.
- float[] viewportPosition = {mViewportPosition.x, mViewportPosition.y};
- mRenderData.transform.mapPoints(viewportPosition);
+ PointF viewportPosition = new PointF(mDesiredCenter.x, mDesiredCenter.y);
+ constrainPointToBounds(viewportPosition, getViewportBounds());
+ float[] viewportAdjustment = {viewportPosition.x, viewportPosition.y};
+ mRenderData.transform.mapPoints(viewportAdjustment);
- float viewportTransX = ((float) mRenderData.screenWidth / 2) - viewportPosition[0];
- float viewportTransY =
- ((float) mRenderData.screenHeight / 2) - viewportPosition[1] - mCursorOffsetScreenY;
+ // Adjust the viewport to include the overpan amount.
+ viewportAdjustment[0] += mViewportOffset.x;
+ viewportAdjustment[1] += mViewportOffset.y;
// Translate the image to move the viewport to the expected screen location.
- mRenderData.transform.postTranslate(viewportTransX, viewportTransY);
-
- updateVisibleImagePadding();
+ PointF viewportCenter = getViewportScreenCenter();
+ mRenderData.transform.postTranslate(
+ viewportCenter.x - viewportAdjustment[0], viewportCenter.y - viewportAdjustment[1]);
mRenderStub.setTransformation(mRenderData.transform);
}
@@ -284,27 +284,7 @@ public class DesktopCanvas {
/** Returns a region which defines the set of valid cursor positions in image space. */
private RectF getImageBounds() {
- // The set of valid cursor positions includes any point on the image as well as the padding.
- // Padding is additional space added to the image which is the larger value of:
- // - Potential overlap of the System UI and image content
- // - Actual amount of padding already being used
- //
- // By expanding the area, we allow the user to move the cursor 'under' the System UI which
- // pulls the content out from under it and allows it to be visible. Once the System UI has
- // been dismissed or changes size, we use the actual padding value instead which prevents
- // the desktop image from 'snapping' back to pre-System UI state.
- RectF systemUiOverlap = getSystemUiOverlap();
- float[] padding = {Math.max(mVisibleImagePadding.left, systemUiOverlap.left),
- Math.max(mVisibleImagePadding.top + mCursorOffsetScreenY, systemUiOverlap.top),
- Math.max(mVisibleImagePadding.right, systemUiOverlap.right),
- Math.max(mVisibleImagePadding.bottom - mCursorOffsetScreenY,
- systemUiOverlap.bottom)};
- Matrix screenToImage = new Matrix();
- mRenderData.transform.invert(screenToImage);
- screenToImage.mapVectors(padding);
-
- return new RectF(-padding[0], -padding[1], mRenderData.imageWidth + padding[2],
- mRenderData.imageHeight + padding[3]);
+ return new RectF(0, 0, mRenderData.imageWidth, mRenderData.imageHeight);
}
/** Returns a region which defines the set of valid viewport center values in image space. */
@@ -315,7 +295,8 @@ public class DesktopCanvas {
Matrix screenToImage = new Matrix();
mRenderData.transform.invert(screenToImage);
- float[] screenVectors = {(float) mRenderData.screenWidth, (float) mRenderData.screenHeight};
+ PointF viewportCenter = getViewportScreenCenter();
+ float[] screenVectors = {viewportCenter.x, viewportCenter.y};
screenToImage.mapVectors(screenVectors);
PointF letterboxPadding = getLetterboxPadding();
@@ -323,14 +304,36 @@ public class DesktopCanvas {
screenToImage.mapVectors(letterboxPaddingVectors);
// screenCenter values are 1/2 of a particular screen dimension mapped to image space.
- float screenCenterX = (screenVectors[0] / 2.0f) - letterboxPaddingVectors[0];
- float screenCenterY = (screenVectors[1] / 2.0f) - letterboxPaddingVectors[1];
+ float screenCenterX = screenVectors[0] - letterboxPaddingVectors[0];
+ float screenCenterY = screenVectors[1] - letterboxPaddingVectors[1];
RectF imageBounds = getImageBounds();
imageBounds.inset(screenCenterX, screenCenterY);
return imageBounds;
}
/**
+ * Returns a region defining the maximum offset distance required to view the entire image
+ * given the current amount of System UI overlapping it.
+ */
+ private RectF getViewportOffsetBounds() {
+ // The allowable region is determined by:
+ // - Overlap of the System UI and image content
+ // - Current viewport offset
+ //
+ // The System UI overlap represents the maximum allowable offset, this is used to bound the
+ // viewport movement in each direction. The current offset is used to prevent 'snapping'
+ // the image when the System UI overlap is reduced.
+ RectF viewportOffsetRect = new RectF();
+ viewportOffsetRect.union(mViewportOffset.x, mViewportOffset.y);
+
+ RectF systemUiOverlap = getSystemUiOverlap();
+ return new RectF(Math.min(viewportOffsetRect.left, -systemUiOverlap.left),
+ Math.min(viewportOffsetRect.top, -systemUiOverlap.top),
+ Math.max(viewportOffsetRect.right, systemUiOverlap.right),
+ Math.max(viewportOffsetRect.bottom, systemUiOverlap.bottom));
+ }
+
+ /**
* Provides the amount of padding needed to center the image content on the screen.
*/
private PointF getLetterboxPadding() {
@@ -339,10 +342,8 @@ public class DesktopCanvas {
// We want to letterbox when the image is smaller than the screen in a specific dimension.
// Since we center the image, split the difference so it is equally distributed.
- float widthAdjust =
- Math.max(((float) mRenderData.screenWidth - imageVectors[0]) / 2.0f, 0.0f);
- float heightAdjust =
- Math.max(((float) mRenderData.screenHeight - imageVectors[1]) / 2.0f, 0.0f);
+ float widthAdjust = Math.max(((float) mRenderData.screenWidth - imageVectors[0]) / 2, 0);
+ float heightAdjust = Math.max((getAdjustedScreenHeight() - imageVectors[1]) / 2, 0);
return new PointF(widthAdjust, heightAdjust);
}
@@ -352,29 +353,98 @@ public class DesktopCanvas {
* desktop image below it. This is the maximum amount that could overlap, not the actual value.
*/
private RectF getSystemUiOverlap() {
- // letterBox padding represents the space added to the image to center it on the screen.
+ if (mSystemUiScreenRect.isEmpty()) {
+ return new RectF();
+ }
+
+ // Letterbox padding represents the space added to the image to center it on the screen.
// Since it does not contain any interactable UI, we ignore it when calculating the overlap
// between the System UI and the remote desktop image.
// Note: Ignore negative padding (clamp to 0) since that means no overlap exists.
+ float adjustedScreenHeight = getAdjustedScreenHeight();
PointF letterboxPadding = getLetterboxPadding();
- return new RectF(Math.max(mSystemUiScreenSize.left - letterboxPadding.x, 0.0f),
- Math.max(mSystemUiScreenSize.top - letterboxPadding.y + mCursorOffsetScreenY, 0.0f),
- Math.max(mSystemUiScreenSize.right - letterboxPadding.x, 0.0f),
- Math.max(mSystemUiScreenSize.bottom - letterboxPadding.y - mCursorOffsetScreenY,
+ return new RectF(Math.max(mSystemUiScreenRect.left - letterboxPadding.x, 0.0f),
+ Math.max(mSystemUiScreenRect.top - letterboxPadding.y, 0.0f),
+ Math.max(mRenderData.screenWidth - mSystemUiScreenRect.right - letterboxPadding.x,
+ 0.0f),
+ Math.max(adjustedScreenHeight - mSystemUiScreenRect.bottom - letterboxPadding.y,
0.0f));
}
/**
- * Calculates the amount of padding visible on each edge of the desktop image.
+ * Applies the given offset, as needed, to allow moving the image outside its normal bounds.
*/
- private void updateVisibleImagePadding() {
- PointF letterboxPadding = getLetterboxPadding();
- float[] imagePoints = {0.0f, 0.0f, mRenderData.imageWidth, mRenderData.imageHeight};
- mRenderData.transform.mapPoints(imagePoints);
+ private void calculateViewportOffset(float offsetX, float offsetY) {
+ if (mSystemUiScreenRect.isEmpty()) {
+ // We only want to directly change the viewport offset when System UI is present.
+ return;
+ }
+
+ float[] offsets = {offsetX, offsetY};
+ mRenderData.transform.mapVectors(offsets);
- mVisibleImagePadding.set(Math.max(imagePoints[0] - letterboxPadding.x, 0.0f),
- Math.max(imagePoints[1] - letterboxPadding.y, 0.0f),
- Math.max(mRenderData.screenWidth - imagePoints[2] - letterboxPadding.x, 0.0f),
- Math.max(mRenderData.screenHeight - imagePoints[3] - letterboxPadding.y, 0.0f));
+ // Use a temporary variable here as {@link #getViewportOffsetBounds()} uses the current
+ // viewport offset as a maximum boundary. If we add the offset first, the result ends up
+ // being unbounded. Thus we use a temporary object for the boundary calculation.
+ PointF requestedOffset =
+ new PointF(mViewportOffset.x + offsets[0], mViewportOffset.y + offsets[1]);
+ constrainPointToBounds(requestedOffset, getViewportOffsetBounds());
+ mViewportOffset.set(requestedOffset);
+ }
+
+ /**
+ * Starts an animation to smoothly reduce the viewport offset. Does nothing if an animation is
+ * already running or the offset is already 0.
+ */
+ private void startOffsetReductionAnimation() {
+ if (mFrameRenderedCallback != null || mViewportOffset.length() < EPSILON) {
+ return;
+ }
+
+ mFrameRenderedCallback = new Event.ParameterCallback<Boolean, Void>() {
+ private static final float DURATION_MS = 250.0f;
+
+ private final Interpolator mInterpolator = new DecelerateInterpolator();
+
+ private long mStartTime = 0;
+ private float mOriginalX = 0.0f;
+ private float mOriginalY = 0.0f;
+
+ @Override
+ public Boolean run(Void p) {
+ if (mFrameRenderedCallback == null) {
+ return false;
+ }
+
+ if (mStartTime == 0) {
+ mStartTime = SystemClock.elapsedRealtime();
+ mOriginalX = mViewportOffset.x;
+ mOriginalY = mViewportOffset.y;
+ }
+
+ float progress = (SystemClock.elapsedRealtime() - mStartTime) / DURATION_MS;
+ if (progress < 1.0f) {
+ float reductionFactor = 1.0f - mInterpolator.getInterpolation(progress);
+ mViewportOffset.set(mOriginalX * reductionFactor, mOriginalY * reductionFactor);
+ } else {
+ mViewportOffset.set(0.0f, 0.0f);
+ mFrameRenderedCallback = null;
+ }
+
+ repositionImage();
+
+ return mFrameRenderedCallback != null;
+ }
+ };
+
+ mRenderStub.onCanvasRendered().addSelfRemovable(mFrameRenderedCallback);
+ }
+
+ /**
+ * Stops an existing offset reduction animation.
+ */
+ private void stopOffsetReductionAnimation() {
+ // Setting this value this null will prevent it from continuing to execute.
+ mFrameRenderedCallback = null;
}
}
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698