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 17 package android.view.cts; 18 19 import static org.junit.Assert.fail; 20 21 import android.app.Activity; 22 import android.content.Context; 23 import android.content.pm.ActivityInfo; 24 import android.graphics.Canvas; 25 import android.graphics.Color; 26 import android.graphics.Paint; 27 import android.graphics.Point; 28 import android.graphics.Rect; 29 import android.os.Bundle; 30 import android.util.Log; 31 import android.view.View; 32 import android.view.ViewTreeObserver.OnDrawListener; 33 import android.view.WindowInsets; 34 import android.view.cts.util.DisplayUtils; 35 import android.widget.FrameLayout; 36 37 import java.util.concurrent.CountDownLatch; 38 import java.util.concurrent.TimeUnit; 39 40 public class PixelCopyViewProducerActivity extends Activity implements OnDrawListener, 41 View.OnApplyWindowInsetsListener{ 42 private static final int[] ORIENTATIONS = { 43 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, 44 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, 45 ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT, 46 ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE, 47 }; 48 // TODO: Lower this (or remove it entirely) by leveraging things like 49 // ViewTreeObserver#registerFrameCommitCallback (and possibly display orientation listeners?) 50 private static final int DRAW_FRAME_COUNT_BEFORE_CAPTURE = 10; 51 private int mCurrentOrientation = 0; 52 private View mContent; 53 private Rect mContentBounds = new Rect(); 54 private Rect mOutsets = new Rect(); 55 private CountDownLatch mFence = new CountDownLatch(DRAW_FRAME_COUNT_BEFORE_CAPTURE); 56 private boolean mSupportsRotation; 57 58 @Override onCreate(Bundle savedInstanceState)59 protected void onCreate(Bundle savedInstanceState) { 60 super.onCreate(savedInstanceState); 61 62 // Check if the device supports both of portrait and landscape orientation screens. 63 mSupportsRotation = DisplayUtils.supportOrientationRequest(this); 64 if (mSupportsRotation) { 65 Log.d("PixelCopyTest", "Setting orientation index = " + mCurrentOrientation); 66 setRequestedOrientation(ORIENTATIONS[mCurrentOrientation]); 67 } 68 69 mContent = new ColoredGrid(this); 70 setContentView(mContent); 71 View view = this.getWindow().getDecorView(); 72 view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); 73 mContent.getViewTreeObserver().addOnDrawListener(this); 74 mContent.setOnApplyWindowInsetsListener(this); 75 76 //setDecorFitsSystemWindows to false will ignore the cutout 77 getWindow().setDecorFitsSystemWindows(false); 78 } 79 80 @Override onDraw()81 public void onDraw() { 82 final int requestedOrientation = ORIENTATIONS[mCurrentOrientation]; 83 boolean isRequestingPortrait = 84 requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT 85 || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; 86 if (mSupportsRotation && (isRequestingPortrait != DisplayUtils.isDevicePortrait(this))) { 87 return; 88 } 89 mContent.post(() -> { 90 Point offset = new Point(); 91 // We pass mContentBounds here just as a throwaway rect, we don't care about 92 // the visible rect just the global offset. 93 mContent.getGlobalVisibleRect(mContentBounds, offset); 94 mContentBounds.set(offset.x - mOutsets.left, offset.y - mOutsets.top, 95 offset.x - mOutsets.left + mContent.getWidth(), 96 offset.y - mOutsets.top + mContent.getHeight()); 97 mFence.countDown(); 98 if (mFence.getCount() > 0) { 99 mContent.invalidate(); 100 } 101 }); 102 } 103 104 @Override onApplyWindowInsets(View v, WindowInsets in)105 public WindowInsets onApplyWindowInsets(View v, WindowInsets in) { 106 if (in.isRound()) { 107 FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mContent.getLayoutParams(); 108 params.setMargins(in.getSystemWindowInsetLeft(), in.getSystemWindowInsetTop(), 109 in.getSystemWindowInsetRight(), in.getSystemWindowInsetBottom()); 110 mOutsets = new Rect(in.getSystemWindowInsetLeft(), in.getSystemWindowInsetTop(), 111 in.getSystemWindowInsetRight(), in.getSystemWindowInsetBottom()); 112 mContent.setLayoutParams(params); 113 } 114 return in; 115 } 116 waitForFirstDrawCompleted(int timeout, TimeUnit unit)117 public void waitForFirstDrawCompleted(int timeout, TimeUnit unit) { 118 try { 119 if (!mFence.await(timeout, unit)) { 120 fail("Timeout"); 121 } 122 } catch (InterruptedException ex) { 123 fail(ex.getMessage()); 124 } 125 } 126 rotate()127 public boolean rotate() { 128 if (!mSupportsRotation) { 129 // Do not rotate the screen if it is not supported. 130 return false; 131 } 132 mFence = new CountDownLatch(DRAW_FRAME_COUNT_BEFORE_CAPTURE); 133 runOnUiThread(() -> { 134 mCurrentOrientation = (mCurrentOrientation + 1) % ORIENTATIONS.length; 135 Log.d("PixelCopyTest", "Setting orientation index = " + mCurrentOrientation); 136 setRequestedOrientation(ORIENTATIONS[mCurrentOrientation]); 137 }); 138 waitForFirstDrawCompleted(10, TimeUnit.SECONDS); 139 return mCurrentOrientation != 0; 140 } 141 142 // Convert a rect in normalized 0-100 dimensions to the bounds of the actual View. normalizedToSurface(Rect inOut)143 public void normalizedToSurface(Rect inOut) { 144 float sx = mContentBounds.width() / 100.0f; 145 float sy = mContentBounds.height() / 100.0f; 146 inOut.left = (int) (inOut.left * sx); 147 inOut.top = (int) (inOut.top * sy); 148 inOut.right = (int) (inOut.right * sx + 0.5f); 149 inOut.bottom = (int) (inOut.bottom * sy + 0.5f); 150 inOut.offset(mContentBounds.left, mContentBounds.top); 151 } 152 153 private static final class ColoredGrid extends View { 154 private Paint mPaint = new Paint(); 155 private Rect mRect = new Rect(); 156 ColoredGrid(Context context)157 ColoredGrid(Context context) { 158 super(context); 159 setWillNotDraw(false); 160 } 161 162 @Override onDraw(Canvas canvas)163 protected void onDraw(Canvas canvas) { 164 int cx = getWidth() / 2; 165 int cy = getHeight() / 2; 166 final int BORDER_WIDTH = 2; 167 168 canvas.drawColor(Color.YELLOW); 169 170 mRect.set(BORDER_WIDTH, BORDER_WIDTH, cx, cy); 171 mPaint.setColor(Color.RED); 172 canvas.drawRect(mRect, mPaint); 173 174 mRect.set(cx, BORDER_WIDTH, getWidth() - BORDER_WIDTH, cy); 175 mPaint.setColor(Color.GREEN); 176 canvas.drawRect(mRect, mPaint); 177 178 mRect.set(BORDER_WIDTH, cy, cx, getHeight() - BORDER_WIDTH); 179 mPaint.setColor(Color.BLUE); 180 canvas.drawRect(mRect, mPaint); 181 182 mRect.set(cx, cy, getWidth() - BORDER_WIDTH, getHeight() - BORDER_WIDTH); 183 mPaint.setColor(Color.BLACK); 184 canvas.drawRect(mRect, mPaint); 185 } 186 } 187 } 188