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

Commit 96898b38 authored by Dianne Hackborn's avatar Dianne Hackborn Committed by Android (Google) Code Review
Browse files

Merge "Add Pointer Location to the window manager."

parents d85639af 90d2db3d
Loading
Loading
Loading
Loading
+333 −0
Original line number Diff line number Diff line
package com.android.common.ui;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;

import java.util.ArrayList;

public class PointerLocationView extends View {
    public static class PointerState {
        private final ArrayList<Float> mXs = new ArrayList<Float>();
        private final ArrayList<Float> mYs = new ArrayList<Float>();
        private boolean mCurDown;
        private int mCurX;
        private int mCurY;
        private float mCurPressure;
        private float mCurSize;
        private int mCurWidth;
        private VelocityTracker mVelocity;
    }

    private final ViewConfiguration mVC;
    private final Paint mTextPaint;
    private final Paint mTextBackgroundPaint;
    private final Paint mTextLevelPaint;
    private final Paint mPaint;
    private final Paint mTargetPaint;
    private final Paint mPathPaint;
    private final FontMetricsInt mTextMetrics = new FontMetricsInt();
    private int mHeaderBottom;
    private boolean mCurDown;
    private int mCurNumPointers;
    private int mMaxNumPointers;
    private final ArrayList<PointerState> mPointers
             = new ArrayList<PointerState>();
    
    private boolean mPrintCoords = true;
    
    public PointerLocationView(Context c) {
        super(c);
        mVC = ViewConfiguration.get(c);
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(10
                * getResources().getDisplayMetrics().density);
        mTextPaint.setARGB(255, 0, 0, 0);
        mTextBackgroundPaint = new Paint();
        mTextBackgroundPaint.setAntiAlias(false);
        mTextBackgroundPaint.setARGB(128, 255, 255, 255);
        mTextLevelPaint = new Paint();
        mTextLevelPaint.setAntiAlias(false);
        mTextLevelPaint.setARGB(192, 255, 0, 0);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setARGB(255, 255, 255, 255);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(2);
        mTargetPaint = new Paint();
        mTargetPaint.setAntiAlias(false);
        mTargetPaint.setARGB(255, 0, 0, 192);
        mPathPaint = new Paint();
        mPathPaint.setAntiAlias(false);
        mPathPaint.setARGB(255, 0, 96, 255);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(1);
        
        PointerState ps = new PointerState();
        ps.mVelocity = VelocityTracker.obtain();
        mPointers.add(ps);
    }

    public void setPrintCoords(boolean state) {
        mPrintCoords = state;
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mTextPaint.getFontMetricsInt(mTextMetrics);
        mHeaderBottom = -mTextMetrics.ascent+mTextMetrics.descent+2;
        if (false) {
            Log.i("foo", "Metrics: ascent=" + mTextMetrics.ascent
                    + " descent=" + mTextMetrics.descent
                    + " leading=" + mTextMetrics.leading
                    + " top=" + mTextMetrics.top
                    + " bottom=" + mTextMetrics.bottom);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        synchronized (mPointers) {
            final int w = getWidth();
            final int itemW = w/7;
            final int base = -mTextMetrics.ascent+1;
            final int bottom = mHeaderBottom;
            
            final int NP = mPointers.size();
            
            if (NP > 0) {
                final PointerState ps = mPointers.get(0);
                canvas.drawRect(0, 0, itemW-1, bottom,mTextBackgroundPaint);
                canvas.drawText("P: " + mCurNumPointers + " / " + mMaxNumPointers,
                        1, base, mTextPaint);
                
                final int N = ps.mXs.size();
                if ((mCurDown && ps.mCurDown) || N == 0) {
                    canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, mTextBackgroundPaint);
                    canvas.drawText("X: " + ps.mCurX, 1 + itemW, base, mTextPaint);
                    canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom, mTextBackgroundPaint);
                    canvas.drawText("Y: " + ps.mCurY, 1 + itemW * 2, base, mTextPaint);
                } else {
                    float dx = ps.mXs.get(N-1) - ps.mXs.get(0);
                    float dy = ps.mYs.get(N-1) - ps.mYs.get(0);
                    canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom,
                            Math.abs(dx) < mVC.getScaledTouchSlop()
                            ? mTextBackgroundPaint : mTextLevelPaint);
                    canvas.drawText("dX: " + String.format("%.1f", dx), 1 + itemW, base, mTextPaint);
                    canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom,
                            Math.abs(dy) < mVC.getScaledTouchSlop()
                            ? mTextBackgroundPaint : mTextLevelPaint);
                    canvas.drawText("dY: " + String.format("%.1f", dy), 1 + itemW * 2, base, mTextPaint);
                }
                
                canvas.drawRect(itemW * 3, 0, (itemW * 4) - 1, bottom, mTextBackgroundPaint);
                int velocity = ps.mVelocity == null ? 0 : (int) (ps.mVelocity.getXVelocity() * 1000);
                canvas.drawText("Xv: " + velocity, 1 + itemW * 3, base, mTextPaint);
                
                canvas.drawRect(itemW * 4, 0, (itemW * 5) - 1, bottom, mTextBackgroundPaint);
                velocity = ps.mVelocity == null ? 0 : (int) (ps.mVelocity.getYVelocity() * 1000);
                canvas.drawText("Yv: " + velocity, 1 + itemW * 4, base, mTextPaint);
                
                canvas.drawRect(itemW * 5, 0, (itemW * 6) - 1, bottom, mTextBackgroundPaint);
                canvas.drawRect(itemW * 5, 0, (itemW * 5) + (ps.mCurPressure * itemW) - 1,
                        bottom, mTextLevelPaint);
                canvas.drawText("Prs: " + String.format("%.2f", ps.mCurPressure), 1 + itemW * 5,
                        base, mTextPaint);
                
                canvas.drawRect(itemW * 6, 0, w, bottom, mTextBackgroundPaint);
                canvas.drawRect(itemW * 6, 0, (itemW * 6) + (ps.mCurSize * itemW) - 1,
                        bottom, mTextLevelPaint);
                canvas.drawText("Size: " + String.format("%.2f", ps.mCurSize), 1 + itemW * 6,
                        base, mTextPaint);
            }
            
            for (int p=0; p<NP; p++) {
                final PointerState ps = mPointers.get(p);
                
                if (mCurDown && ps.mCurDown) {
                    canvas.drawLine(0, (int)ps.mCurY, getWidth(), (int)ps.mCurY, mTargetPaint);
                    canvas.drawLine((int)ps.mCurX, 0, (int)ps.mCurX, getHeight(), mTargetPaint);
                    int pressureLevel = (int)(ps.mCurPressure*255);
                    mPaint.setARGB(255, pressureLevel, 128, 255-pressureLevel);
                    canvas.drawPoint(ps.mCurX, ps.mCurY, mPaint);
                    canvas.drawCircle(ps.mCurX, ps.mCurY, ps.mCurWidth, mPaint);
                }
            }
            
            for (int p=0; p<NP; p++) {
                final PointerState ps = mPointers.get(p);
                
                final int N = ps.mXs.size();
                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.mXs.get(i);
                    float y = ps.mYs.get(i);
                    if (Float.isNaN(x)) {
                        haveLast = false;
                        continue;
                    }
                    if (haveLast) {
                        canvas.drawLine(lastX, lastY, x, y, mPathPaint);
                        canvas.drawPoint(lastX, lastY, mPaint);
                        drawn = true;
                    }
                    lastX = x;
                    lastY = y;
                    haveLast = true;
                }
                
                if (drawn) {
                    if (ps.mVelocity != null) {
                        mPaint.setARGB(255, 255, 64, 128);
                        float xVel = ps.mVelocity.getXVelocity() * (1000/60);
                        float yVel = ps.mVelocity.getYVelocity() * (1000/60);
                        canvas.drawLine(lastX, lastY, lastX+xVel, lastY+yVel, mPaint);
                    } else {
                        canvas.drawPoint(lastX, lastY, mPaint);
                    }
                }
            }
        }
    }

    public void addTouchEvent(MotionEvent event) {
        synchronized (mPointers) {
            int action = event.getAction();
            
            //Log.i("Pointer", "Motion: action=0x" + Integer.toHexString(action)
            //        + " pointers=" + event.getPointerCount());
            
            int NP = mPointers.size();
            
            //mRect.set(0, 0, getWidth(), mHeaderBottom+1);
            //invalidate(mRect);
            //if (mCurDown) {
            //    mRect.set(mCurX-mCurWidth-3, mCurY-mCurWidth-3,
            //            mCurX+mCurWidth+3, mCurY+mCurWidth+3);
            //} else {
            //    mRect.setEmpty();
            //}
            if (action == MotionEvent.ACTION_DOWN) {
                for (int p=0; p<NP; p++) {
                    final PointerState ps = mPointers.get(p);
                    ps.mXs.clear();
                    ps.mYs.clear();
                    ps.mVelocity = VelocityTracker.obtain();
                    ps.mCurDown = false;
                }
                mPointers.get(0).mCurDown = true;
                mMaxNumPointers = 0;
                if (mPrintCoords) {
                    Log.i("Pointer", "Pointer 1: DOWN");
                }
            }
            
            if ((action&MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) {
                final int id = (action&MotionEvent.ACTION_POINTER_ID_MASK)
                        >> MotionEvent.ACTION_POINTER_ID_SHIFT;
                while (NP <= id) {
                    PointerState ps = new PointerState();
                    ps.mVelocity = VelocityTracker.obtain();
                    mPointers.add(ps);
                    NP++;
                }
                final PointerState ps = mPointers.get(id);
                ps.mVelocity = VelocityTracker.obtain();
                ps.mCurDown = true;
                if (mPrintCoords) {
                    Log.i("Pointer", "Pointer " + (id+1) + ": DOWN");
                }
            }
            
            final int NI = event.getPointerCount();
            
            mCurDown = action != MotionEvent.ACTION_UP
                    && action != MotionEvent.ACTION_CANCEL;
            mCurNumPointers = mCurDown ? NI : 0;
            if (mMaxNumPointers < mCurNumPointers) {
                mMaxNumPointers = mCurNumPointers;
            }
            
            for (int i=0; i<NI; i++) {
                final PointerState ps = mPointers.get(event.getPointerId(i));
                ps.mVelocity.addMovement(event);
                ps.mVelocity.computeCurrentVelocity(1);
                final int N = event.getHistorySize();
                for (int j=0; j<N; j++) {
                    if (mPrintCoords) {
                        Log.i("Pointer", "Pointer " + (i+1) + ": ("
                                + event.getHistoricalX(i, j)
                                + ", " + event.getHistoricalY(i, j) + ")"
                                + " Prs=" + event.getHistoricalPressure(i, j)
                                + " Size=" + event.getHistoricalSize(i, j));
                    }
                    ps.mXs.add(event.getHistoricalX(i, j));
                    ps.mYs.add(event.getHistoricalY(i, j));
                }
                if (mPrintCoords) {
                    Log.i("Pointer", "Pointer " + (i+1) + ": ("
                            + event.getX(i) + ", " + event.getY(i) + ")"
                            + " Prs=" + event.getPressure(i)
                            + " Size=" + event.getSize(i));
                }
                ps.mXs.add(event.getX(i));
                ps.mYs.add(event.getY(i));
                ps.mCurX = (int)event.getX(i);
                ps.mCurY = (int)event.getY(i);
                //Log.i("Pointer", "Pointer #" + p + ": (" + ps.mCurX
                //        + "," + ps.mCurY + ")");
                ps.mCurPressure = event.getPressure(i);
                ps.mCurSize = event.getSize(i);
                ps.mCurWidth = (int)(ps.mCurSize*(getWidth()/3));
            }
            
            if ((action&MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) {
                final int id = (action&MotionEvent.ACTION_POINTER_ID_MASK)
                        >> MotionEvent.ACTION_POINTER_ID_SHIFT;
                final PointerState ps = mPointers.get(id);
                ps.mXs.add(Float.NaN);
                ps.mYs.add(Float.NaN);
                ps.mCurDown = false;
                if (mPrintCoords) {
                    Log.i("Pointer", "Pointer " + (id+1) + ": UP");
                }
            }
            
            if (action == MotionEvent.ACTION_UP) {
                for (int i=0; i<NI; i++) {
                    final PointerState ps = mPointers.get(event.getPointerId(i));
                    if (ps.mCurDown) {
                        ps.mCurDown = false;
                        if (mPrintCoords) {
                            Log.i("Pointer", "Pointer " + (i+1) + ": UP");
                        }
                    }
                }
            }
            
            //if (mCurDown) {
            //    mRect.union(mCurX-mCurWidth-3, mCurY-mCurWidth-3,
            //            mCurX+mCurWidth+3, mCurY+mCurWidth+3);
            //}
            //invalidate(mRect);
            postInvalidate();
        }
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        addTouchEvent(event);
        return true;
    }
}
+8 −0
Original line number Diff line number Diff line
@@ -1488,6 +1488,14 @@ public final class Settings {
         */
        public static final String DEFAULT_INSTALL_LOCATION = "default_install_location";

        /**
         * Show pointer location on screen?
         * 0 = no
         * 1 = yes
         * @hide
         */
        public static final String POINTER_LOCATION = "pointer_location";

        /**
         * Settings to backup. This is here so that it's in the same place as the settings
         * keys and easy to update.
+5 −0
Original line number Diff line number Diff line
@@ -851,6 +851,11 @@ public interface WindowManagerPolicy {
     */
    public boolean isCheekPressedAgainstScreen(MotionEvent ev);
    
    /**
     * Called every time the window manager is dispatching a pointer event.
     */
    public void dispatchedPointerEventLw(MotionEvent ev, int targetX, int targetY);
    
    public void setCurrentOrientationLw(int newOrientation);
    
    /**
+31 −23
Original line number Diff line number Diff line
@@ -1773,6 +1773,32 @@ public class WindowManagerService extends IWindowManager.Stub
        }
    }

    void dispatchPointerElsewhereLocked(WindowState srcWin, WindowState relWin,
            MotionEvent pointer, long eventTime, boolean skipped) {
        if (relWin != null) {
            mPolicy.dispatchedPointerEventLw(pointer, relWin.mFrame.left, relWin.mFrame.top);
        } else {
            mPolicy.dispatchedPointerEventLw(pointer, 0, 0);
        }
        
        // If we sent an initial down to the wallpaper, then continue
        // sending events until the final up.
        if (mSendingPointersToWallpaper) {
            if (skipped) {
                Log.i(TAG, "Sending skipped pointer to wallpaper!");
            }
            sendPointerToWallpaperLocked(relWin, pointer, eventTime);
            
        // If we are on top of the wallpaper, then the wallpaper also
        // gets to see this movement.
        } else if (srcWin != null
                && pointer.getAction() == MotionEvent.ACTION_DOWN
                && mWallpaperTarget == srcWin
                && srcWin.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) {
            sendPointerToWallpaperLocked(relWin, pointer, eventTime);
        }
    }
    
    public int addWindow(Session session, IWindow client,
            WindowManager.LayoutParams attrs, int viewVisibility,
            Rect outContentInsets) {
@@ -4918,10 +4944,7 @@ public class WindowManagerService extends IWindowManager.Stub
                Log.w(TAG, "No window to dispatch pointer action " + ev.getAction());
            }
            synchronized (mWindowMap) {
                if (mSendingPointersToWallpaper) {
                    Log.i(TAG, "Sending skipped pointer to wallpaper!");
                    sendPointerToWallpaperLocked(null, ev, ev.getEventTime());
                }
                dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), true);
            }
            if (qev != null) {
                mQueue.recycleEvent(qev);
@@ -4931,10 +4954,7 @@ public class WindowManagerService extends IWindowManager.Stub
        }
        if (targetObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
            synchronized (mWindowMap) {
                if (mSendingPointersToWallpaper) {
                    Log.i(TAG, "Sending skipped pointer to wallpaper!");
                    sendPointerToWallpaperLocked(null, ev, ev.getEventTime());
                }
                dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), true);
            }
            if (qev != null) {
                mQueue.recycleEvent(qev);
@@ -5059,9 +5079,7 @@ public class WindowManagerService extends IWindowManager.Stub
            if (!target.isVisibleLw()) {
                // During this motion dispatch, the target window has become
                // invisible.
                if (mSendingPointersToWallpaper) {
                    sendPointerToWallpaperLocked(null, ev, eventTime);
                }
                dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), false);
                if (qev != null) {
                    mQueue.recycleEvent(qev);
                }
@@ -5094,13 +5112,7 @@ public class WindowManagerService extends IWindowManager.Stub
                    }
                }

                // If we are on top of the wallpaper, then the wallpaper also
                // gets to see this movement.
                if ((mWallpaperTarget == target &&
                        target.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD)
                        || mSendingPointersToWallpaper) {
                    sendPointerToWallpaperLocked(null, ev, eventTime);
                }
                dispatchPointerElsewhereLocked(target, null, ev, ev.getEventTime(), false);

                final Rect frame = target.mFrame;
                ev.offsetLocation(-(float)frame.left, -(float)frame.top);
@@ -6031,11 +6043,7 @@ public class WindowManagerService extends IWindowManager.Stub

            if (res != null && returnWhat == RETURN_PENDING_POINTER) {
                synchronized (mWindowMap) {
                    if ((mWallpaperTarget == win &&
                            win.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD)
                            || mSendingPointersToWallpaper) {
                        sendPointerToWallpaperLocked(win, res, res.getEventTime());
                    }
                    dispatchPointerElsewhereLocked(win, win, res, res.getEventTime(), false);
                }
            }