| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.components.paintpreview.player.frame; |
| |
| import static org.mockito.Matchers.argThat; |
| import static org.mockito.Matchers.eq; |
| import static org.mockito.Mockito.verify; |
| |
| import android.app.Activity; |
| import android.content.Context; |
| import android.graphics.Bitmap; |
| import android.graphics.Matrix; |
| import android.graphics.Rect; |
| import android.os.Parcel; |
| import android.util.Pair; |
| import android.view.View; |
| import android.widget.OverScroller; |
| |
| import androidx.annotation.NonNull; |
| |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.ArgumentMatcher; |
| import org.mockito.Mockito; |
| import org.robolectric.Robolectric; |
| import org.robolectric.annotation.Config; |
| import org.robolectric.shadows.ShadowLooper; |
| import org.robolectric.shadows.ShadowView; |
| |
| import org.chromium.base.Callback; |
| import org.chromium.base.ContextUtils; |
| import org.chromium.base.UnguessableToken; |
| import org.chromium.base.test.BaseRobolectricTestRunner; |
| import org.chromium.components.paintpreview.player.PlayerCompositorDelegate; |
| import org.chromium.ui.modelutil.PropertyModel; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| /** |
| * Tests for the {@link PlayerFrameMediator} class. |
| */ |
| @RunWith(BaseRobolectricTestRunner.class) |
| @Config(shadows = {PaintPreviewCustomFlingingShadowScroller.class, ShadowView.class}) |
| public class PlayerFrameMediatorTest { |
| private static final int CONTENT_WIDTH = 560; |
| private static final int CONTENT_HEIGHT = 1150; |
| |
| private UnguessableToken mFrameGuid; |
| private PropertyModel mModel; |
| private TestPlayerCompositorDelegate mCompositorDelegate; |
| private OverScroller mScroller; |
| private boolean mHasUserInteraction; |
| private Runnable mUserInteractionCallback; |
| private PlayerFrameViewport mViewport; |
| private PlayerFrameMediator mMediator; |
| |
| /** |
| * Generate an UnguessableToken with a static value. |
| */ |
| private UnguessableToken frameGuid() { |
| // Use a parcel for testing to avoid calling the normal native constructor. |
| Parcel parcel = Parcel.obtain(); |
| parcel.writeLong(123321L); |
| parcel.writeLong(987654L); |
| parcel.setDataPosition(0); |
| return UnguessableToken.CREATOR.createFromParcel(parcel); |
| } |
| |
| /** |
| * Used for keeping track of all bitmap requests that {@link PlayerFrameMediator} makes. |
| */ |
| private class RequestedBitmap { |
| UnguessableToken mFrameGuid; |
| Rect mClipRect; |
| float mScaleFactor; |
| Callback<Bitmap> mBitmapCallback; |
| Runnable mErrorCallback; |
| |
| public RequestedBitmap(UnguessableToken frameGuid, Rect clipRect, float scaleFactor, |
| Callback<Bitmap> bitmapCallback, Runnable errorCallback) { |
| this.mFrameGuid = frameGuid; |
| this.mClipRect = clipRect; |
| this.mScaleFactor = scaleFactor; |
| this.mBitmapCallback = bitmapCallback; |
| this.mErrorCallback = errorCallback; |
| } |
| |
| public RequestedBitmap(UnguessableToken frameGuid, Rect clipRect, float scaleFactor) { |
| this.mFrameGuid = frameGuid; |
| this.mClipRect = clipRect; |
| this.mScaleFactor = scaleFactor; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (o == null) return false; |
| |
| if (o == this) return true; |
| |
| if (o.getClass() != this.getClass()) return false; |
| |
| RequestedBitmap rb = (RequestedBitmap) o; |
| return rb.mClipRect.equals(mClipRect) && rb.mFrameGuid.equals(mFrameGuid) |
| && rb.mScaleFactor == mScaleFactor; |
| } |
| |
| @NonNull |
| @Override |
| public String toString() { |
| return mFrameGuid + ", " + mClipRect + ", " + mScaleFactor; |
| } |
| } |
| |
| /** |
| * Used for keeping track of all click events that {@link PlayerFrameMediator} sends to |
| * {@link PlayerCompositorDelegate}. |
| */ |
| private class ClickedPoint { |
| UnguessableToken mFrameGuid; |
| int mX; |
| int mY; |
| |
| public ClickedPoint(UnguessableToken frameGuid, int x, int y) { |
| mFrameGuid = frameGuid; |
| this.mX = x; |
| this.mY = y; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (o == null) return false; |
| |
| if (o == this) return true; |
| |
| if (o.getClass() != this.getClass()) return false; |
| |
| ClickedPoint cp = (ClickedPoint) o; |
| return cp.mFrameGuid.equals(mFrameGuid) && cp.mX == mX && cp.mY == mY; |
| } |
| |
| @NonNull |
| @Override |
| public String toString() { |
| return "Click event for frame " + mFrameGuid.toString() + " on (" + mX + ", " + mY |
| + ")"; |
| } |
| } |
| |
| /** |
| * Mocks {@link PlayerCompositorDelegate}. Stores all bitmap requests as |
| * {@link RequestedBitmap}s. |
| */ |
| private class TestPlayerCompositorDelegate implements PlayerCompositorDelegate { |
| List<RequestedBitmap> mRequestedBitmap = new ArrayList<>(); |
| List<ClickedPoint> mClickedPoints = new ArrayList<>(); |
| |
| @Override |
| public void requestBitmap(UnguessableToken frameGuid, Rect clipRect, float scaleFactor, |
| Callback<Bitmap> bitmapCallback, Runnable errorCallback) { |
| mRequestedBitmap.add(new RequestedBitmap( |
| frameGuid, new Rect(clipRect), scaleFactor, bitmapCallback, errorCallback)); |
| } |
| |
| @Override |
| public void onClick(UnguessableToken frameGuid, int x, int y) { |
| mClickedPoints.add(new ClickedPoint(frameGuid, x, y)); |
| } |
| } |
| |
| private class MatrixMatcher implements ArgumentMatcher<Matrix> { |
| private Matrix mLeft; |
| |
| MatrixMatcher(Matrix left) { |
| mLeft = left; |
| } |
| |
| @Override |
| public boolean matches(Matrix right) { |
| return mLeft.equals(right); |
| } |
| } |
| |
| @Before |
| public void setUp() { |
| mFrameGuid = frameGuid(); |
| mModel = new PropertyModel.Builder(PlayerFrameProperties.ALL_KEYS).build(); |
| mCompositorDelegate = new TestPlayerCompositorDelegate(); |
| mScroller = new OverScroller(ContextUtils.getApplicationContext()); |
| mUserInteractionCallback = () -> mHasUserInteraction = true; |
| mViewport = new PlayerFrameViewport(); |
| mMediator = new PlayerFrameMediator(mModel, mCompositorDelegate, mViewport, mScroller, |
| mUserInteractionCallback, mFrameGuid, CONTENT_WIDTH, CONTENT_HEIGHT, 0, 0); |
| } |
| |
| private static Rect getRectForTile(int tileWidth, int tileHeight, int row, int col) { |
| int left = col * tileWidth; |
| int top = row * tileHeight; |
| return new Rect(left, top, left + tileWidth, top + tileHeight); |
| } |
| |
| private static List<Boolean> getVisibilities(List<View> views) { |
| List<Boolean> visibilities = new ArrayList<>(); |
| for (View view : views) { |
| visibilities.add(view.getVisibility() == View.VISIBLE); |
| } |
| return visibilities; |
| } |
| |
| private static void assertViewportStateIs(Matrix matrix, PlayerFrameViewport viewport) { |
| float matrixValues[] = new float[9]; |
| matrix.getValues(matrixValues); |
| assert matrixValues[Matrix.MSCALE_X] == matrixValues[Matrix.MSCALE_Y]; |
| assertViewportStateIs(matrixValues[Matrix.MSCALE_X], matrixValues[Matrix.MTRANS_X], |
| matrixValues[Matrix.MTRANS_Y], viewport); |
| } |
| |
| /** |
| * Asserts that the viewport's transformation state matches. |
| */ |
| private static void assertViewportStateIs(float expectedScaleFactor, float expectedX, |
| float expectedY, PlayerFrameViewport viewport) { |
| final float tolerance = 0.01f; |
| Assert.assertEquals(expectedScaleFactor, viewport.getScale(), tolerance); |
| Assert.assertEquals(expectedX, viewport.getTransX(), tolerance); |
| Assert.assertEquals(expectedY, viewport.getTransY(), tolerance); |
| } |
| |
| /** |
| * Tests that {@link PlayerFrameMediator} is initialized correctly on the first call to |
| * {@link PlayerFrameMediator#setLayoutDimensions}. |
| */ |
| @Test |
| public void testInitialLayoutDimensions() { |
| // Initial view port setup. |
| mMediator.setLayoutDimensions(150, 200); |
| |
| // View port should be as big as size set in the first setLayoutDimensions call, showing |
| // the top left corner. |
| Rect expectedViewPort = new Rect(0, 0, 150, 200); |
| Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT)); |
| |
| // The bitmap matrix should be empty, but initialized with the correct number of rows and |
| // columns. Because we set the initial scale factor to view port width over content width, |
| // we should have only one column. |
| Bitmap[][] bitmapMatrix = mModel.get(PlayerFrameProperties.BITMAP_MATRIX); |
| Assert.assertTrue(Arrays.deepEquals(bitmapMatrix, new Bitmap[2][1])); |
| Assert.assertEquals(new ArrayList<Pair<View, Rect>>(), |
| mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS)); |
| } |
| |
| /** |
| * Tests that {@link PlayerFrameMediator} requests for the right bitmap tiles as the view port |
| * moves. |
| */ |
| @Test |
| public void testBitmapRequest() { |
| // Initial view port setup. |
| mMediator.updateViewportSize(100, 200, 1f); |
| |
| // Requests for bitmaps in all tiles that are visible in the view port as well as their |
| // adjacent tiles should've been made. |
| // The current view port fully matches the top left bitmap tile, so we expect requests for |
| // the top left bitmap, one bitmap to its right, and one to its bottom. |
| // Below is a schematic of the entire bitmap matrix. Those marked with number should have |
| // been requested, in the order of numbers. |
| // ------------------------- |
| // | 1 | 3 | | | | | |
| // ------------------------- |
| // | 2 | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| List<RequestedBitmap> expectedRequestedBitmaps = new ArrayList<>(); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 0), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 0), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 1), 1f)); |
| Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap); |
| |
| mMediator.scrollBy(10, 20); |
| // The view port was moved with the #updateViewport call. It should've been updated in the |
| // model. |
| Rect expectedViewPort = new Rect(10, 20, 110, 220); |
| Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT)); |
| |
| // The current viewport covers portions of the 4 top left bitmap tiles. We have requested |
| // bitmaps for 3 of them before. Make sure requests for the 4th bitmap, as well adjacent |
| // bitmaps are made. |
| // Below is a schematic of the entire bitmap matrix. Those marked with number should have |
| // been requested, in the order of numbers. |
| // ------------------------- |
| // | x | x | 3 | | | | |
| // ------------------------- |
| // | x | 1 | 5 | | | | |
| // ------------------------- |
| // | 2 | 4 | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 1), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 0), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 2), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 1), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 2), 1f)); |
| Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap); |
| |
| // Move the view port slightly. It is still covered by the same 4 tiles. Since there were |
| // already bitmap requests out for those tiles and their adjacent tiles, we shouldn't have |
| // made new requests. |
| mMediator.scrollBy(10, 20); |
| Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap); |
| |
| // Move the view port to the bottom right so it covers portions of the 4 bottom right bitmap |
| // tiles. 4 new bitmap requests should be made. |
| // Below is a schematic of the entire bitmap matrix. Those marked with number should have |
| // been requested, in the order of numbers. |
| // ------------------------- |
| // | x | x | x | | | | |
| // ------------------------- |
| // | x | x | x | | | | |
| // ------------------------- |
| // | x | x | | | | | |
| // ------------------------- |
| // | | | | | 5 | 8 | |
| // ------------------------- |
| // | | | | 6 | 1 | 3 | |
| // ------------------------- |
| // | | | | 7 | 2 | 4 | |
| mMediator.scrollBy(430, 900); |
| expectedViewPort.set(450, 940, 550, 1140); |
| Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT)); |
| |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 4, 4), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 5, 4), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 4, 5), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 5, 5), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 3, 4), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 4, 3), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 5, 3), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 3, 5), 1f)); |
| Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap); |
| } |
| |
| /** |
| * Tests that the mediator keeps around the required bitmaps and removes the unrequired bitmaps |
| * when the view port changes. Required bitmaps are those in the viewport and its adjacent |
| * tiles. |
| */ |
| @Test |
| public void testRequiredBitmapMatrix() { |
| // Initial view port setup. |
| mMediator.updateViewportSize(100, 200, 1f); |
| |
| boolean[][] expectedRequiredBitmaps = new boolean[6][6]; |
| |
| // The current view port fully matches the top left bitmap tile. |
| // Below is a schematic of the entire bitmap matrix. Tiles marked with x are required for |
| // the current view port. |
| // ------------------------- |
| // | x | x | | | | | |
| // ------------------------- |
| // | x | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| expectedRequiredBitmaps[0][0] = true; |
| expectedRequiredBitmaps[0][1] = true; |
| expectedRequiredBitmaps[1][0] = true; |
| Assert.assertTrue(Arrays.deepEquals( |
| expectedRequiredBitmaps, mMediator.mBitmapState.mRequiredBitmaps)); |
| |
| mMediator.scrollBy(10, 15); |
| // The current viewport covers portions of the 4 top left bitmap tiles. |
| // ------------------------- |
| // | x | x | x | | | | |
| // ------------------------- |
| // | x | x | x | | | | |
| // ------------------------- |
| // | x | x | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| expectedRequiredBitmaps[0][2] = true; |
| expectedRequiredBitmaps[1][1] = true; |
| expectedRequiredBitmaps[1][2] = true; |
| expectedRequiredBitmaps[2][0] = true; |
| expectedRequiredBitmaps[2][1] = true; |
| Assert.assertTrue(Arrays.deepEquals( |
| expectedRequiredBitmaps, mMediator.mBitmapState.mRequiredBitmaps)); |
| |
| mMediator.scrollBy(200, 400); |
| // The current view port contains portions of the middle 4 tiles. |
| // Tiles marked with x are required for the current view port. |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | x | x | | | |
| // ------------------------- |
| // | | x | x | x | x | | |
| // ------------------------- |
| // | | x | x | x | x | | |
| // ------------------------- |
| // | | | x | x | | | |
| // ------------------------- |
| // | | | | | | | |
| expectedRequiredBitmaps[0][0] = false; |
| expectedRequiredBitmaps[0][1] = false; |
| expectedRequiredBitmaps[0][2] = false; |
| expectedRequiredBitmaps[1][0] = false; |
| expectedRequiredBitmaps[1][1] = false; |
| expectedRequiredBitmaps[2][0] = false; |
| expectedRequiredBitmaps[1][3] = true; |
| expectedRequiredBitmaps[2][2] = true; |
| expectedRequiredBitmaps[2][3] = true; |
| expectedRequiredBitmaps[2][4] = true; |
| expectedRequiredBitmaps[3][1] = true; |
| expectedRequiredBitmaps[3][2] = true; |
| expectedRequiredBitmaps[3][3] = true; |
| expectedRequiredBitmaps[3][4] = true; |
| expectedRequiredBitmaps[4][2] = true; |
| expectedRequiredBitmaps[4][3] = true; |
| Assert.assertTrue(Arrays.deepEquals( |
| expectedRequiredBitmaps, mMediator.mBitmapState.mRequiredBitmaps)); |
| |
| mMediator.scrollBy(200, 400); |
| // The current view port contains portions of the 4 bottom right tiles. |
| // Tiles marked with x are required for the current view port. |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | x | x | |
| // ------------------------- |
| // | | | | x | x | x | |
| // ------------------------- |
| // | | | | x | x | x | |
| expectedRequiredBitmaps[1][2] = false; |
| expectedRequiredBitmaps[1][3] = false; |
| expectedRequiredBitmaps[2][1] = false; |
| expectedRequiredBitmaps[2][2] = false; |
| expectedRequiredBitmaps[2][3] = false; |
| expectedRequiredBitmaps[2][4] = false; |
| expectedRequiredBitmaps[3][1] = false; |
| expectedRequiredBitmaps[3][2] = false; |
| expectedRequiredBitmaps[3][3] = false; |
| expectedRequiredBitmaps[4][2] = false; |
| |
| expectedRequiredBitmaps[3][5] = true; |
| expectedRequiredBitmaps[4][4] = true; |
| expectedRequiredBitmaps[4][5] = true; |
| expectedRequiredBitmaps[5][3] = true; |
| expectedRequiredBitmaps[5][4] = true; |
| expectedRequiredBitmaps[5][5] = true; |
| Assert.assertTrue(Arrays.deepEquals( |
| expectedRequiredBitmaps, mMediator.mBitmapState.mRequiredBitmaps)); |
| } |
| |
| /** |
| * Mocks responses on bitmap requests from {@link PlayerFrameMediator} and tests those responses |
| * are correctly handled. |
| */ |
| @Test |
| public void testBitmapRequestResponse() { |
| // Sets the bitmap tile size to 150x200 and triggers bitmap request for the upper left tile |
| // and its adjacent tiles. |
| mMediator.updateViewportSize(150, 200, 1f); |
| |
| // Create mock bitmaps for response. |
| Bitmap bitmap00 = Mockito.mock(Bitmap.class); |
| Bitmap bitmap10 = Mockito.mock(Bitmap.class); |
| Bitmap bitmap20 = Mockito.mock(Bitmap.class); |
| Bitmap bitmap01 = Mockito.mock(Bitmap.class); |
| Bitmap bitmap11 = Mockito.mock(Bitmap.class); |
| Bitmap bitmap21 = Mockito.mock(Bitmap.class); |
| Bitmap bitmap02 = Mockito.mock(Bitmap.class); |
| Bitmap bitmap12 = Mockito.mock(Bitmap.class); |
| |
| Bitmap[][] expectedBitmapMatrix = new Bitmap[6][4]; |
| expectedBitmapMatrix[0][0] = bitmap00; |
| expectedBitmapMatrix[1][0] = bitmap10; |
| expectedBitmapMatrix[0][1] = bitmap01; |
| |
| // Call the request callback with mock bitmaps and assert they're added to the model. |
| mCompositorDelegate.mRequestedBitmap.get(0).mBitmapCallback.onResult(bitmap00); |
| mCompositorDelegate.mRequestedBitmap.get(1).mBitmapCallback.onResult(bitmap10); |
| mCompositorDelegate.mRequestedBitmap.get(2).mBitmapCallback.onResult(bitmap01); |
| Assert.assertTrue(Arrays.deepEquals( |
| expectedBitmapMatrix, mModel.get(PlayerFrameProperties.BITMAP_MATRIX))); |
| |
| // Move the viewport to an area that is covered by 4 top left tiles. |
| mMediator.scrollBy(10, 10); |
| |
| // Scroll should've triggered bitmap requests for an the 4th new tile as well as adjacent |
| // tiles. See comments on {@link #testBitmapRequest} for details on which tiles will be |
| // requested. |
| // Call the request callback with mock bitmaps and assert they're added to the model. |
| expectedBitmapMatrix[1][1] = bitmap11; |
| expectedBitmapMatrix[0][2] = bitmap02; |
| expectedBitmapMatrix[2][1] = bitmap21; |
| expectedBitmapMatrix[1][2] = bitmap12; |
| mCompositorDelegate.mRequestedBitmap.get(3).mBitmapCallback.onResult(bitmap11); |
| // Mock a compositing failure for this tile. No bitmaps should be added. |
| mCompositorDelegate.mRequestedBitmap.get(4).mErrorCallback.run(); |
| mCompositorDelegate.mRequestedBitmap.get(5).mBitmapCallback.onResult(bitmap02); |
| mCompositorDelegate.mRequestedBitmap.get(6).mBitmapCallback.onResult(bitmap21); |
| mCompositorDelegate.mRequestedBitmap.get(7).mBitmapCallback.onResult(bitmap12); |
| Assert.assertTrue(Arrays.deepEquals( |
| expectedBitmapMatrix, mModel.get(PlayerFrameProperties.BITMAP_MATRIX))); |
| |
| // Assert 8 bitmap requests have been made in total. |
| Assert.assertEquals(8, mCompositorDelegate.mRequestedBitmap.size()); |
| |
| // Move the view port while staying within the 4 bitmap tiles in order to trigger the |
| // request logic again. Make sure only one new request is added, for the tile with a |
| // compositing failure. |
| mMediator.scrollBy(10, 10); |
| Assert.assertEquals(9, mCompositorDelegate.mRequestedBitmap.size()); |
| Assert.assertEquals(new RequestedBitmap(mFrameGuid, getRectForTile(150, 200, 2, 0), 1f), |
| mCompositorDelegate.mRequestedBitmap.get( |
| mCompositorDelegate.mRequestedBitmap.size() - 1)); |
| } |
| |
| /** |
| * View port should be updated on scroll events, but it shouldn't go out of content bounds. |
| */ |
| @Test |
| public void testViewPortOnScrollBy() { |
| // Initial view port setup. |
| mMediator.updateViewportSize(100, 200, 1f); |
| Rect expectedViewPort = new Rect(0, 0, 100, 200); |
| |
| // Scroll right and down by a within bounds amount. Both scroll directions should be |
| // effective. |
| Assert.assertTrue(mMediator.scrollBy(250f, 80f)); |
| expectedViewPort.offset(250, 80); |
| Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT)); |
| |
| // Scroll by an out of bounds horizontal value. Should be scrolled to the rightmost point. |
| Assert.assertTrue(mMediator.scrollBy(1000f, 50f)); |
| expectedViewPort.offset(210, 50); |
| Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT)); |
| |
| // Scroll by an out of bounds horizontal and vertical value. |
| // Should be scrolled to the bottom right point. |
| Assert.assertTrue(mMediator.scrollBy(600f, 5000f)); |
| expectedViewPort.offset(0, 820); |
| Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT)); |
| |
| // Scroll right and down. Should be impossible since we're already at the bottom right |
| // point. |
| Assert.assertFalse(mMediator.scrollBy(10f, 15f)); |
| expectedViewPort.offset(0, 0); |
| Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT)); |
| |
| // Scroll right and up. Horizontal scroll should be ignored and should be scrolled to the |
| // top. |
| Assert.assertTrue(mMediator.scrollBy(100f, -2000f)); |
| expectedViewPort.offset(0, -950); |
| Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT)); |
| |
| // Scroll right and up. Both scroll directions should be ignored. |
| Assert.assertFalse(mMediator.scrollBy(100f, -2000f)); |
| expectedViewPort.offset(0, 0); |
| Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT)); |
| |
| // Scroll left and up. Vertical scroll should be ignored and should be scrolled to the |
| // left. |
| Assert.assertTrue(mMediator.scrollBy(-1000f, -2000f)); |
| expectedViewPort.offset(-460, 0); |
| Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT)); |
| |
| // Scroll left and up. Both scroll directions should be ignored. |
| Assert.assertFalse(mMediator.scrollBy(-1000f, -2000f)); |
| expectedViewPort.offset(0, 0); |
| Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT)); |
| |
| // Scroll left and down. Horizontal scroll should be ignored and should be scrolled to the |
| // bottom. |
| Assert.assertTrue(mMediator.scrollBy(-1000f, 2000f)); |
| expectedViewPort.offset(0, 950); |
| Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT)); |
| |
| // Scroll left and down. Both scroll directions should be ignored. |
| Assert.assertFalse(mMediator.scrollBy(-1000f, 2000f)); |
| expectedViewPort.offset(0, 0); |
| Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT)); |
| |
| // Scroll right and up. Both scroll values should be reflected. |
| Assert.assertTrue(mMediator.scrollBy(200, -100)); |
| expectedViewPort.offset(200, -100); |
| Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT)); |
| } |
| |
| /** |
| * Tests sub-frames' visibility when view port changes. sub-frames that are out of the view |
| * port's bounds should not be added to the model. |
| */ |
| @Test |
| public void testSubFramesPosition() { |
| Context context = Robolectric.buildActivity(Activity.class).get(); |
| View subFrame1View = new View(context); |
| View subFrame2View = new View(context); |
| View subFrame3View = new View(context); |
| |
| Pair<View, Rect> subFrame1 = new Pair<>(subFrame1View, new Rect(10, 20, 60, 120)); |
| Pair<View, Rect> subFrame2 = new Pair<>(subFrame2View, new Rect(30, 130, 70, 160)); |
| Pair<View, Rect> subFrame3 = new Pair<>(subFrame3View, new Rect(120, 35, 150, 65)); |
| |
| mMediator.addSubFrame( |
| subFrame1.first, subFrame1.second, Mockito.mock(PlayerFrameMediator.class)); |
| mMediator.addSubFrame( |
| subFrame2.first, subFrame2.second, Mockito.mock(PlayerFrameMediator.class)); |
| mMediator.addSubFrame( |
| subFrame3.first, subFrame3.second, Mockito.mock(PlayerFrameMediator.class)); |
| |
| // Initial view port setup. |
| mMediator.updateViewportSize(100, 200, 1f); |
| List<View> expectedViews = new ArrayList<>(); |
| List<Rect> expectedRects = new ArrayList<>(); |
| List<Boolean> expectedVisibility = new ArrayList<>(); |
| expectedViews.add(subFrame1.first); |
| expectedViews.add(subFrame2.first); |
| expectedViews.add(subFrame3.first); |
| expectedRects.add(subFrame1.second); |
| expectedRects.add(subFrame2.second); |
| expectedRects.add(subFrame3.second); |
| expectedVisibility.add(true); |
| expectedVisibility.add(true); |
| expectedVisibility.add(false); |
| Assert.assertEquals(expectedViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS)); |
| Assert.assertEquals(expectedRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS)); |
| Assert.assertEquals(expectedVisibility, |
| getVisibilities(mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS))); |
| |
| mMediator.scrollBy(100, 0); |
| expectedRects.set(2, new Rect(20, 35, 50, 65)); |
| expectedVisibility.clear(); |
| expectedVisibility.add(false); |
| expectedVisibility.add(false); |
| expectedVisibility.add(true); |
| Assert.assertEquals(expectedViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS)); |
| Assert.assertEquals(expectedRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS)); |
| Assert.assertEquals(expectedVisibility, |
| getVisibilities(mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS))); |
| |
| mMediator.scrollBy(-50, 0); |
| expectedRects.clear(); |
| expectedRects.add(new Rect(-40, 20, 10, 120)); |
| expectedRects.add(new Rect(-20, 130, 20, 160)); |
| expectedRects.add(new Rect(70, 35, 100, 65)); |
| expectedVisibility.clear(); |
| expectedVisibility.add(true); |
| expectedVisibility.add(true); |
| expectedVisibility.add(true); |
| Assert.assertEquals(expectedViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS)); |
| Assert.assertEquals(expectedRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS)); |
| Assert.assertEquals(expectedVisibility, |
| getVisibilities(mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS))); |
| |
| mMediator.scrollBy(0, 200); |
| expectedRects.clear(); |
| expectedRects.add(subFrame1.second); |
| expectedRects.add(subFrame2.second); |
| expectedRects.add(subFrame3.second); |
| expectedVisibility.clear(); |
| expectedVisibility.add(false); |
| expectedVisibility.add(false); |
| expectedVisibility.add(false); |
| Assert.assertEquals(expectedViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS)); |
| Assert.assertEquals(expectedRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS)); |
| Assert.assertEquals(expectedVisibility, |
| getVisibilities(mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS))); |
| } |
| |
| /** |
| * View port should be updated on fling events, but it shouldn't go out of content bounds. |
| */ |
| @Test |
| public void testViewPortOnFling() { |
| // Initial view port setup. |
| mMediator.updateViewportSize(100, 200, 1f); |
| Rect expectedViewPort = new Rect(0, 0, 100, 200); |
| |
| mMediator.onFling(100, 0); |
| expectedViewPort.offsetTo(mScroller.getFinalX(), mScroller.getFinalY()); |
| ShadowLooper.runUiThreadTasks(); |
| Assert.assertTrue(mScroller.isFinished()); |
| Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT)); |
| |
| mMediator.onFling(-100, 0); |
| expectedViewPort.offsetTo(mScroller.getFinalX(), mScroller.getFinalY()); |
| ShadowLooper.runUiThreadTasks(); |
| Assert.assertTrue(mScroller.isFinished()); |
| Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT)); |
| |
| mMediator.onFling(0, 200); |
| expectedViewPort.offsetTo(mScroller.getFinalX(), mScroller.getFinalY()); |
| ShadowLooper.runUiThreadTasks(); |
| Assert.assertTrue(mScroller.isFinished()); |
| Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT)); |
| |
| mMediator.onFling(0, -200); |
| expectedViewPort.offsetTo(mScroller.getFinalX(), mScroller.getFinalY()); |
| ShadowLooper.runUiThreadTasks(); |
| Assert.assertTrue(mScroller.isFinished()); |
| Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT)); |
| |
| mMediator.onFling(100, 200); |
| expectedViewPort.offsetTo(mScroller.getFinalX(), mScroller.getFinalY()); |
| ShadowLooper.runUiThreadTasks(); |
| Assert.assertTrue(mScroller.isFinished()); |
| Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT)); |
| |
| mMediator.onFling(-100, -200); |
| expectedViewPort.offsetTo(mScroller.getFinalX(), mScroller.getFinalY()); |
| ShadowLooper.runUiThreadTasks(); |
| Assert.assertTrue(mScroller.isFinished()); |
| Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT)); |
| } |
| |
| /** |
| * Tests that {@link PlayerFrameMediator} correctly relays the click events to |
| * {@link PlayerCompositorDelegate} and accounts for scroll offsets. |
| */ |
| @Test |
| public void testOnClick() { |
| // Initial view port setup. |
| mMediator.updateViewportSize(100, 200, 1f); |
| List<ClickedPoint> expectedClickedPoints = new ArrayList<>(); |
| |
| // No scrolling has happened yet. |
| mMediator.onClick(15, 26); |
| expectedClickedPoints.add(new ClickedPoint(mFrameGuid, 15, 26)); |
| Assert.assertEquals(expectedClickedPoints, mCompositorDelegate.mClickedPoints); |
| |
| // Scroll, and then click. The call to {@link PlayerFrameMediator} must account for the |
| // scroll offset. |
| mMediator.scrollBy(90, 100); |
| mMediator.onClick(70, 50); |
| expectedClickedPoints.add(new ClickedPoint(mFrameGuid, 160, 150)); |
| Assert.assertEquals(expectedClickedPoints, mCompositorDelegate.mClickedPoints); |
| |
| mMediator.scrollBy(-40, -60); |
| mMediator.onClick(30, 80); |
| expectedClickedPoints.add(new ClickedPoint(mFrameGuid, 80, 120)); |
| Assert.assertEquals(expectedClickedPoints, mCompositorDelegate.mClickedPoints); |
| } |
| |
| /** |
| * Tests that {@link PlayerFrameMediator} correctly consumes scale events and scales between |
| * the allowed range. |
| */ |
| @Test |
| public void testViewPortOnScaleBy() { |
| // Initial view port setup. |
| mMediator.setLayoutDimensions(100, 200); |
| mMediator.updateViewportSize(100, 200, 1f); |
| |
| // The current view port fully matches the top left bitmap tile. |
| // Below is a schematic of the entire bitmap matrix. Tiles marked with x are required for |
| // the current view port. |
| // ------------------------- |
| // | x | x | | | | | |
| // ------------------------- |
| // | x | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| boolean[][] expectedRequiredBitmaps = new boolean[6][6]; |
| expectedRequiredBitmaps[0][0] = true; |
| expectedRequiredBitmaps[0][1] = true; |
| expectedRequiredBitmaps[1][0] = true; |
| Assert.assertTrue(Arrays.deepEquals( |
| expectedRequiredBitmaps, mMediator.mBitmapState.mRequiredBitmaps)); |
| |
| // Now a scale factor of 2 will be applied. This will happen at a focal point of 0, 0. |
| // The same bitmaps will be required but the grid will be double the size. |
| Assert.assertTrue(mMediator.scaleBy(2f, 0, 0)); |
| Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0)); |
| |
| expectedRequiredBitmaps = new boolean[12][12]; |
| expectedRequiredBitmaps[0][0] = true; |
| expectedRequiredBitmaps[0][1] = true; |
| expectedRequiredBitmaps[1][0] = true; |
| Assert.assertTrue(Arrays.deepEquals( |
| expectedRequiredBitmaps, mMediator.mBitmapState.mRequiredBitmaps)); |
| |
| // Reduce the scale factor by 0.5 returning to a scale of 1. |
| Assert.assertTrue(mMediator.scaleBy(0.5f, 0, 0)); |
| Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0)); |
| |
| expectedRequiredBitmaps = new boolean[6][6]; |
| expectedRequiredBitmaps[0][0] = true; |
| expectedRequiredBitmaps[0][1] = true; |
| expectedRequiredBitmaps[1][0] = true; |
| Assert.assertTrue(Arrays.deepEquals( |
| expectedRequiredBitmaps, mMediator.mBitmapState.mRequiredBitmaps)); |
| |
| // Increase the scale factor to 6 which is above the maximum limit returning to a scale |
| // of 5. Note that the grid is smaller than 30x30 as the viewport is not a multiple of the |
| // content width and height so there is difference. |
| Assert.assertTrue(mMediator.scaleBy(6f, 0, 0)); |
| Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0)); |
| |
| expectedRequiredBitmaps = new boolean[29][28]; |
| expectedRequiredBitmaps[0][0] = true; |
| expectedRequiredBitmaps[0][1] = true; |
| expectedRequiredBitmaps[1][0] = true; |
| Assert.assertTrue(Arrays.deepEquals( |
| expectedRequiredBitmaps, mMediator.mBitmapState.mRequiredBitmaps)); |
| |
| // Reduce the scale factor back to 1. |
| Assert.assertTrue(mMediator.scaleBy(0.2f, 0, 0)); |
| Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0)); |
| |
| expectedRequiredBitmaps = new boolean[6][6]; |
| expectedRequiredBitmaps[0][0] = true; |
| expectedRequiredBitmaps[0][1] = true; |
| expectedRequiredBitmaps[1][0] = true; |
| Assert.assertTrue(Arrays.deepEquals( |
| expectedRequiredBitmaps, mMediator.mBitmapState.mRequiredBitmaps)); |
| |
| // We now reduce the scale factor to less than mInitialScaleFactor; however, the maximum |
| // scale out is limited to mInitialScaleFactor. |
| float initialScaleFactor = 100f / 560f; |
| Assert.assertTrue(mMediator.scaleBy(initialScaleFactor, 0, 0)); |
| Assert.assertTrue(mMediator.scaleBy(0.5f, 0, 0)); |
| Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0)); |
| |
| expectedRequiredBitmaps = new boolean[2][1]; |
| expectedRequiredBitmaps[0][0] = true; |
| expectedRequiredBitmaps[1][0] = true; |
| Assert.assertTrue(Arrays.deepEquals( |
| expectedRequiredBitmaps, mMediator.mBitmapState.mRequiredBitmaps)); |
| } |
| |
| /** |
| * Tests that {@link PlayerFrameMediator} works correctly when scrolling. |
| */ |
| @Test |
| public void testViewPortOnScaleByWithScroll() { |
| // Initial view port setup. |
| mMediator.updateViewportSize(100, 200, 1f); |
| |
| boolean[][] expectedRequiredBitmaps = new boolean[6][6]; |
| |
| // STEP 1: Original request. |
| // The current view port fully matches the top left bitmap tile. |
| // Below is a schematic of the entire bitmap matrix. Tiles marked with x are required for |
| // the current view port. |
| // ------------------------- |
| // | x | x | | | | | |
| // ------------------------- |
| // | x | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| expectedRequiredBitmaps[0][0] = true; |
| expectedRequiredBitmaps[0][1] = true; |
| expectedRequiredBitmaps[1][0] = true; |
| List<RequestedBitmap> expectedRequestedBitmaps = new ArrayList<>(); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 0), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 0), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 1), 1f)); |
| |
| // Both matricies should be identity to start. |
| assertViewportStateIs(1f, 0f, 0f, mViewport); |
| Assert.assertTrue(mModel.get(PlayerFrameProperties.SCALE_MATRIX).isIdentity()); |
| // Ensure the correct bitmaps are required and requested. |
| Assert.assertTrue(Arrays.deepEquals( |
| expectedRequiredBitmaps, mMediator.mBitmapState.mRequiredBitmaps)); |
| Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap); |
| |
| // STEP 2: Scroll slightly. |
| mMediator.scrollBy(10, 15); |
| // The current viewport covers portions of the 4 top left bitmap tiles. |
| // ------------------------- |
| // | x | x | x | | | | |
| // ------------------------- |
| // | x | x | x | | | | |
| // ------------------------- |
| // | x | x | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| expectedRequiredBitmaps[0][2] = true; |
| expectedRequiredBitmaps[1][1] = true; |
| expectedRequiredBitmaps[1][2] = true; |
| expectedRequiredBitmaps[2][0] = true; |
| expectedRequiredBitmaps[2][1] = true; |
| Assert.assertTrue(Arrays.deepEquals( |
| expectedRequiredBitmaps, mMediator.mBitmapState.mRequiredBitmaps)); |
| |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 1), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 0), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 2), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 1), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 2), 1f)); |
| Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap); |
| |
| // The viewport matrix should track scroll and zoom. |
| Matrix expectedViewportMatrix = new Matrix(); |
| float[] expectedViewportMatrixValues = new float[9]; |
| expectedViewportMatrix.getValues(expectedViewportMatrixValues); |
| expectedViewportMatrixValues[Matrix.MTRANS_X] = 10; |
| expectedViewportMatrixValues[Matrix.MTRANS_Y] = 15; |
| expectedViewportMatrix.setValues(expectedViewportMatrixValues); |
| |
| assertViewportStateIs(expectedViewportMatrix, mViewport); |
| Assert.assertTrue(mModel.get(PlayerFrameProperties.SCALE_MATRIX).isIdentity()); |
| |
| // STEP 3: Now a scale factor of 2 will be applied. This will happen at a focal point of 50, |
| // 100. |
| Assert.assertTrue(mMediator.scaleBy(2f, 50f, 100f)); |
| |
| // Before the scaling commits both matricies should update. |
| expectedViewportMatrix.postScale(2f, 2f, -50f, -100f); |
| Matrix expectedBitmapMatrix = new Matrix(); |
| expectedBitmapMatrix.postScale(2f, 2f, 50f, 100f); |
| assertViewportStateIs(expectedViewportMatrix, mViewport); |
| Assert.assertEquals(expectedBitmapMatrix, mModel.get(PlayerFrameProperties.SCALE_MATRIX)); |
| |
| // Bitmaps should be the same as before scaling until scaling is finished. |
| Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap); |
| mCompositorDelegate.mRequestedBitmap.clear(); |
| expectedRequestedBitmaps.clear(); |
| |
| Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0)); |
| |
| expectedRequiredBitmaps = new boolean[12][12]; |
| expectedRequiredBitmaps[0][0] = true; |
| expectedRequiredBitmaps[0][1] = true; |
| expectedRequiredBitmaps[1][0] = true; |
| expectedRequiredBitmaps[0][2] = true; |
| expectedRequiredBitmaps[1][1] = true; |
| expectedRequiredBitmaps[1][2] = true; |
| expectedRequiredBitmaps[2][0] = true; |
| expectedRequiredBitmaps[2][1] = true; |
| Assert.assertTrue(Arrays.deepEquals( |
| expectedRequiredBitmaps, mMediator.mBitmapState.mRequiredBitmaps)); |
| |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 0), 2f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 0), 2f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 1), 2f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 1), 2f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 0), 2f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 2), 2f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 1), 2f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 2), 2f)); |
| Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap); |
| |
| // The bitmap matrix should be cleared. |
| expectedBitmapMatrix.reset(); |
| Assert.assertTrue(mModel.get(PlayerFrameProperties.SCALE_MATRIX).isIdentity()); |
| |
| // STEP4: Now a scale factor of 0.5 will be applied. This will happen at a focal point of |
| // 50, 100. |
| Assert.assertTrue(mMediator.scaleBy(0.5f, 50f, 100f)); |
| |
| // Ensure the matricies are correct mid-scale. |
| expectedViewportMatrix.postScale(0.5f, 0.5f, -50f, -100f); |
| expectedBitmapMatrix.postScale(0.5f, 0.5f, 50f, 100f); |
| assertViewportStateIs(expectedViewportMatrix, mViewport); |
| Assert.assertEquals(expectedBitmapMatrix, mModel.get(PlayerFrameProperties.SCALE_MATRIX)); |
| |
| // Bitmaps should be the same as before scaling until scaling is finished. |
| Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap); |
| mCompositorDelegate.mRequestedBitmap.clear(); |
| expectedRequestedBitmaps.clear(); |
| |
| Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0)); |
| |
| expectedRequiredBitmaps = new boolean[6][6]; |
| expectedRequiredBitmaps[0][0] = true; |
| expectedRequiredBitmaps[0][1] = true; |
| expectedRequiredBitmaps[1][0] = true; |
| expectedRequiredBitmaps[0][2] = true; |
| expectedRequiredBitmaps[1][1] = true; |
| expectedRequiredBitmaps[1][2] = true; |
| expectedRequiredBitmaps[2][0] = true; |
| expectedRequiredBitmaps[2][1] = true; |
| Assert.assertTrue(Arrays.deepEquals( |
| expectedRequiredBitmaps, mMediator.mBitmapState.mRequiredBitmaps)); |
| |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 0), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 0), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 1), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 1), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 0), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 2), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 1), 1f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 2), 1f)); |
| Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap); |
| |
| expectedBitmapMatrix.reset(); |
| Assert.assertTrue(mModel.get(PlayerFrameProperties.SCALE_MATRIX).isIdentity()); |
| |
| // Now a scale factor of 2 will be applied. This will happen at a focal point of 100, 200. |
| // Due to the position of the focal point the required bitmaps will move. |
| // ------------------------- |
| // | | x | x | | | | |
| // ------------------------- |
| // | x | x | x | x | | | |
| // ------------------------- |
| // | x | x | x | x | | | |
| // ------------------------- |
| // | | x | x | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| Assert.assertTrue(mMediator.scaleBy(2f, 100f, 200f)); |
| |
| expectedViewportMatrix.postScale(2f, 2f, -100f, -200f); |
| expectedBitmapMatrix.postScale(2f, 2f, 100f, 200f); |
| assertViewportStateIs(expectedViewportMatrix, mViewport); |
| Assert.assertEquals(expectedBitmapMatrix, mModel.get(PlayerFrameProperties.SCALE_MATRIX)); |
| |
| Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap); |
| mCompositorDelegate.mRequestedBitmap.clear(); |
| expectedRequestedBitmaps.clear(); |
| |
| Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0)); |
| |
| expectedRequiredBitmaps = new boolean[12][12]; |
| expectedRequiredBitmaps[0][1] = true; |
| expectedRequiredBitmaps[0][2] = true; |
| expectedRequiredBitmaps[1][0] = true; |
| expectedRequiredBitmaps[1][1] = true; |
| expectedRequiredBitmaps[1][2] = true; |
| expectedRequiredBitmaps[1][3] = true; |
| expectedRequiredBitmaps[2][0] = true; |
| expectedRequiredBitmaps[2][1] = true; |
| expectedRequiredBitmaps[2][2] = true; |
| expectedRequiredBitmaps[2][3] = true; |
| expectedRequiredBitmaps[3][1] = true; |
| expectedRequiredBitmaps[3][2] = true; |
| Assert.assertTrue(Arrays.deepEquals( |
| expectedRequiredBitmaps, mMediator.mBitmapState.mRequiredBitmaps)); |
| |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 1), 2f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 1), 2f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 2), 2f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 2), 2f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 1), 2f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 0), 2f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 3, 1), 2f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 0), 2f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 2), 2f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 3), 2f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 3, 2), 2f)); |
| expectedRequestedBitmaps.add( |
| new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 3), 2f)); |
| Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap); |
| |
| expectedBitmapMatrix.reset(); |
| Assert.assertTrue(mModel.get(PlayerFrameProperties.SCALE_MATRIX).isIdentity()); |
| } |
| |
| /** |
| * Tests that {@link PlayerFrameMediator} works correctly when subframes are present. |
| */ |
| @Test |
| public void testViewPortOnScaleByWithSubFrames() { |
| Context context = Robolectric.buildActivity(Activity.class).get(); |
| View subFrame1View = new View(context); |
| View subFrame2View = new View(context); |
| |
| PlayerFrameMediator subFrame1Mediator = Mockito.mock(PlayerFrameMediator.class); |
| Pair<View, Rect> subFrame1 = new Pair<>(subFrame1View, new Rect(10, 20, 60, 40)); |
| PlayerFrameMediator subFrame2Mediator = Mockito.mock(PlayerFrameMediator.class); |
| Pair<View, Rect> subFrame2 = new Pair<>(subFrame2View, new Rect(30, 50, 70, 160)); |
| |
| mMediator.addSubFrame(subFrame1.first, subFrame1.second, subFrame1Mediator); |
| mMediator.addSubFrame(subFrame2.first, subFrame2.second, subFrame2Mediator); |
| |
| // Both subframes should be visible. |
| mMediator.updateViewportSize(50, 100, 1f); |
| List<View> expectedViews = new ArrayList<>(); |
| List<Rect> expectedRects = new ArrayList<>(); |
| List<Boolean> expectedVisibility = new ArrayList<>(); |
| expectedViews.add(subFrame1.first); |
| expectedViews.add(subFrame2.first); |
| expectedRects.add(subFrame1.second); |
| expectedRects.add(subFrame2.second); |
| expectedVisibility.add(true); |
| expectedVisibility.add(true); |
| Assert.assertEquals(expectedViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS)); |
| Assert.assertEquals(expectedRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS)); |
| Assert.assertEquals(expectedVisibility, |
| getVisibilities(mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS))); |
| |
| expectedRects.clear(); |
| expectedRects.add(new Rect(20, 40, 120, 80)); |
| expectedRects.add(new Rect(60, 100, 140, 320)); |
| expectedVisibility.set(1, false); |
| |
| // During scaling the second subframe should disappear from the viewport. |
| Assert.assertTrue(mMediator.scaleBy(2f, 0f, 0f)); |
| Assert.assertEquals(expectedViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS)); |
| Assert.assertEquals(expectedRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS)); |
| Assert.assertEquals(expectedVisibility, |
| getVisibilities(mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS))); |
| Matrix expectedMatrix = new Matrix(); |
| expectedMatrix.setScale(2f, 2f); |
| verify(subFrame1Mediator) |
| .setBitmapScaleMatrixOfSubframe(argThat(new MatrixMatcher(expectedMatrix)), eq(2f)); |
| |
| Assert.assertTrue(mMediator.scaleFinished(1f, 0f, 0f)); |
| Assert.assertEquals(expectedViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS)); |
| Assert.assertEquals(expectedRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS)); |
| Assert.assertEquals(expectedVisibility, |
| getVisibilities(mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS))); |
| expectedMatrix.reset(); |
| verify(subFrame1Mediator) |
| .setBitmapScaleMatrixOfSubframe(argThat(new MatrixMatcher(expectedMatrix)), eq(1f)); |
| verify(subFrame1Mediator).forceRedraw(); |
| verify(subFrame1Mediator).resetScaleFactor(); |
| |
| // Scroll so the second subframe is back in the viewport.. |
| mMediator.scrollBy(20, 40); |
| expectedRects.clear(); |
| expectedRects.add(new Rect(0, 0, 100, 40)); |
| expectedRects.add(new Rect(40, 60, 120, 280)); |
| expectedVisibility.clear(); |
| expectedVisibility.add(true); |
| expectedVisibility.add(true); |
| Assert.assertEquals(expectedViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS)); |
| Assert.assertEquals(expectedRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS)); |
| Assert.assertEquals(expectedVisibility, |
| getVisibilities(mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS))); |
| |
| // Scale out keeping the subframes in the viewport.. |
| Assert.assertTrue(mMediator.scaleBy(0.75f, 25f, 50f)); |
| expectedRects.clear(); |
| expectedRects.add(new Rect(6, 12, 81, 42)); |
| expectedRects.add(new Rect(36, 57, 96, 222)); |
| Assert.assertEquals(expectedViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS)); |
| Assert.assertEquals(expectedRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS)); |
| Assert.assertEquals(expectedVisibility, |
| getVisibilities(mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS))); |
| expectedMatrix.setScale(0.75f, 0.75f); |
| verify(subFrame1Mediator) |
| .setBitmapScaleMatrixOfSubframe( |
| argThat(new MatrixMatcher(expectedMatrix)), eq(1.5f)); |
| verify(subFrame2Mediator) |
| .setBitmapScaleMatrixOfSubframe( |
| argThat(new MatrixMatcher(expectedMatrix)), eq(1.5f)); |
| } |
| |
| /** |
| * Tests that {@link PlayerFrameMediator} correctly scales and keeps the content in bounds. |
| */ |
| @Test |
| public void testViewPortOnScaleByWithinBounds() { |
| // Initial view port setup. |
| mMediator.updateViewportSize(100, 200, 1f); |
| |
| boolean[][] expectedRequiredBitmaps = new boolean[6][6]; |
| |
| // The current view port fully matches the top left bitmap tile. |
| // Below is a schematic of the entire bitmap matrix. Tiles marked with x are required for |
| // the current view port. |
| // ------------------------- |
| // | x | x | | | | | |
| // ------------------------- |
| // | x | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| // ------------------------- |
| // | | | | | | | |
| expectedRequiredBitmaps[0][0] = true; |
| expectedRequiredBitmaps[0][1] = true; |
| expectedRequiredBitmaps[1][0] = true; |
| Assert.assertTrue(Arrays.deepEquals( |
| expectedRequiredBitmaps, mMediator.mBitmapState.mRequiredBitmaps)); |
| |
| // Now a scale factor of 2 will be applied. This will happen at a focal point of 0, 0. |
| // The same bitmaps will be required but the grid will be double the size. |
| Assert.assertTrue(mMediator.scaleBy(2f, 0, 0)); |
| Matrix expectedViewportMatrix = new Matrix(); |
| Matrix expectedBitmapMatrix = new Matrix(); |
| expectedViewportMatrix.postScale(2f, 2f, 0f, 0f); |
| expectedBitmapMatrix.postScale(2f, 2f, 0f, 0f); |
| assertViewportStateIs(expectedViewportMatrix, mViewport); |
| Assert.assertEquals(expectedBitmapMatrix, mModel.get(PlayerFrameProperties.SCALE_MATRIX)); |
| |
| Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0)); |
| |
| expectedRequiredBitmaps = new boolean[12][12]; |
| expectedRequiredBitmaps[0][0] = true; |
| expectedRequiredBitmaps[0][1] = true; |
| expectedRequiredBitmaps[1][0] = true; |
| Assert.assertTrue(Arrays.deepEquals( |
| expectedRequiredBitmaps, mMediator.mBitmapState.mRequiredBitmaps)); |
| |
| // Reduce the scale factor by 0.5 returning to a scale of 1 but try to do so with a focal |
| // point that causes translation outside the bounds. The focal point should be ignored. |
| Assert.assertTrue(mMediator.scaleBy(0.5f, 50f, 50f)); |
| expectedViewportMatrix.postScale(0.5f, 0.5f, 0f, 0f); |
| expectedBitmapMatrix.reset(); |
| expectedBitmapMatrix.postScale(0.5f, 0.5f, 0f, 0f); |
| assertViewportStateIs(expectedViewportMatrix, mViewport); |
| Assert.assertEquals(expectedBitmapMatrix, mModel.get(PlayerFrameProperties.SCALE_MATRIX)); |
| |
| Assert.assertTrue(mMediator.scaleFinished(1f, -50f, -50f)); |
| |
| expectedRequiredBitmaps = new boolean[6][6]; |
| expectedRequiredBitmaps[0][0] = true; |
| expectedRequiredBitmaps[0][1] = true; |
| expectedRequiredBitmaps[1][0] = true; |
| Assert.assertTrue(Arrays.deepEquals( |
| expectedRequiredBitmaps, mMediator.mBitmapState.mRequiredBitmaps)); |
| } |
| |
| /** |
| * Tests that {@link PlayerFrameMediator} works correctly with nested subframes. This test |
| * pretends that mMediator is for a subframe. The calls made to this mediator are verified |
| * to occur in {@link testViewPortOnScaleByWithSubFrames}. |
| */ |
| @Test |
| public void testViewPortOnScaleByWithNestedSubFrames() { |
| Context context = Robolectric.buildActivity(Activity.class).get(); |
| View subframeView = new View(context); |
| |
| PlayerFrameMediator subFrameMediator = Mockito.mock(PlayerFrameMediator.class); |
| Pair<View, Rect> subFrame = new Pair<>(subframeView, new Rect(10, 20, 60, 40)); |
| mMediator.addSubFrame(subFrame.first, subFrame.second, subFrameMediator); |
| |
| // The subframe should be visible. |
| mMediator.updateViewportSize(50, 100, 1f); |
| List<View> expectedVisibleViews = new ArrayList<>(); |
| List<Rect> expectedVisibleRects = new ArrayList<>(); |
| expectedVisibleViews.add(subFrame.first); |
| expectedVisibleRects.add(subFrame.second); |
| Assert.assertEquals(expectedVisibleViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS)); |
| Assert.assertEquals(expectedVisibleRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS)); |
| |
| expectedVisibleViews.clear(); |
| expectedVisibleRects.clear(); |
| expectedVisibleViews.add(subFrame.first); |
| expectedVisibleRects.add(new Rect(20, 40, 120, 80)); |
| |
| // Scale by a factor of two via the parent. |
| Matrix scaleMatrix = new Matrix(); |
| scaleMatrix.setScale(2f, 2f); |
| mMediator.setBitmapScaleMatrixOfSubframe(scaleMatrix, 2f); |
| Assert.assertEquals(expectedVisibleViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS)); |
| Assert.assertEquals(expectedVisibleRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS)); |
| verify(subFrameMediator) |
| .setBitmapScaleMatrixOfSubframe(argThat(new MatrixMatcher(scaleMatrix)), eq(2f)); |
| |
| expectedVisibleViews.clear(); |
| expectedVisibleRects.clear(); |
| expectedVisibleViews.add(subFrame.first); |
| expectedVisibleRects.add(new Rect(15, 30, 90, 60)); |
| |
| // Zoom out by a factor of 0.75, note the scale factor argument is compounded. |
| scaleMatrix.setScale(0.75f, 0.75f); |
| mMediator.setBitmapScaleMatrixOfSubframe(scaleMatrix, 1.5f); |
| Assert.assertEquals(expectedVisibleViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS)); |
| Assert.assertEquals(expectedVisibleRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS)); |
| verify(subFrameMediator) |
| .setBitmapScaleMatrixOfSubframe(argThat(new MatrixMatcher(scaleMatrix)), eq(1.5f)); |
| |
| // Simulate scaleFinished() by force a scale factor clear and redraw. |
| mMediator.resetScaleFactor(); |
| verify(subFrameMediator).resetScaleFactor(); |
| mMediator.forceRedraw(); |
| verify(subFrameMediator).forceRedraw(); |
| } |
| /** |
| * Tests that {@link PlayerFrameMediator} calls the user interaction callback. |
| */ |
| @Test |
| public void testUserInteractionCallback() { |
| mMediator.updateViewportSize(100, 200, 1f); |
| |
| Assert.assertFalse( |
| "User interaction callback shouldn't have been called", mHasUserInteraction); |
| mMediator.scrollBy(0, 10); |
| Assert.assertTrue("User interaction callback should have been called", mHasUserInteraction); |
| |
| mHasUserInteraction = false; |
| mMediator.onFling(0, 10); |
| Assert.assertTrue("User interaction callback should have been called", mHasUserInteraction); |
| |
| mHasUserInteraction = false; |
| mMediator.scaleBy(1.5f, 20, 30); |
| Assert.assertTrue("User interaction callback should have been called", mHasUserInteraction); |
| } |
| } |