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 77 @Override onDraw()78 public void onDraw() { 79 final int requestedOrientation = ORIENTATIONS[mCurrentOrientation]; 80 boolean isRequestingPortrait = 81 requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT 82 || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; 83 if (mSupportsRotation && (isRequestingPortrait != DisplayUtils.isDevicePortrait(this))) { 84 return; 85 } 86 mContent.post(() -> { 87 Point offset = new Point(); 88 // We pass mContentBounds here just as a throwaway rect, we don't care about 89 // the visible rect just the global offset. 90 mContent.getGlobalVisibleRect(mContentBounds, offset); 91 mContentBounds.set(offset.x - mOutsets.left, offset.y - mOutsets.top, 92 offset.x - mOutsets.left + mContent.getWidth(), 93 offset.y - mOutsets.top + mContent.getHeight()); 94 mFence.countDown(); 95 if (mFence.getCount() > 0) { 96 mContent.invalidate(); 97 } 98 }); 99 } 100 101 @Override onApplyWindowInsets(View v, WindowInsets in)102 public WindowInsets onApplyWindowInsets(View v, WindowInsets in) { 103 if (in.isRound()) { 104 FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mContent.getLayoutParams(); 105 params.setMargins(in.getSystemWindowInsetLeft(), in.getSystemWindowInsetTop(), 106 in.getSystemWindowInsetRight(), in.getSystemWindowInsetBottom()); 107 mOutsets = new Rect(in.getSystemWindowInsetLeft(), in.getSystemWindowInsetTop(), 108 in.getSystemWindowInsetRight(), in.getSystemWindowInsetBottom()); 109 mContent.setLayoutParams(params); 110 } 111 return in; 112 } 113 waitForFirstDrawCompleted(int timeout, TimeUnit unit)114 public void waitForFirstDrawCompleted(int timeout, TimeUnit unit) { 115 try { 116 if (!mFence.await(timeout, unit)) { 117 fail("Timeout"); 118 } 119 } catch (InterruptedException ex) { 120 fail(ex.getMessage()); 121 } 122 } 123 rotate()124 public boolean rotate() { 125 if (!mSupportsRotation) { 126 // Do not rotate the screen if it is not supported. 127 return false; 128 } 129 mFence = new CountDownLatch(DRAW_FRAME_COUNT_BEFORE_CAPTURE); 130 runOnUiThread(() -> { 131 mCurrentOrientation = (mCurrentOrientation + 1) % ORIENTATIONS.length; 132 Log.d("PixelCopyTest", "Setting orientation index = " + mCurrentOrientation); 133 setRequestedOrientation(ORIENTATIONS[mCurrentOrientation]); 134 }); 135 waitForFirstDrawCompleted(10, TimeUnit.SECONDS); 136 return mCurrentOrientation != 0; 137 } 138 139 // Convert a rect in normalized 0-100 dimensions to the bounds of the actual View. normalizedToSurface(Rect inOut)140 public void normalizedToSurface(Rect inOut) { 141 float sx = mContentBounds.width() / 100.0f; 142 float sy = mContentBounds.height() / 100.0f; 143 inOut.left = (int) (inOut.left * sx); 144 inOut.top = (int) (inOut.top * sy); 145 inOut.right = (int) (inOut.right * sx + 0.5f); 146 inOut.bottom = (int) (inOut.bottom * sy + 0.5f); 147 inOut.offset(mContentBounds.left, mContentBounds.top); 148 } 149 150 private static final class ColoredGrid extends View { 151 private Paint mPaint = new Paint(); 152 private Rect mRect = new Rect(); 153 ColoredGrid(Context context)154 ColoredGrid(Context context) { 155 super(context); 156 setWillNotDraw(false); 157 } 158 159 @Override onDraw(Canvas canvas)160 protected void onDraw(Canvas canvas) { 161 int cx = getWidth() / 2; 162 int cy = getHeight() / 2; 163 final int BORDER_WIDTH = 2; 164 165 canvas.drawColor(Color.YELLOW); 166 167 mRect.set(BORDER_WIDTH, BORDER_WIDTH, cx, cy); 168 mPaint.setColor(Color.RED); 169 canvas.drawRect(mRect, mPaint); 170 171 mRect.set(cx, BORDER_WIDTH, getWidth() - BORDER_WIDTH, cy); 172 mPaint.setColor(Color.GREEN); 173 canvas.drawRect(mRect, mPaint); 174 175 mRect.set(BORDER_WIDTH, cy, cx, getHeight() - BORDER_WIDTH); 176 mPaint.setColor(Color.BLUE); 177 canvas.drawRect(mRect, mPaint); 178 179 mRect.set(cx, cy, getWidth() - BORDER_WIDTH, getHeight() - BORDER_WIDTH); 180 mPaint.setColor(Color.BLACK); 181 canvas.drawRect(mRect, mPaint); 182 } 183 } 184 } 185