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