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