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