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

Commit 9be12da0 authored by Prabir Pradhan's avatar Prabir Pradhan Committed by Android (Google) Code Review
Browse files

Merge changes I4dfe7970,I47a3d668,Id2e77451 into main

* changes:
  PointerLocationView: Draw pointers in unrotated display space
  PointerLocationView: Reland optimization for trace drawing
  PointerLocationView: Reformat file
parents 02e6e665 86494fef
Loading
Loading
Loading
Loading
+162 −90
Original line number Diff line number Diff line
@@ -16,14 +16,19 @@

package com.android.internal.widget;

import static java.lang.Float.NaN;

import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.RectF;
import android.graphics.Region;
import android.hardware.input.InputManager;
@@ -40,6 +45,7 @@ import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.RoundedCorner;
import android.view.Surface;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
@@ -65,18 +71,21 @@ public class PointerLocationView extends View implements InputDeviceListener,
    private static final PointerState EMPTY_POINTER_STATE = new PointerState();

    public static class PointerState {
        // Trace of previous points.
        private float[] mTraceX = new float[32];
        private float[] mTraceY = new float[32];
        private boolean[] mTraceCurrent = new boolean[32];
        private int mTraceCount;
        private float mCurrentX = NaN;
        private float mCurrentY = NaN;
        private float mPreviousX = NaN;
        private float mPreviousY = NaN;
        private float mFirstX = NaN;
        private float mFirstY = NaN;
        private boolean mPreviousPointIsHistorical;
        private boolean mCurrentPointIsHistorical;

        // True if the pointer is down.
        @UnsupportedAppUsage
        private boolean mCurDown;

        // Most recent coordinates.
        private PointerCoords mCoords = new PointerCoords();
        private final PointerCoords mCoords = new PointerCoords();
        private int mToolType;

        // Most recent velocity.
@@ -96,31 +105,20 @@ public class PointerLocationView extends View implements InputDeviceListener,
        public PointerState() {
        }

        public void clearTrace() {
            mTraceCount = 0;
        void addTrace(float x, float y, boolean isHistorical) {
            if (Float.isNaN(mFirstX)) {
                mFirstX = x;
            }

        public void addTrace(float x, float y, boolean current) {
            int traceCapacity = mTraceX.length;
            if (mTraceCount == traceCapacity) {
                traceCapacity *= 2;
                float[] newTraceX = new float[traceCapacity];
                System.arraycopy(mTraceX, 0, newTraceX, 0, mTraceCount);
                mTraceX = newTraceX;

                float[] newTraceY = new float[traceCapacity];
                System.arraycopy(mTraceY, 0, newTraceY, 0, mTraceCount);
                mTraceY = newTraceY;

                boolean[] newTraceCurrent = new boolean[traceCapacity];
                System.arraycopy(mTraceCurrent, 0, newTraceCurrent, 0, mTraceCount);
                mTraceCurrent= newTraceCurrent;
            if (Float.isNaN(mFirstY)) {
                mFirstY = y;
            }

            mTraceX[mTraceCount] = x;
            mTraceY[mTraceCount] = y;
            mTraceCurrent[mTraceCount] = current;
            mTraceCount += 1;
            mPreviousX = mCurrentX;
            mPreviousY = mCurrentY;
            mCurrentX = x;
            mCurrentY = y;
            mPreviousPointIsHistorical = mCurrentPointIsHistorical;
            mCurrentPointIsHistorical = isHistorical;
        }
    }

@@ -149,6 +147,13 @@ public class PointerLocationView extends View implements InputDeviceListener,
    private final SparseArray<PointerState> mPointers = new SparseArray<PointerState>();
    private final PointerCoords mTempCoords = new PointerCoords();

    // Draw the trace of all pointers in the current gesture in a separate layer
    // that is not cleared on every frame so that we don't have to re-draw the
    // entire trace on each frame. The trace bitmap is in the coordinate space of the unrotated
    // display.
    private Bitmap mTraceBitmap;
    private final Canvas mTraceCanvas;

    private final Region mSystemGestureExclusion = new Region();
    private final Region mSystemGestureExclusionRejected = new Region();
    private final Path mSystemGestureExclusionPath = new Path();
@@ -197,6 +202,9 @@ public class PointerLocationView extends View implements InputDeviceListener,
        mPathPaint.setARGB(255, 0, 96, 255);
        mPathPaint.setStyle(Paint.Style.STROKE);

        mTraceCanvas = new Canvas();
        configureTraceBitmap();

        configureDensityDependentFactors();

        mSystemGestureExclusionPaint = new Paint();
@@ -268,7 +276,8 @@ public class PointerLocationView extends View implements InputDeviceListener,

    // Draw an oval.  When angle is 0 radians, orients the major axis vertically,
    // angles less than or greater than 0 radians rotate the major axis left or right.
    private RectF mReusableOvalRect = new RectF();
    private final RectF mReusableOvalRect = new RectF();

    private void drawOval(Canvas canvas, float x, float y, float major, float minor,
            float angle, Paint paint) {
        canvas.save(Canvas.MATRIX_SAVE_FLAG);
@@ -285,6 +294,12 @@ public class PointerLocationView extends View implements InputDeviceListener,
    protected void onDraw(Canvas canvas) {
        final int NP = mPointers.size();

        // Pointer trace.
        canvas.save();
        rotateCanvasToUnrotatedDisplay(canvas);
        canvas.drawBitmap(mTraceBitmap, 0, 0, null);
        canvas.restore();

        if (!mSystemGestureExclusion.isEmpty()) {
            mSystemGestureExclusionPath.reset();
            mSystemGestureExclusion.getBoundaryPath(mSystemGestureExclusionPath);
@@ -300,35 +315,14 @@ public class PointerLocationView extends View implements InputDeviceListener,
        // Labels
        drawLabels(canvas);

        // Pointer trace.
        // Current pointer states.
        canvas.save();
        rotateCanvasToUnrotatedDisplay(canvas);
        for (int p = 0; p < NP; p++) {
            final PointerState ps = mPointers.valueAt(p);
            float lastX = ps.mCurrentX, lastY = ps.mCurrentY;

            // Draw path.
            final int N = ps.mTraceCount;
            float lastX = 0, lastY = 0;
            boolean haveLast = false;
            boolean drawn = false;
            mPaint.setARGB(255, 128, 255, 255);
            for (int i=0; i < N; i++) {
                float x = ps.mTraceX[i];
                float y = ps.mTraceY[i];
                if (Float.isNaN(x) || Float.isNaN(y)) {
                    haveLast = false;
                    continue;
                }
                if (haveLast) {
                    canvas.drawLine(lastX, lastY, x, y, mPathPaint);
                    final Paint paint = ps.mTraceCurrent[i - 1] ? mCurrentPointPaint : mPaint;
                    canvas.drawPoint(lastX, lastY, paint);
                    drawn = true;
                }
                lastX = x;
                lastY = y;
                haveLast = true;
            }

            if (drawn) {
            if (!Float.isNaN(lastX) && !Float.isNaN(lastY)) {
                // Draw velocity vector.
                mPaint.setARGB(255, 255, 64, 128);
                float xVel = ps.mXVelocity * (1000 / 60);
@@ -406,6 +400,7 @@ public class PointerLocationView extends View implements InputDeviceListener,
                }
            }
        }
        canvas.restore();
    }

    private void drawLabels(Canvas canvas) {
@@ -424,8 +419,7 @@ public class PointerLocationView extends View implements InputDeviceListener,
                .append(" / ").append(mMaxNumPointers)
                .toString(), 1, base, mTextPaint);

        final int count = ps.mTraceCount;
        if ((mCurDown && ps.mCurDown) || count == 0) {
        if ((mCurDown && ps.mCurDown) || Float.isNaN(ps.mCurrentX)) {
            canvas.drawRect(itemW, mHeaderPaddingTop, (itemW * 2) - 1, bottom,
                    mTextBackgroundPaint);
            canvas.drawText(mText.clear()
@@ -437,8 +431,8 @@ public class PointerLocationView extends View implements InputDeviceListener,
                    .append("Y: ").append(ps.mCoords.y, 1)
                    .toString(), 1 + itemW * 2, base, mTextPaint);
        } else {
            float dx = ps.mTraceX[count - 1] - ps.mTraceX[0];
            float dy = ps.mTraceY[count - 1] - ps.mTraceY[0];
            float dx = ps.mCurrentX - ps.mFirstX;
            float dy = ps.mCurrentY - ps.mFirstY;
            canvas.drawRect(itemW, mHeaderPaddingTop, (itemW * 2) - 1, bottom,
                    Math.abs(dx) < mVC.getScaledTouchSlop()
                            ? mTextBackgroundPaint : mTextLevelPaint);
@@ -586,6 +580,11 @@ public class PointerLocationView extends View implements InputDeviceListener,

    @Override
    public void onPointerEvent(MotionEvent event) {
        // PointerLocationView stores and draws events in the unrotated display space, so undo the
        // event's rotation to bring it back to the unrotated display space.
        event.transform(MotionEvent.createRotateMatrix(inverseRotation(event.getSurfaceRotation()),
                mTraceBitmap.getWidth(), mTraceBitmap.getHeight()));

        final int action = event.getAction();

        if (action == MotionEvent.ACTION_DOWN
@@ -598,6 +597,7 @@ public class PointerLocationView extends View implements InputDeviceListener,
                mCurNumPointers = 0;
                mMaxNumPointers = 0;
                mVelocity.clear();
                mTraceCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                if (mAltVelocity != null) {
                    mAltVelocity.clear();
                }
@@ -646,7 +646,8 @@ public class PointerLocationView extends View implements InputDeviceListener,
                    logCoords("Pointer", action, i, coords, id, event);
                }
                if (ps != null) {
                    ps.addTrace(coords.x, coords.y, false);
                    ps.addTrace(coords.x, coords.y, /*isHistorical*/ true);
                    updateDrawTrace(ps);
                }
            }
        }
@@ -659,7 +660,8 @@ public class PointerLocationView extends View implements InputDeviceListener,
                logCoords("Pointer", action, i, coords, id, event);
            }
            if (ps != null) {
                ps.addTrace(coords.x, coords.y, true);
                ps.addTrace(coords.x, coords.y, /*isHistorical*/ false);
                updateDrawTrace(ps);
                ps.mXVelocity = mVelocity.getXVelocity(id);
                ps.mYVelocity = mVelocity.getYVelocity(id);
                if (mAltVelocity != null) {
@@ -702,13 +704,27 @@ public class PointerLocationView extends View implements InputDeviceListener,
                if (mActivePointerId == id) {
                    mActivePointerId = event.getPointerId(index == 0 ? 1 : 0);
                }
                ps.addTrace(Float.NaN, Float.NaN, false);
                ps.addTrace(Float.NaN, Float.NaN, true);
            }
        }

        invalidate();
    }

    private void updateDrawTrace(PointerState ps) {
        mPaint.setARGB(255, 128, 255, 255);
        float x = ps.mCurrentX;
        float y = ps.mCurrentY;
        float lastX = ps.mPreviousX;
        float lastY = ps.mPreviousY;
        if (Float.isNaN(x) || Float.isNaN(y) || Float.isNaN(lastX) || Float.isNaN(lastY)) {
            return;
        }
        mTraceCanvas.drawLine(lastX, lastY, x, y, mPathPaint);
        Paint paint = ps.mPreviousPointIsHistorical ? mPaint : mCurrentPointPaint;
        mTraceCanvas.drawPoint(lastX, lastY, paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        onPointerEvent(event);
@@ -971,10 +987,11 @@ public class PointerLocationView extends View implements InputDeviceListener,
        }
    }

    private ISystemGestureExclusionListener mSystemGestureExclusionListener =
    private final ISystemGestureExclusionListener mSystemGestureExclusionListener =
            new ISystemGestureExclusionListener.Stub() {
                @Override
        public void onSystemGestureExclusionChanged(int displayId, Region systemGestureExclusion,
                public void onSystemGestureExclusionChanged(int displayId,
                        Region systemGestureExclusion,
                        Region systemGestureExclusionUnrestricted) {
                    Region exclusion = Region.obtain(systemGestureExclusion);
                    Region rejected = Region.obtain();
@@ -997,6 +1014,7 @@ public class PointerLocationView extends View implements InputDeviceListener,
    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        configureTraceBitmap();
        configureDensityDependentFactors();
    }

@@ -1008,4 +1026,58 @@ public class PointerLocationView extends View implements InputDeviceListener,
        mCurrentPointPaint.setStrokeWidth(1 * mDensity);
        mPathPaint.setStrokeWidth(1 * mDensity);
    }

    private void configureTraceBitmap() {
        final var display = mContext.getDisplay();
        final boolean rotated = display.getRotation() == Surface.ROTATION_90
                || display.getRotation() == Surface.ROTATION_270;
        int unrotatedWidth = rotated ? display.getHeight() : display.getWidth();
        int unrotatedHeight = rotated ? display.getWidth() : display.getHeight();

        if (mTraceBitmap != null && mTraceBitmap.getWidth() == unrotatedWidth
                && mTraceBitmap.getHeight() == unrotatedHeight) {
            return;
        }
        if (unrotatedWidth <= 0 || unrotatedHeight <= 0) {
            Slog.w(TAG, "Ignoring configuration: invalid display size: " + unrotatedWidth + "x"
                    + unrotatedHeight);
            // Initialize the bitmap to an arbitrary size. It should be reconfigured with a valid
            // size in the future.
            unrotatedWidth = 100;
            unrotatedHeight = 100;
        }
        mTraceBitmap = Bitmap.createBitmap(unrotatedWidth, unrotatedHeight,
                Bitmap.Config.ARGB_8888);
        mTraceCanvas.setBitmap(mTraceBitmap);
    }

    private static int inverseRotation(@Surface.Rotation int rotation) {
        return switch(rotation) {
            case Surface.ROTATION_0 -> Surface.ROTATION_0;
            case Surface.ROTATION_90 -> Surface.ROTATION_270;
            case Surface.ROTATION_180 -> Surface.ROTATION_180;
            case Surface.ROTATION_270 -> Surface.ROTATION_90;
            default -> {
                Slog.e(TAG, "Received unexpected surface rotation: " + rotation);
                yield Surface.ROTATION_0;
            }
        };
    }

    private void rotateCanvasToUnrotatedDisplay(Canvas c) {
        switch (inverseRotation(mContext.getDisplay().getRotation())) {
            case Surface.ROTATION_90 -> {
                c.rotate(90);
                c.translate(0, -mTraceBitmap.getHeight());
            }
            case Surface.ROTATION_180 -> {
                c.rotate(180);
                c.translate(-mTraceBitmap.getWidth(), -mTraceBitmap.getHeight());
            }
            case Surface.ROTATION_270 -> {
                c.rotate(270);
                c.translate(-mTraceBitmap.getWidth(), 0);
            }
        }
    }
}