1 /* 2 * Copyright (C) 2014 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.uirendering.cts.testinfrastructure; 17 18 import static org.junit.Assert.fail; 19 20 import android.app.Activity; 21 import android.content.res.Configuration; 22 import android.graphics.Point; 23 import android.os.Bundle; 24 import android.os.Handler; 25 import android.os.HandlerThread; 26 import android.os.Message; 27 import android.support.annotation.Nullable; 28 import android.uirendering.cts.R; 29 import android.util.Log; 30 import android.view.FrameMetrics; 31 import android.view.View; 32 import android.view.ViewGroup; 33 import android.view.ViewStub; 34 import android.view.ViewTreeObserver; 35 import android.view.Window; 36 37 import java.util.concurrent.CountDownLatch; 38 import java.util.concurrent.TimeUnit; 39 40 /** 41 * A generic activity that uses a view specified by the user. 42 */ 43 public class DrawActivity extends Activity { 44 private final static long TIME_OUT_MS = 10000; 45 private final Point mLock = new Point(); 46 47 private Handler mHandler; 48 private View mView; 49 private View mViewWrapper; 50 private boolean mOnTv; 51 onCreate(Bundle bundle)52 public void onCreate(Bundle bundle){ 53 super.onCreate(bundle); 54 getWindow().getDecorView().setSystemUiVisibility( 55 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN); 56 mHandler = new RenderSpecHandler(); 57 int uiMode = getResources().getConfiguration().uiMode; 58 mOnTv = (uiMode & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION; 59 60 // log frame metrics 61 HandlerThread handlerThread = new HandlerThread("FrameMetrics"); 62 handlerThread.start(); 63 getWindow().addOnFrameMetricsAvailableListener( 64 new Window.OnFrameMetricsAvailableListener() { 65 int mRtFrameCount = 0; 66 @Override 67 public void onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics, 68 int dropCountSinceLastInvocation) { 69 Log.d("UiRendering", "Window frame count " + mRtFrameCount 70 + ", frame drops " + dropCountSinceLastInvocation); 71 mRtFrameCount++; 72 } 73 }, new Handler(handlerThread.getLooper())); 74 75 // log draw metrics 76 View view = new View(this); 77 setContentView(view); 78 view.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() { 79 int mFrameCount; 80 @Override 81 public void onDraw() { 82 Log.d("UiRendering", "View tree frame count " + mFrameCount); 83 mFrameCount++; 84 } 85 }); 86 } 87 getOnTv()88 public boolean getOnTv() { 89 return mOnTv; 90 } 91 enqueueRenderSpecAndWait(int layoutId, CanvasClient canvasClient, @Nullable ViewInitializer viewInitializer, boolean useHardware, boolean usePicture)92 public Point enqueueRenderSpecAndWait(int layoutId, CanvasClient canvasClient, 93 @Nullable ViewInitializer viewInitializer, boolean useHardware, boolean usePicture) { 94 ((RenderSpecHandler) mHandler).setViewInitializer(viewInitializer); 95 int arg2 = (useHardware ? View.LAYER_TYPE_NONE : View.LAYER_TYPE_SOFTWARE); 96 Point point = new Point(); 97 synchronized (mLock) { 98 if (canvasClient != null) { 99 mHandler.obtainMessage(RenderSpecHandler.CANVAS_MSG, usePicture ? 1 : 0, 100 arg2, canvasClient).sendToTarget(); 101 } else { 102 mHandler.obtainMessage(RenderSpecHandler.LAYOUT_MSG, layoutId, arg2).sendToTarget(); 103 } 104 105 try { 106 mLock.wait(TIME_OUT_MS); 107 point.set(mLock.x, mLock.y); 108 } catch (InterruptedException e) { 109 e.printStackTrace(); 110 } 111 } 112 return point; 113 } 114 reset()115 public void reset() { 116 CountDownLatch fence = new CountDownLatch(1); 117 mHandler.obtainMessage(RenderSpecHandler.RESET_MSG, fence).sendToTarget(); 118 try { 119 if (!fence.await(10, TimeUnit.SECONDS)) { 120 fail("Timeout exception"); 121 } 122 } catch (InterruptedException ex) { 123 fail(ex.getMessage()); 124 } 125 } 126 127 private ViewInitializer mViewInitializer; 128 notifyOnDrawCompleted()129 private void notifyOnDrawCompleted() { 130 DrawCounterListener onDrawListener = new DrawCounterListener(); 131 mView.getViewTreeObserver().addOnDrawListener(onDrawListener); 132 mView.invalidate(); 133 } 134 135 private class RenderSpecHandler extends Handler { 136 public static final int RESET_MSG = 0; 137 public static final int LAYOUT_MSG = 1; 138 public static final int CANVAS_MSG = 2; 139 140 setViewInitializer(ViewInitializer viewInitializer)141 public void setViewInitializer(ViewInitializer viewInitializer) { 142 mViewInitializer = viewInitializer; 143 } 144 handleMessage(Message message)145 public void handleMessage(Message message) { 146 Log.d("UiRendering", "message of type " + message.what); 147 if (message.what == RESET_MSG) { 148 ((ViewGroup)findViewById(android.R.id.content)).removeAllViews(); 149 ((CountDownLatch)message.obj).countDown(); 150 return; 151 } 152 setContentView(R.layout.test_container); 153 ViewStub stub = (ViewStub) findViewById(R.id.test_content_stub); 154 mViewWrapper = findViewById(R.id.test_content_wrapper); 155 switch (message.what) { 156 case LAYOUT_MSG: { 157 stub.setLayoutResource(message.arg1); 158 mView = stub.inflate(); 159 } break; 160 161 case CANVAS_MSG: { 162 stub.setLayoutResource(R.layout.test_content_canvasclientview); 163 mView = stub.inflate(); 164 ((CanvasClientView) mView).setCanvasClient((CanvasClient) (message.obj)); 165 if (message.arg1 != 0) { 166 ((CanvasClientView) mView).setUsePicture(true); 167 } 168 } break; 169 } 170 171 if (mView == null) { 172 throw new IllegalStateException("failed to inflate test content"); 173 } 174 175 if (mViewInitializer != null) { 176 mViewInitializer.initializeView(mView); 177 } 178 179 // set layer on wrapper parent of view, so view initializer 180 // can control layer type of View under test. 181 mViewWrapper.setLayerType(message.arg2, null); 182 183 notifyOnDrawCompleted(); 184 } 185 } 186 187 @Override onPause()188 protected void onPause() { 189 super.onPause(); 190 if (mViewInitializer != null) { 191 mViewInitializer.teardownView(); 192 } 193 } 194 195 @Override finish()196 public void finish() { 197 // Ignore 198 } 199 200 /** Call this when all the tests that use this activity have completed. 201 * This will then clean up any internal state and finish the activity. */ allTestsFinished()202 public void allTestsFinished() { 203 super.finish(); 204 } 205 206 private class DrawCounterListener implements ViewTreeObserver.OnDrawListener { 207 private final int[] mLocationOnScreen = new int[2]; 208 private static final int DEBUG_REQUIRE_EXTRA_FRAMES = 1; 209 private int mDrawCount = 0; 210 211 @Override onDraw()212 public void onDraw() { 213 if (++mDrawCount <= DEBUG_REQUIRE_EXTRA_FRAMES) { 214 mView.postInvalidate(); 215 return; 216 } 217 mView.post(() -> { 218 Log.d("UiRendering", "notifying capture"); 219 mView.getViewTreeObserver().removeOnDrawListener(this); 220 synchronized (mLock) { 221 mViewWrapper.getLocationOnScreen(mLocationOnScreen); 222 mLock.set(mLocationOnScreen[0], mLocationOnScreen[1]); 223 mLock.notify(); 224 } 225 }); 226 } 227 } 228 } 229