Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b354fe08 authored by Ram Indani's avatar Ram Indani Committed by Android (Google) Code Review
Browse files

Merge "Add support to render app on multiple displays"

parents dd028ec3 1a4ceb79
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
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"
    }
+8 −2
Original line number Diff line number Diff line
@@ -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>
+65 −206
Original line number Diff line number Diff line
@@ -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();
    }

public class TouchLatencyActivity extends Activity {
    private Mode mDisplayModes[];
    private int mCurrentModeIndex;
    private DisplayManager mDisplayManager;
    private final DisplayManager.DisplayListener mDisplayListener =
            new DisplayManager.DisplayListener() {
        @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();
        public void onDisplayAdded(int i) {
            invalidateOptionsMenu();
        }

        @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();
        public void onDisplayRemoved(int i) {
            invalidateOptionsMenu();
        }

    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;
        @Override
        public void onDisplayChanged(int i) {
            invalidateOptionsMenu();
        }

public class TouchLatencyActivity extends Activity {
    private Mode mDisplayModes[];
    private int mCurrentModeIndex;
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
@@ -236,9 +58,9 @@ public class TouchLatencyActivity extends Activity {

        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 @@ public class TouchLatencyActivity extends Activity {
                break;
            }
        }

        Trace.endSection();
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        Trace.beginSection("TouchLatencyActivity onCreateOptionsMenu");
@@ -265,17 +85,26 @@ public class TouchLatencyActivity extends Activity {
            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 @@ public class TouchLatencyActivity extends Activity {
        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 @@ public class TouchLatencyActivity extends Activity {
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
        switch (id) {
            case R.id.action_settings: {
                mTouchView.changeMode(item);
        } else if (id == R.id.display_mode) {
                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;
}
+128 −0
Original line number Diff line number Diff line
/*
 * 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;
}
+219 −0
Original line number Diff line number Diff line
/*
 * 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;
}
Loading