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