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

Commit c8c41e48 authored by Abdelrahman Awadalla's avatar Abdelrahman Awadalla
Browse files

An optimization for Pointer Location for drawing the trace

In the past, the history of the X and Y coordinates was stored in two arrays which had initial sizes of 32 each and whenever a new pointer event is received it stores that into the first empty slot in the array. But, whenever it reaches the end of the array it starts to double the size of it and copy the data to the doubled size array, giving a indefinite space complexity to that algorithm. It was also noticed that after some time the device goes laggy and the Pointer Location is so delayed.

This is an optimization to change the space complexity from indefinite to O(1) by doing a bitmap of the size of the device's screen and draw the trace on it. Whenever a new pointer event is send it will draw a line from the last X and Y coordinates to the current ones and save it in that bitmap.

Test: Build
Test: Presubmit checks
Bug: 283195699
Flag: EXEMPT bugfix
Change-Id: I6a5c5953ef80537e7066396e085cb15ad67f33b7
parent a8a1fbb0
Loading
Loading
Loading
Loading
+89 −86
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;
@@ -65,11 +70,14 @@ 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
@@ -96,31 +104,20 @@ public class PointerLocationView extends View implements InputDeviceListener,
        public PointerState() {
        }

        public void clearTrace() {
            mTraceCount = 0;
        public 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 +146,12 @@ 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.
    private final 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 +200,10 @@ public class PointerLocationView extends View implements InputDeviceListener,
        mPathPaint.setARGB(255, 0, 96, 255);
        mPathPaint.setStyle(Paint.Style.STROKE);

        mTraceBitmap = Bitmap.createBitmap(getResources().getDisplayMetrics().widthPixels,
                getResources().getDisplayMetrics().heightPixels, Bitmap.Config.ARGB_8888);
        mTraceCanvas = new Canvas(mTraceBitmap);

        configureDensityDependentFactors();

        mSystemGestureExclusionPaint = new Paint();
@@ -269,6 +276,7 @@ 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 void drawOval(Canvas canvas, float x, float y, float major, float minor,
            float angle, Paint paint) {
        canvas.save(Canvas.MATRIX_SAVE_FLAG);
@@ -285,6 +293,8 @@ public class PointerLocationView extends View implements InputDeviceListener,
    protected void onDraw(Canvas canvas) {
        final int NP = mPointers.size();

        canvas.drawBitmap(mTraceBitmap, 0, 0, null);

        if (!mSystemGestureExclusion.isEmpty()) {
            mSystemGestureExclusionPath.reset();
            mSystemGestureExclusion.getBoundaryPath(mSystemGestureExclusionPath);
@@ -303,32 +313,9 @@ public class PointerLocationView extends View implements InputDeviceListener,
        // Pointer trace.
        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);
@@ -424,8 +411,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 +423,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);
@@ -598,6 +584,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 +633,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, true);
                    updateDrawTrace(ps);
                }
            }
        }
@@ -659,7 +647,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, false);
                updateDrawTrace(ps);
                ps.mXVelocity = mVelocity.getXVelocity(id);
                ps.mYVelocity = mVelocity.getYVelocity(id);
                if (mAltVelocity != null) {
@@ -702,13 +691,26 @@ 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)) {
            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);
@@ -974,7 +976,8 @@ public class PointerLocationView extends View implements InputDeviceListener,
    private 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();