Add support to render app on multiple displays
BUG: 241152647
Test: Manual Testing
Change-Id: I912f164b99c34e9b04d73cf37d0a97a19a1afc40
diff --git a/tests/TouchLatency/app/build.gradle b/tests/TouchLatency/app/build.gradle
index 04a8788..f5ae6f4 100644
--- a/tests/TouchLatency/app/build.gradle
+++ b/tests/TouchLatency/app/build.gradle
@@ -1,13 +1,13 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 28
+ compileSdkVersion 33
buildToolsVersion '28.0.3'
defaultConfig {
applicationId "com.prefabulated.touchlatency"
minSdkVersion 28
- targetSdkVersion 28
+ targetSdkVersion 33
versionCode 1
versionName "1.0"
}
diff --git a/tests/TouchLatency/app/src/main/AndroidManifest.xml b/tests/TouchLatency/app/src/main/AndroidManifest.xml
index 9894736..25bb5d9 100644
--- a/tests/TouchLatency/app/src/main/AndroidManifest.xml
+++ b/tests/TouchLatency/app/src/main/AndroidManifest.xml
@@ -20,16 +20,22 @@
<application android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
- android:theme="@style/AppTheme">
+ android:theme="@style/AppTheme"
+ android:resizeableActivity="true" >
<activity android:name=".TouchLatencyActivity"
android:label="@string/app_name"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
-
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+
+ <activity android:name=".TouchLatencyActivityPresentation"
+ android:label="@string/app_name"
+ android:parentActivityName=".TouchLatencyActivity"
+ android:exported="true">
+ </activity>
</application>
</manifest>
diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
index a2842b6..6ab3b3e 100644
--- a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
+++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
@@ -17,218 +17,40 @@
package com.prefabulated.touchlatency;
import android.app.Activity;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Paint.Align;
+import android.app.ActivityOptions;
+import android.content.Intent;
+import android.hardware.display.DisplayManager;
import android.os.Bundle;
+import android.os.Handler;
import android.os.Trace;
-import android.util.AttributeSet;
-import android.util.Log;
import android.view.Display;
import android.view.Display.Mode;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
import android.view.Window;
import android.view.WindowManager;
-import java.math.RoundingMode;
-import java.text.DecimalFormat;
-
-class TouchLatencyView extends View implements View.OnTouchListener {
- private static final String LOG_TAG = "TouchLatency";
- private static final int BACKGROUND_COLOR = 0xFF400080;
- private static final int INNER_RADIUS = 70;
- private static final int BALL_DIAMETER = 200;
- private static final int SEC_TO_NANOS = 1000000000;
- private static final float FPS_UPDATE_THRESHOLD = 20;
- private static final long BALL_VELOCITY = 420;
-
- public TouchLatencyView(Context context, AttributeSet attrs) {
- super(context, attrs);
- Trace.beginSection("TouchLatencyView constructor");
- setOnTouchListener(this);
- setWillNotDraw(false);
- mBluePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mBluePaint.setColor(0xFF0000FF);
- mBluePaint.setStyle(Paint.Style.FILL);
- mGreenPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mGreenPaint.setColor(0xFF00FF00);
- mGreenPaint.setStyle(Paint.Style.FILL);
- mYellowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mYellowPaint.setColor(0xFFFFFF00);
- mYellowPaint.setStyle(Paint.Style.FILL);
- mRedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mRedPaint.setColor(0xFFFF0000);
- mRedPaint.setStyle(Paint.Style.FILL);
- mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mTextPaint.setColor(0xFFFFFFFF);
- mTextPaint.setTextSize(100);
- mTextPaint.setTextAlign(Align.RIGHT);
-
- mTouching = false;
-
- mLastDrawNano = 0;
- mFps = 0;
- mLastFpsUpdate = 0;
- mFrameCount = 0;
-
- mDf = new DecimalFormat("fps: #.##");
- mDf.setRoundingMode(RoundingMode.HALF_UP);
-
- Trace.endSection();
- }
-
- @Override
- public boolean onTouch(View view, MotionEvent event) {
- Trace.beginSection("TouchLatencyView onTouch");
- int action = event.getActionMasked();
- if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) {
- mTouching = true;
- invalidate();
-
- mTouchX = event.getX();
- mTouchY = event.getY();
- } else if (action == MotionEvent.ACTION_UP) {
- mTouching = false;
- invalidate();
- }
- Trace.endSection();
- return true;
- }
-
- private void drawTouch(Canvas canvas) {
- Trace.beginSection("TouchLatencyView drawTouch");
-
- try {
- if (!mTouching) {
- Log.d(LOG_TAG, "Filling background");
- canvas.drawColor(BACKGROUND_COLOR);
- return;
- }
-
- float deltaX = (mTouchX - mLastDrawnX);
- float deltaY = (mTouchY - mLastDrawnY);
- float scaleFactor = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY) * 1.5f;
-
- mLastDrawnX = mTouchX;
- mLastDrawnY = mTouchY;
-
- canvas.drawColor(BACKGROUND_COLOR);
- canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 3 * scaleFactor, mRedPaint);
- canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 2 * scaleFactor, mYellowPaint);
- canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + scaleFactor, mGreenPaint);
- canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS, mBluePaint);
- } finally {
- Trace.endSection();
- }
- }
-
- private Paint getBallColor() {
- if (mFps > 75)
- return mGreenPaint;
- else if (mFps > 45)
- return mYellowPaint;
- else
- return mRedPaint;
- }
-
- private void drawBall(Canvas canvas) {
- Trace.beginSection("TouchLatencyView drawBall");
- int width = canvas.getWidth();
- int height = canvas.getHeight();
- float fps = 0f;
-
- long t = System.nanoTime();
- long tDiff = t - mLastDrawNano;
- mLastDrawNano = t;
- mFrameCount++;
-
- if (tDiff < SEC_TO_NANOS) {
- fps = 1f * SEC_TO_NANOS / tDiff;
- }
-
- long fDiff = t - mLastFpsUpdate;
- if (Math.abs(mFps - fps) > FPS_UPDATE_THRESHOLD) {
- mFps = fps;
- mLastFpsUpdate = t;
- mFrameCount = 0;
- } else if (fDiff > SEC_TO_NANOS) {
- mFps = 1f * mFrameCount * SEC_TO_NANOS / fDiff;
- mLastFpsUpdate = t;
- mFrameCount = 0;
- }
-
- final long pos = t * BALL_VELOCITY / SEC_TO_NANOS;
- final long xMax = width - BALL_DIAMETER;
- final long yMax = height - BALL_DIAMETER;
- long xOffset = pos % xMax;
- long yOffset = pos % yMax;
-
- float left, right, top, bottom;
-
- if (((pos / xMax) & 1) == 0) {
- left = xMax - xOffset;
- } else {
- left = xOffset;
- }
- right = left + BALL_DIAMETER;
-
- if (((pos / yMax) & 1) == 0) {
- top = yMax - yOffset;
- } else {
- top = yOffset;
- }
- bottom = top + BALL_DIAMETER;
-
- // Draw the ball
- canvas.drawColor(BACKGROUND_COLOR);
- canvas.drawOval(left, top, right, bottom, getBallColor());
- canvas.drawText(mDf.format(mFps), width, 100, mTextPaint);
-
- invalidate();
- Trace.endSection();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- Trace.beginSection("TouchLatencyView onDraw");
- if (mMode == 0) {
- drawTouch(canvas);
- } else {
- drawBall(canvas);
- }
- Trace.endSection();
- }
-
- public void changeMode(MenuItem item) {
- Trace.beginSection("TouchLatencyView changeMode");
- final int NUM_MODES = 2;
- final String modes[] = {"Touch", "Ball"};
- mMode = (mMode + 1) % NUM_MODES;
- invalidate();
- item.setTitle(modes[mMode]);
- Trace.endSection();
- }
-
- private final Paint mBluePaint, mGreenPaint, mYellowPaint, mRedPaint, mTextPaint;
- private int mMode;
-
- private boolean mTouching;
- private float mTouchX, mTouchY;
- private float mLastDrawnX, mLastDrawnY;
-
- private long mLastDrawNano, mLastFpsUpdate, mFrameCount;
- private float mFps;
- private DecimalFormat mDf;
-}
-
public class TouchLatencyActivity extends Activity {
private Mode mDisplayModes[];
private int mCurrentModeIndex;
+ private DisplayManager mDisplayManager;
+ private final DisplayManager.DisplayListener mDisplayListener =
+ new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int i) {
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public void onDisplayRemoved(int i) {
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public void onDisplayChanged(int i) {
+ invalidateOptionsMenu();
+ }
+ };
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -236,9 +58,9 @@
Trace.beginSection("TouchLatencyActivity onCreate");
setContentView(R.layout.activity_touch_latency);
-
mTouchView = findViewById(R.id.canvasView);
+ configureDisplayListener();
WindowManager wm = getWindowManager();
Display display = wm.getDefaultDisplay();
mDisplayModes = display.getSupportedModes();
@@ -250,11 +72,9 @@
break;
}
}
-
Trace.endSection();
}
-
@Override
public boolean onCreateOptionsMenu(Menu menu) {
Trace.beginSection("TouchLatencyActivity onCreateOptionsMenu");
@@ -265,17 +85,26 @@
Mode currentMode = getWindowManager().getDefaultDisplay().getMode();
updateDisplayMode(menuItem, currentMode);
}
+ updateMultiDisplayMenu(menu.findItem(R.id.multi_display));
Trace.endSection();
return true;
}
-
private void updateDisplayMode(MenuItem menuItem, Mode displayMode) {
int fps = (int) displayMode.getRefreshRate();
menuItem.setTitle(fps + "hz");
menuItem.setVisible(true);
}
+ private void updateMultiDisplayMenu(MenuItem item) {
+ item.setVisible(mDisplayManager.getDisplays().length > 1);
+ }
+
+ private void configureDisplayListener() {
+ mDisplayManager = getSystemService(DisplayManager.class);
+ mDisplayManager.registerDisplayListener(mDisplayListener, new Handler());
+ }
+
public void changeDisplayMode(MenuItem item) {
Window w = getWindow();
WindowManager.LayoutParams params = w.getAttributes();
@@ -299,6 +128,19 @@
mCurrentModeIndex = modeIndex;
}
+ private void changeMultipleDisplays() {
+ Intent intent = new Intent(this, TouchLatencyActivityPresentation.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK);
+ ActivityOptions options = ActivityOptions.makeBasic();
+ for (int i = 1; i < mDisplayManager.getDisplays().length; ++i) {
+ // We assume the first display is already displaying the TouchLatencyActivity
+ int displayId = mDisplayManager.getDisplays()[i].getDisplayId();
+ options.setLaunchDisplayId(displayId);
+ intent.putExtra(TouchLatencyActivityPresentation.DISPLAY_ID, displayId);
+ startActivity(intent, options.toBundle());
+ }
+ }
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
@@ -309,15 +151,32 @@
int id = item.getItemId();
//noinspection SimplifiableIfStatement
- if (id == R.id.action_settings) {
- mTouchView.changeMode(item);
- } else if (id == R.id.display_mode) {
- changeDisplayMode(item);
+ switch (id) {
+ case R.id.action_settings: {
+ mTouchView.changeMode(item);
+ break;
+ }
+ case R.id.display_mode: {
+ changeDisplayMode(item);
+ break;
+ }
+ case R.id.multi_display: {
+ changeMultipleDisplays();
+ break;
+ }
}
Trace.endSection();
return super.onOptionsItemSelected(item);
}
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mDisplayManager != null) {
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ }
+ }
+
private TouchLatencyView mTouchView;
}
diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivityPresentation.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivityPresentation.java
new file mode 100644
index 0000000..2602e6b
--- /dev/null
+++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivityPresentation.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.prefabulated.touchlatency;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Trace;
+import android.view.Display;
+import android.view.Display.Mode;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.Window;
+import android.view.WindowManager;
+
+public class TouchLatencyActivityPresentation extends Activity {
+ public static final String DISPLAY_ID = "DISPLAY_ID";
+ private Mode[] mDisplayModes;
+ private int mCurrentModeIndex;
+ private int mDisplayId;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (getIntent().hasExtra(DISPLAY_ID)) {
+ mDisplayId = (int) getIntent().getExtras().get(DISPLAY_ID);
+ }
+ Trace.beginSection(
+ "TouchLatencyActivityPresentation::DisplayId::" + mDisplayId + " onCreate");
+ setContentView(R.layout.activity_touch_latency);
+
+ mTouchView = findViewById(R.id.canvasView);
+
+ WindowManager wm = getWindowManager();
+ Display display = wm.getDefaultDisplay();
+ mDisplayModes = display.getSupportedModes();
+ Mode currentMode = getWindowManager().getDefaultDisplay().getMode();
+
+ for (int i = 0; i < mDisplayModes.length; i++) {
+ if (currentMode.getModeId() == mDisplayModes[i].getModeId()) {
+ mCurrentModeIndex = i;
+ break;
+ }
+ }
+ Trace.endSection();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ Trace.beginSection(
+ "TouchLatencyActivityPresentation::DisplayId:: "
+ + mDisplayId + " onCreateOptionsMenu");
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_touch_latency, menu);
+ if (mDisplayModes.length > 1) {
+ MenuItem menuItem = menu.findItem(R.id.display_mode);
+ Mode currentMode = getWindowManager().getDefaultDisplay().getMode();
+ updateDisplayMode(menuItem, currentMode);
+ }
+ Trace.endSection();
+ return true;
+ }
+
+ private void updateDisplayMode(MenuItem menuItem, Mode displayMode) {
+ int fps = (int) displayMode.getRefreshRate();
+ menuItem.setTitle(fps + "hz");
+ menuItem.setVisible(true);
+ }
+
+ public void changeDisplayMode(MenuItem item) {
+ Window w = getWindow();
+ WindowManager.LayoutParams params = w.getAttributes();
+
+ int modeIndex = (mCurrentModeIndex + 1) % mDisplayModes.length;
+ params.preferredDisplayModeId = mDisplayModes[modeIndex].getModeId();
+ w.setAttributes(params);
+
+ updateDisplayMode(item, mDisplayModes[modeIndex]);
+ mCurrentModeIndex = modeIndex;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ Trace.beginSection(
+ "TouchLatencyActivityPresentation::DisplayId::"
+ + mDisplayId + " onOptionsItemSelected");
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+
+ //noinspection SimplifiableIfStatement
+ switch (id) {
+ case R.id.action_settings: {
+ mTouchView.changeMode(item);
+ break;
+ }
+ case R.id.display_mode: {
+ changeDisplayMode(item);
+ break;
+ }
+ }
+
+ Trace.endSection();
+ return super.onOptionsItemSelected(item);
+ }
+
+ private TouchLatencyView mTouchView;
+}
diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyView.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyView.java
new file mode 100644
index 0000000..0803e8e
--- /dev/null
+++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyView.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.prefabulated.touchlatency;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Trace;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+
+class TouchLatencyView extends View implements View.OnTouchListener {
+ private static final String LOG_TAG = "TouchLatency";
+ private static final int BACKGROUND_COLOR = 0xFF400080;
+ private static final int INNER_RADIUS = 70;
+ private static final int BALL_DIAMETER = 200;
+ private static final int SEC_TO_NANOS = 1000000000;
+ private static final float FPS_UPDATE_THRESHOLD = 20;
+ private static final long BALL_VELOCITY = 420;
+
+ public TouchLatencyView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ Trace.beginSection("TouchLatencyView constructor");
+ setOnTouchListener(this);
+ setWillNotDraw(false);
+ mBluePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mBluePaint.setColor(0xFF0000FF);
+ mBluePaint.setStyle(Paint.Style.FILL);
+ mGreenPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mGreenPaint.setColor(0xFF00FF00);
+ mGreenPaint.setStyle(Paint.Style.FILL);
+ mYellowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mYellowPaint.setColor(0xFFFFFF00);
+ mYellowPaint.setStyle(Paint.Style.FILL);
+ mRedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mRedPaint.setColor(0xFFFF0000);
+ mRedPaint.setStyle(Paint.Style.FILL);
+ mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mTextPaint.setColor(0xFFFFFFFF);
+ mTextPaint.setTextSize(100);
+ mTextPaint.setTextAlign(Paint.Align.RIGHT);
+
+ mTouching = false;
+
+ mLastDrawNano = 0;
+ mFps = 0;
+ mLastFpsUpdate = 0;
+ mFrameCount = 0;
+
+ mDf = new DecimalFormat("fps: #.##");
+ mDf.setRoundingMode(RoundingMode.HALF_UP);
+
+ Trace.endSection();
+ }
+
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ Trace.beginSection("TouchLatencyView onTouch");
+ int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) {
+ mTouching = true;
+ invalidate();
+
+ mTouchX = event.getX();
+ mTouchY = event.getY();
+ } else if (action == MotionEvent.ACTION_UP) {
+ mTouching = false;
+ invalidate();
+ }
+ Trace.endSection();
+ return true;
+ }
+
+ private void drawTouch(Canvas canvas) {
+ Trace.beginSection("TouchLatencyView drawTouch");
+
+ try {
+ if (!mTouching) {
+ Log.d(LOG_TAG, "Filling background");
+ canvas.drawColor(BACKGROUND_COLOR);
+ return;
+ }
+
+ float deltaX = (mTouchX - mLastDrawnX);
+ float deltaY = (mTouchY - mLastDrawnY);
+ float scaleFactor = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY) * 1.5f;
+
+ mLastDrawnX = mTouchX;
+ mLastDrawnY = mTouchY;
+
+ canvas.drawColor(BACKGROUND_COLOR);
+ canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 3 * scaleFactor, mRedPaint);
+ canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 2 * scaleFactor, mYellowPaint);
+ canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + scaleFactor, mGreenPaint);
+ canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS, mBluePaint);
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ private Paint getBallColor() {
+ if (mFps > 75) {
+ return mGreenPaint;
+ } else if (mFps > 45) {
+ return mYellowPaint;
+ } else
+ return mRedPaint;
+ }
+
+ private void drawBall(Canvas canvas) {
+ Trace.beginSection("TouchLatencyView drawBall");
+ int width = canvas.getWidth();
+ int height = canvas.getHeight();
+ float fps = 0f;
+
+ long t = System.nanoTime();
+ long tDiff = t - mLastDrawNano;
+ mLastDrawNano = t;
+ mFrameCount++;
+
+ if (tDiff < SEC_TO_NANOS) {
+ fps = 1f * SEC_TO_NANOS / tDiff;
+ }
+
+ long fDiff = t - mLastFpsUpdate;
+ if (Math.abs(mFps - fps) > FPS_UPDATE_THRESHOLD) {
+ mFps = fps;
+ mLastFpsUpdate = t;
+ mFrameCount = 0;
+ } else if (fDiff > SEC_TO_NANOS) {
+ mFps = 1f * mFrameCount * SEC_TO_NANOS / fDiff;
+ mLastFpsUpdate = t;
+ mFrameCount = 0;
+ }
+
+ final long pos = t * BALL_VELOCITY / SEC_TO_NANOS;
+ final long xMax = width - BALL_DIAMETER;
+ final long yMax = height - BALL_DIAMETER;
+ long xOffset = pos % xMax;
+ long yOffset = pos % yMax;
+
+ float left, right, top, bottom;
+
+ if (((pos / xMax) & 1) == 0) {
+ left = xMax - xOffset;
+ } else {
+ left = xOffset;
+ }
+ right = left + BALL_DIAMETER;
+
+ if (((pos / yMax) & 1) == 0) {
+ top = yMax - yOffset;
+ } else {
+ top = yOffset;
+ }
+ bottom = top + BALL_DIAMETER;
+
+ // Draw the ball
+ canvas.drawColor(BACKGROUND_COLOR);
+ canvas.drawOval(left, top, right, bottom, getBallColor());
+ canvas.drawText(mDf.format(mFps), width, 100, mTextPaint);
+
+ invalidate();
+ Trace.endSection();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ Trace.beginSection("TouchLatencyView onDraw");
+ if (mMode == 0) {
+ drawTouch(canvas);
+ } else {
+ drawBall(canvas);
+ }
+ Trace.endSection();
+ }
+
+ public void changeMode(MenuItem item) {
+ Trace.beginSection("TouchLatencyView changeMode");
+ final int NUM_MODES = 2;
+ final String modes[] = {"Touch", "Ball"};
+ mMode = (mMode + 1) % NUM_MODES;
+ invalidate();
+ item.setTitle(modes[mMode]);
+ Trace.endSection();
+ }
+
+ private final Paint mBluePaint, mGreenPaint, mYellowPaint, mRedPaint, mTextPaint;
+ private int mMode;
+
+ private boolean mTouching;
+ private float mTouchX, mTouchY;
+ private float mLastDrawnX, mLastDrawnY;
+
+ private long mLastDrawNano, mLastFpsUpdate, mFrameCount;
+ private float mFps;
+ private DecimalFormat mDf;
+}
diff --git a/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml b/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml
index 52be919..abc7fd5 100644
--- a/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml
+++ b/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml
@@ -25,4 +25,10 @@
android:showAsAction="ifRoom"
android:title="@string/display_mode"
android:visible="false"/>
+
+ <item
+ android:id="@+id/multi_display"
+ android:showAsAction="ifRoom"
+ android:title="@string/multi_display"
+ android:visible="false"/>
</menu>
diff --git a/tests/TouchLatency/app/src/main/res/values/strings.xml b/tests/TouchLatency/app/src/main/res/values/strings.xml
index 771992c..5ee86d8 100644
--- a/tests/TouchLatency/app/src/main/res/values/strings.xml
+++ b/tests/TouchLatency/app/src/main/res/values/strings.xml
@@ -18,4 +18,5 @@
<string name="mode">Touch</string>
<string name="display_mode">Mode</string>
+ <string name="multi_display">multi-display</string>
</resources>
diff --git a/tests/TouchLatency/build.gradle b/tests/TouchLatency/build.gradle
index 03abe82..381e55e 100644
--- a/tests/TouchLatency/build.gradle
+++ b/tests/TouchLatency/build.gradle
@@ -6,7 +6,7 @@
google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.2.1'
+ classpath 'com.android.tools.build:gradle:4.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
index 2d80b69..4d9ca16 100644
--- a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
+++ b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists