1 /*
2  * Copyright (C) 2021 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.view.cts;
17 
18 import static android.server.wm.WindowManagerState.getLogicalDisplaySize;
19 
20 import android.animation.ObjectAnimator;
21 import android.animation.PropertyValuesHolder;
22 import android.animation.ValueAnimator;
23 import android.annotation.SuppressLint;
24 import android.content.Context;
25 import android.graphics.Canvas;
26 import android.graphics.Color;
27 import android.graphics.PorterDuff;
28 import android.graphics.Rect;
29 import android.graphics.Region;
30 import android.support.test.uiautomator.UiObjectNotFoundException;
31 import android.util.IntProperty;
32 import android.util.Property;
33 import android.view.Gravity;
34 import android.view.Surface;
35 import android.view.SurfaceControl;
36 import android.view.View;
37 import android.view.ViewTreeObserver;
38 import android.view.animation.LinearInterpolator;
39 import android.view.cts.surfacevalidator.AnimationFactory;
40 import android.view.cts.surfacevalidator.AnimationTestCase;
41 import android.view.cts.surfacevalidator.CapturedActivityWithResource;
42 import android.view.cts.surfacevalidator.PixelChecker;
43 import android.view.cts.surfacevalidator.ViewFactory;
44 import android.widget.FrameLayout;
45 
46 import androidx.test.filters.LargeTest;
47 import androidx.test.filters.RequiresDevice;
48 import androidx.test.rule.ActivityTestRule;
49 import androidx.test.runner.AndroidJUnit4;
50 
51 import org.junit.After;
52 import org.junit.Before;
53 import org.junit.Rule;
54 import org.junit.Test;
55 import org.junit.rules.TestName;
56 import org.junit.runner.RunWith;
57 
58 @RunWith(AndroidJUnit4.class)
59 @LargeTest
60 @SuppressLint("RtlHardcoded")
61 @RequiresDevice
62 public class AttachedSurfaceControlSyncTest {
63     private static final String TAG = "AttachedSurfaceControlSyncTests";
64 
65     @Rule
66     public ActivityTestRule<CapturedActivityWithResource> mActivityRule =
67             new ActivityTestRule<>(CapturedActivityWithResource.class);
68 
69     @Rule
70     public TestName mName = new TestName();
71 
72     private CapturedActivityWithResource mActivity;
73 
makeInfinite(ValueAnimator a)74       private static ValueAnimator makeInfinite(ValueAnimator a) {
75         a.setRepeatMode(ObjectAnimator.REVERSE);
76         a.setRepeatCount(ObjectAnimator.INFINITE);
77         a.setDuration(200);
78         a.setInterpolator(new LinearInterpolator());
79         return a;
80     }
81 
82     static class GreenSurfaceAnchorView extends View {
83         SurfaceControl mSurfaceControl;
84         final Surface mSurface;
85         final int[] mLocation = new int[2];
86 
87         private final ViewTreeObserver.OnPreDrawListener mDrawListener = () -> {
88             SurfaceControl.Transaction t = new SurfaceControl.Transaction();
89             getLocationInWindow(mLocation);
90             t.setGeometry(mSurfaceControl, null, new Rect(mLocation[0], mLocation[1],
91                     mLocation[0]+100,
92                     mLocation[1]+100), 0);
93             getRootSurfaceControl().applyTransactionOnDraw(t);
94             return true;
95         };
96 
GreenSurfaceAnchorView(Context c)97         GreenSurfaceAnchorView(Context c) {
98             super(c, null, 0, 0);
99             mSurfaceControl = new SurfaceControl.Builder()
100                               .setName("SurfaceAnchorView")
101                               .setBufferSize(100, 100)
102                               .build();
103             mSurface = new Surface(mSurfaceControl);
104             Canvas canvas = mSurface.lockHardwareCanvas();
105             canvas.drawColor(Color.GREEN);
106             mSurface.unlockCanvasAndPost(canvas);
107         }
108 
109         @Override
gatherTransparentRegion(Region region)110         public boolean gatherTransparentRegion(Region region) {
111             int w = getWidth();
112             int h = getHeight();
113             if (w>0 && h>0) {
114                 getLocationInWindow(mLocation);
115                 int l = mLocation[0];
116                 int t = mLocation[1];
117                 region.op(l, t, l+w, t+h, Region.Op.UNION);
118             }
119             return false;
120         }
121 
122         @Override
onAttachedToWindow()123         protected void onAttachedToWindow() {
124             super.onAttachedToWindow();
125             SurfaceControl.Transaction t =
126                 getRootSurfaceControl().buildReparentTransaction(mSurfaceControl);
127             t.setLayer(mSurfaceControl, -1)
128                 .setVisibility(mSurfaceControl, true)
129                 .apply();
130 
131             ViewTreeObserver observer = getViewTreeObserver();
132             observer.addOnPreDrawListener(mDrawListener);
133 
134             getParent().requestTransparentRegion(this);
135         }
136 
137         @Override
onDetachedFromWindow()138         protected void onDetachedFromWindow() {
139             ViewTreeObserver observer = getViewTreeObserver();
140             observer.removeOnPreDrawListener(mDrawListener);
141 
142             new SurfaceControl.Transaction().reparent(mSurfaceControl, null).apply();
143             mSurfaceControl.release();
144             mSurface.release();
145 
146             super.onDetachedFromWindow();
147         }
148 
149         @Override
onDraw(Canvas canvas)150         public void onDraw(Canvas canvas) {
151             canvas.drawColor(0, PorterDuff.Mode.CLEAR);
152         }
153     }
154 
155     private static final ViewFactory sGreenSurfaceControlAnchorFactory =
156             GreenSurfaceAnchorView::new;
157 
158     private static final AnimationFactory sTranslateAnimationFactory = view -> {
159         Property<View, Integer> translationX = new IntProperty<View>("translationX") {
160             @Override
161             public void setValue(View object, int value) {
162                 object.setTranslationX(value);
163             }
164 
165             @Override
166             public Integer get(View object) {
167                 return (int) object.getTranslationX();
168             }
169         };
170 
171         Property<View, Integer> translationY = new IntProperty<View>("translationY") {
172             @Override
173             public void setValue(View object, int value) {
174                 object.setTranslationY(value);
175             }
176 
177             @Override
178             public Integer get(View object) {
179                 return (int) object.getTranslationY();
180             }
181         };
182 
183         PropertyValuesHolder pvhX = PropertyValuesHolder.ofInt(translationX, 10, 30);
184         PropertyValuesHolder pvhY = PropertyValuesHolder.ofInt(translationY, 10, 30);
185         return makeInfinite(ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY));
186     };
187 
188     @Before
setup()189     public void setup() {
190         mActivity = mActivityRule.getActivity();
191         mActivity.setLogicalDisplaySize(getLogicalDisplaySize());
192     }
193 
194     /**
195      * Want to be especially sure we don't leave up the permission dialog, so try and dismiss
196      * after test.
197      */
198     @After
tearDown()199     public void tearDown() throws UiObjectNotFoundException {
200         mActivity.dismissPermissionDialog();
201     }
202 
203     /** Draws a moving 10x10 green rectangle with hole punch, make sure we don't get any sync errors */
204     @Test
testSync()205     public void testSync() throws Throwable {
206         mActivity.verifyTest(new AnimationTestCase(
207                 sGreenSurfaceControlAnchorFactory,
208                 new FrameLayout.LayoutParams(100, 100, Gravity.LEFT | Gravity.TOP),
209                 sTranslateAnimationFactory,
210                 new PixelChecker() {
211                     @Override
212                     public boolean checkPixels(int blackishPixelCount, int width, int height) {
213                         return blackishPixelCount == 0;
214                     }
215                 }), mName);
216     }
217 }
218