1 /*
2  * Copyright (C) 2016 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 package android.uirendering.cts.testclasses;
17 
18 import android.animation.ObjectAnimator;
19 import android.graphics.Bitmap;
20 import android.graphics.Bitmap.Config;
21 import android.graphics.Canvas;
22 import android.graphics.Color;
23 import android.graphics.Rect;
24 import android.uirendering.cts.R;
25 import android.uirendering.cts.bitmapverifiers.ColorVerifier;
26 import android.uirendering.cts.testinfrastructure.ActivityTestBase;
27 import android.uirendering.cts.testinfrastructure.CanvasClient;
28 import android.uirendering.cts.testinfrastructure.ViewInitializer;
29 import android.view.Gravity;
30 import android.view.PixelCopy;
31 import android.view.SurfaceHolder;
32 import android.view.SurfaceView;
33 import android.view.View;
34 import android.view.animation.LinearInterpolator;
35 import android.widget.FrameLayout;
36 
37 import androidx.test.filters.LargeTest;
38 import androidx.test.runner.AndroidJUnit4;
39 
40 import com.android.compatibility.common.util.SynchronousPixelCopy;
41 
42 import org.junit.Assert;
43 import org.junit.Test;
44 import org.junit.runner.RunWith;
45 
46 import java.util.concurrent.CountDownLatch;
47 
48 @LargeTest
49 @RunWith(AndroidJUnit4.class)
50 public class SurfaceViewTests extends ActivityTestBase {
51 
52     static final CanvasCallback sGreenCanvasCallback =
53             new CanvasCallback((canvas, width, height) -> canvas.drawColor(Color.GREEN));
54     static final CanvasCallback sWhiteCanvasCallback =
55             new CanvasCallback((canvas, width, height) -> canvas.drawColor(Color.WHITE));
56     static final CanvasCallback sRedCanvasCallback =
57             new CanvasCallback((canvas, width, height) -> canvas.drawColor(Color.RED));
58 
59     private static class CanvasCallback implements SurfaceHolder.Callback {
60         final CanvasClient mCanvasClient;
61 
CanvasCallback(CanvasClient canvasClient)62         public CanvasCallback(CanvasClient canvasClient) {
63             mCanvasClient = canvasClient;
64         }
65 
66         @Override
surfaceCreated(SurfaceHolder holder)67         public void surfaceCreated(SurfaceHolder holder) {
68         }
69 
70         @Override
surfaceChanged(SurfaceHolder holder, int format, int width, int height)71         public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
72             Canvas canvas = holder.lockCanvas();
73             mCanvasClient.draw(canvas, width, height);
74             holder.unlockCanvasAndPost(canvas);
75         }
76 
77         @Override
surfaceDestroyed(SurfaceHolder holder)78         public void surfaceDestroyed(SurfaceHolder holder) {
79         }
80     }
81 
createInfiniteAnimator(Object target, String prop, float start, float end)82     static ObjectAnimator createInfiniteAnimator(Object target, String prop,
83             float start, float end) {
84         ObjectAnimator a = ObjectAnimator.ofFloat(target, prop, start, end);
85         a.setRepeatMode(ObjectAnimator.REVERSE);
86         a.setRepeatCount(ObjectAnimator.INFINITE);
87         a.setDuration(200);
88         a.setInterpolator(new LinearInterpolator());
89         a.start();
90         return a;
91     }
92 
93     @Test
testMovingWhiteSurfaceView()94     public void testMovingWhiteSurfaceView() {
95         // A moving SurfaceViews with white content against a white background should be invisible
96         ViewInitializer initializer = new ViewInitializer() {
97             ObjectAnimator mAnimator;
98             @Override
99             public void initializeView(View view) {
100                 FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout);
101                 mAnimator = createInfiniteAnimator(root, "translationY", 0, 50);
102 
103                 SurfaceView surfaceViewA = new SurfaceView(view.getContext());
104                 surfaceViewA.getHolder().addCallback(sWhiteCanvasCallback);
105                 root.addView(surfaceViewA, new FrameLayout.LayoutParams(
106                         90, 40, Gravity.START | Gravity.TOP));
107             }
108             @Override
109             public void teardownView() {
110                 mAnimator.cancel();
111             }
112         };
113         Screenshotter screenshotter = testPositionInfo -> {
114             Bitmap source = getInstrumentation().getUiAutomation().takeScreenshot();
115             return Bitmap.createBitmap(source,
116                     testPositionInfo.screenOffset.x, testPositionInfo.screenOffset.y,
117                     TEST_WIDTH, TEST_HEIGHT);
118         };
119         createTest()
120                 .addLayout(R.layout.frame_layout, initializer, true)
121                 .withScreenshotter(screenshotter)
122                 .runWithAnimationVerifier(new ColorVerifier(Color.WHITE, 0 /* zero tolerance */));
123     }
124 
125     private static class SurfaceViewHelper implements ViewInitializer, Screenshotter, SurfaceHolder.Callback {
126         private final CanvasClient mCanvasClient;
127         private final CountDownLatch mFence = new CountDownLatch(1);
128         private SurfaceView mSurfaceView;
129 
SurfaceViewHelper(CanvasClient canvasClient)130         public SurfaceViewHelper(CanvasClient canvasClient) {
131             mCanvasClient = canvasClient;
132         }
133 
134         @Override
takeScreenshot(TestPositionInfo testPositionInfo)135         public Bitmap takeScreenshot(TestPositionInfo testPositionInfo) {
136             SynchronousPixelCopy copy = new SynchronousPixelCopy();
137             Bitmap dest = Bitmap.createBitmap(
138                     TEST_WIDTH, TEST_HEIGHT, Config.ARGB_8888);
139             Rect srcRect = new Rect(0, 0, TEST_WIDTH, TEST_HEIGHT);
140             int copyResult = copy.request(mSurfaceView, srcRect, dest);
141             Assert.assertEquals(PixelCopy.SUCCESS, copyResult);
142             return dest;
143         }
144 
145         @Override
initializeView(View view)146         public void initializeView(View view) {
147             FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout);
148             mSurfaceView = new SurfaceView(view.getContext());
149             mSurfaceView.getHolder().addCallback(this);
150             onSurfaceViewCreated(mSurfaceView);
151             root.addView(mSurfaceView, new FrameLayout.LayoutParams(
152                     FrameLayout.LayoutParams.MATCH_PARENT,
153                     FrameLayout.LayoutParams.MATCH_PARENT));
154         }
155 
onSurfaceViewCreated(SurfaceView surfaceView)156         public void onSurfaceViewCreated(SurfaceView surfaceView) {
157 
158         }
159 
160         @Override
surfaceCreated(SurfaceHolder holder)161         public void surfaceCreated(SurfaceHolder holder) {
162         }
163 
164         @Override
surfaceChanged(SurfaceHolder holder, int format, int width, int height)165         public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
166             // TODO: Remove the post() which is a temporary workaround for b/32484713
167             mSurfaceView.post(() -> {
168                 Canvas canvas = holder.lockHardwareCanvas();
169                 mCanvasClient.draw(canvas, width, height);
170                 holder.unlockCanvasAndPost(canvas);
171                 mFence.countDown();
172             });
173         }
174 
175         @Override
surfaceDestroyed(SurfaceHolder holder)176         public void surfaceDestroyed(SurfaceHolder holder) {
177         }
178 
getFence()179         public CountDownLatch getFence() {
180             return mFence;
181         }
182     }
183 
184     @Test
testSurfaceHolderHardwareCanvas()185     public void testSurfaceHolderHardwareCanvas() {
186         SurfaceViewHelper helper = new SurfaceViewHelper((canvas, width, height) -> {
187             Assert.assertNotNull(canvas);
188             Assert.assertTrue(canvas.isHardwareAccelerated());
189             canvas.drawColor(Color.GREEN);
190         });
191         createTest()
192                 .addLayout(R.layout.frame_layout, helper, true, helper.getFence())
193                 .withScreenshotter(helper)
194                 .runWithVerifier(new ColorVerifier(Color.GREEN, 0 /* zero tolerance */));
195     }
196 
197     @Test
testSurfaceViewHolePunchWithLayer()198     public void testSurfaceViewHolePunchWithLayer() {
199         SurfaceViewHelper helper = new SurfaceViewHelper((canvas, width, height) -> {
200             Assert.assertNotNull(canvas);
201             Assert.assertTrue(canvas.isHardwareAccelerated());
202             canvas.drawColor(Color.GREEN);
203         }
204         ) {
205             @Override
206             public void onSurfaceViewCreated(SurfaceView surfaceView) {
207                 surfaceView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
208             }
209         };
210         createTest()
211                 .addLayout(R.layout.frame_layout, helper, true, helper.getFence())
212                 .withScreenshotter(helper)
213                 .runWithVerifier(new ColorVerifier(Color.GREEN, 0 /* zero tolerance */));
214 
215     }
216 }
217