1 /* 2 * Copyright (C) 2019 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.gameperformance; 17 18 import android.annotation.MainThread; 19 import android.annotation.NonNull; 20 import android.annotation.WorkerThread; 21 import android.app.Activity; 22 import android.content.Context; 23 import android.graphics.Canvas; 24 import android.graphics.drawable.AnimationDrawable; 25 import android.view.WindowManager; 26 import android.widget.AbsoluteLayout; 27 import android.widget.ImageView; 28 import android.window.WindowMetricsHelper; 29 30 import java.util.ArrayList; 31 import java.util.List; 32 import java.util.concurrent.CountDownLatch; 33 34 /** 35 * View that holds requested number of UI controls as ImageView with an infinite animation. 36 */ 37 public class CustomControlView extends AbsoluteLayout { 38 private final static int CONTROL_DIMENSION = 48; 39 40 private final int mPerRowControlCount; 41 private List<Long> mFrameTimes = new ArrayList<>(); 42 CustomControlView(@onNull Context context)43 public CustomControlView(@NonNull Context context) { 44 super(context); 45 46 final WindowManager wm = context.getSystemService(WindowManager.class); 47 final int width = WindowMetricsHelper.getBoundsExcludingNavigationBarAndCutout( 48 wm.getCurrentWindowMetrics()).width(); 49 mPerRowControlCount = width / CONTROL_DIMENSION; 50 } 51 52 /** 53 * Helper class that overrides ImageView and observes draw requests. Only 54 * one such control is created which is the first control in the view. 55 */ 56 class ReferenceImageView extends ImageView { ReferenceImageView(Context context)57 public ReferenceImageView(Context context) { 58 super(context); 59 } 60 @Override draw(Canvas canvas)61 public void draw(Canvas canvas) { 62 reportFrame(); 63 super.draw(canvas); 64 } 65 } 66 67 @WorkerThread createControls( @onNull Activity activity, int controlCount)68 public void createControls( 69 @NonNull Activity activity, int controlCount) throws InterruptedException { 70 synchronized (this) { 71 final CountDownLatch latch = new CountDownLatch(1); 72 activity.runOnUiThread(new Runnable() { 73 @Override 74 public void run() { 75 removeAllViews(); 76 77 for (int i = 0; i < controlCount; ++i) { 78 final ImageView image = (i == 0) ? 79 new ReferenceImageView(activity) : new ImageView(activity); 80 final int x = (i % mPerRowControlCount) * CONTROL_DIMENSION; 81 final int y = (i / mPerRowControlCount) * CONTROL_DIMENSION; 82 final AbsoluteLayout.LayoutParams layoutParams = 83 new AbsoluteLayout.LayoutParams( 84 CONTROL_DIMENSION, CONTROL_DIMENSION, x, y); 85 image.setLayoutParams(layoutParams); 86 image.setBackgroundResource(R.drawable.animation); 87 final AnimationDrawable animation = 88 (AnimationDrawable)image.getBackground(); 89 animation.start(); 90 addView(image); 91 } 92 93 latch.countDown(); 94 } 95 }); 96 latch.await(); 97 } 98 } 99 100 @MainThread reportFrame()101 private void reportFrame() { 102 final long time = System.currentTimeMillis(); 103 synchronized (mFrameTimes) { 104 mFrameTimes.add(time); 105 } 106 } 107 108 /** 109 * Resets frame times in order to calculate FPS for the different test pass. 110 */ resetFrameTimes()111 public void resetFrameTimes() { 112 synchronized (mFrameTimes) { 113 mFrameTimes.clear(); 114 } 115 } 116 117 /** 118 * Returns current FPS based on collected frame times. 119 */ getFps()120 public double getFps() { 121 synchronized (mFrameTimes) { 122 if (mFrameTimes.size() < 2) { 123 return 0.0f; 124 } 125 return 1000.0 * mFrameTimes.size() / 126 (mFrameTimes.get(mFrameTimes.size() - 1) - mFrameTimes.get(0)); 127 } 128 } 129 }