1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.view.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertTrue; 21 22 import android.Manifest; 23 import android.graphics.Bitmap; 24 import android.graphics.Color; 25 import android.graphics.Matrix; 26 import android.graphics.Rect; 27 import android.os.Handler; 28 import android.os.Looper; 29 import android.platform.test.annotations.AppModeSdkSandbox; 30 import android.view.PixelCopy; 31 import android.view.View; 32 import android.view.ViewGroup; 33 34 import androidx.test.InstrumentationRegistry; 35 import androidx.test.annotation.UiThreadTest; 36 import androidx.test.filters.SmallTest; 37 import androidx.test.rule.ActivityTestRule; 38 import androidx.test.runner.AndroidJUnit4; 39 40 import com.android.compatibility.common.util.AdoptShellPermissionsRule; 41 42 import org.junit.Rule; 43 import org.junit.Test; 44 import org.junit.runner.RunWith; 45 46 import java.util.concurrent.CountDownLatch; 47 import java.util.concurrent.TimeUnit; 48 49 @SmallTest 50 @RunWith(AndroidJUnit4.class) 51 @AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).") 52 public class ViewAnimationMatrixTest { 53 54 @Rule(order = 0) 55 public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule( 56 androidx.test.platform.app.InstrumentationRegistry 57 .getInstrumentation().getUiAutomation(), 58 Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX); 59 60 @Rule(order = 1) 61 public ActivityTestRule<ViewAnimationMatrixActivity> mRule = 62 new ActivityTestRule<>(ViewAnimationMatrixActivity.class, false, false); 63 64 @UiThreadTest 65 @Test testAnimationMatrixGetter()66 public void testAnimationMatrixGetter() { 67 View view = new View(InstrumentationRegistry.getTargetContext()); 68 Matrix matrix = new Matrix(); 69 matrix.setTranslate(34, 65); 70 matrix.setRotate(45); 71 view.setAnimationMatrix(matrix); 72 73 assertEquals(matrix, view.getAnimationMatrix()); 74 } 75 76 @Test testAnimationMatrixAppliedDuringDrawing()77 public void testAnimationMatrixAppliedDuringDrawing() throws Throwable { 78 ViewAnimationMatrixActivity activity = mRule.launchActivity(null); 79 final View view = activity.mView; 80 final View root = activity.mRoot; 81 82 // view has some offset and rotation originally 83 mRule.runOnUiThread(() -> view.setAnimationMatrix(moveToTopLeftCorner(view))); 84 // now it should be in the top left corner of the parent 85 86 waitForDraw(root); 87 Bitmap bitmap = captureView(root, view.getWidth(), view.getHeight()); 88 89 assertAllPixelsAre(Color.BLACK, bitmap); 90 } 91 92 @Test testAnimationMatrixClearedWithPassingNull()93 public void testAnimationMatrixClearedWithPassingNull() throws Throwable { 94 ViewAnimationMatrixActivity activity = mRule.launchActivity(null); 95 final View view = activity.mView; 96 final View root = activity.mRoot; 97 98 mRule.runOnUiThread(() -> view.setAnimationMatrix(moveToTopLeftCorner(view))); 99 mRule.runOnUiThread(() -> view.setAnimationMatrix(null)); 100 101 waitForDraw(root); 102 Bitmap bitmap = captureView(root, view.getWidth(), view.getHeight()); 103 104 // view should again be drawn with the original offset, so our target rect is empty 105 assertAllPixelsAre(Color.WHITE, bitmap); 106 } 107 moveToTopLeftCorner(View view)108 private Matrix moveToTopLeftCorner(View view) { 109 final ViewGroup.MarginLayoutParams lp = 110 (ViewGroup.MarginLayoutParams) view.getLayoutParams(); 111 Matrix matrix = new Matrix(); 112 matrix.setRotate(-view.getRotation(), lp.width / 2f, lp.height / 2f); 113 matrix.postTranslate(-lp.leftMargin, 0); 114 return matrix; 115 } 116 captureView(final View view, int width, int height)117 private Bitmap captureView(final View view, int width, int height) throws Throwable { 118 final Bitmap dest = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 119 final CountDownLatch latch = new CountDownLatch(1); 120 int[] offset = new int[2]; 121 view.getLocationInWindow(offset); 122 Rect srcRect = new Rect(0, 0, width, height); 123 srcRect.offset(offset[0], offset[1]); 124 PixelCopy.OnPixelCopyFinishedListener onCopyFinished = 125 copyResult -> { 126 assertEquals(PixelCopy.SUCCESS, copyResult); 127 latch.countDown(); 128 }; 129 PixelCopy.request(mRule.getActivity().getWindow(), srcRect, dest, onCopyFinished, 130 new Handler(Looper.getMainLooper())); 131 assertTrue(latch.await(1, TimeUnit.SECONDS)); 132 return dest; 133 } 134 waitForDraw(final View view)135 private void waitForDraw(final View view) throws Throwable { 136 final CountDownLatch latch = new CountDownLatch(1); 137 mRule.runOnUiThread(() -> { 138 view.getViewTreeObserver().registerFrameCommitCallback(latch::countDown); 139 view.invalidate(); 140 }); 141 assertTrue(latch.await(1, TimeUnit.SECONDS)); 142 } 143 assertAllPixelsAre(int color, Bitmap bitmap)144 private void assertAllPixelsAre(int color, Bitmap bitmap) { 145 // skipping a few border pixels in case of antialiazing 146 for (int i = 2; i < bitmap.getWidth() - 2; i++) { 147 for (int j = 2; j < bitmap.getHeight() - 2; j++) { 148 assertEquals(color, bitmap.getPixel(i, j)); 149 } 150 } 151 } 152 153 } 154