• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }