Home | History | Annotate | Download | only in chromoting
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 package org.chromium.chromoting;
      6 
      7 import android.graphics.PointF;
      8 import android.os.SystemClock;
      9 import android.util.SparseArray;
     10 import android.view.InputDevice;
     11 import android.view.MotionEvent;
     12 
     13 /**
     14  * Utility for creating MotionEvents for multi-touch event simulation. The events are created by
     15  * calling MotionEvent.obtain(...) with suitable parameters.
     16  */
     17 public class TouchEventGenerator {
     18     /**
     19      * Stores the current set of held-down fingers. This is a sparse array since the indices of the
     20      * fingers are not contiguous. For example, if fingers 0, 1, 2 are pressed then finger 1 is
     21      * lifted, the device ids 0, 2 would correspond to the indexes 0, 1.
     22      */
     23     private SparseArray<PointF> mFingerPositions = new SparseArray<PointF>();
     24 
     25     /**
     26      * The event's DownTime. This is set to the system's current time when the first finger is
     27      * pressed.
     28      */
     29     private long mDownTime;
     30 
     31     /**
     32      * Creates a fake event with the given action and device id. The event is generated using the
     33      * information in |mFingerPositions|.
     34      *
     35      * @param actionMasked The (masked) action to generate, for example, ACTION_POINTER_DOWN.
     36      * @param id The device id of the event. There must already be an entry for |id| in
     37      *        |mFingerPositions|, so that |id| can be converted to an index and combined with
     38      *        |actionMasked| to set the new event's |action| property.
     39      */
     40     private MotionEvent obtainEvent(int actionMasked, int id) {
     41         int actionIndex = mFingerPositions.indexOfKey(id);
     42         assert actionIndex >= 0;
     43         int action = (actionIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT) | actionMasked;
     44 
     45         long eventTime = SystemClock.uptimeMillis();
     46         int size = mFingerPositions.size();
     47 
     48         // Generate the arrays of pointers and positions for the event.
     49         MotionEvent.PointerProperties[] pointers = new MotionEvent.PointerProperties[size];
     50         MotionEvent.PointerCoords[] positions = new MotionEvent.PointerCoords[size];
     51         for (int i = 0; i < size; i++) {
     52             int key = mFingerPositions.keyAt(i);
     53             PointF position = mFingerPositions.valueAt(i);
     54 
     55             pointers[i] = new MotionEvent.PointerProperties();
     56             pointers[i].id = key;
     57 
     58             positions[i] = new MotionEvent.PointerCoords();
     59             positions[i].x = position.x;
     60             positions[i].y = position.y;
     61         }
     62 
     63         return MotionEvent.obtain(mDownTime, eventTime, action, size, pointers, positions,
     64                 0, 0, 1, 1, id, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
     65     }
     66 
     67     /**
     68      * Obtains a finger-down event.
     69      *
     70      * @param id The device id of the new finger that is pressed. The caller must ensure this is
     71      *        a finger not currently held down.
     72      * @param x The x-coordinate of the new finger position.
     73      * @param y The y-coordinate of the new finger position.
     74      */
     75     public MotionEvent obtainDownEvent(int id, float x, float y) {
     76         assert mFingerPositions.get(id) == null;
     77         mFingerPositions.put(id, new PointF(x, y));
     78         int actionMasked;
     79         if (mFingerPositions.size() == 1) {
     80             mDownTime = SystemClock.uptimeMillis();
     81             actionMasked = MotionEvent.ACTION_DOWN;
     82         } else {
     83             actionMasked = MotionEvent.ACTION_POINTER_DOWN;
     84         }
     85         return obtainEvent(actionMasked, id);
     86     }
     87 
     88     /**
     89      * Obtains a finger-up event.
     90      *
     91      * @param id The device id of the finger to be lifted. The caller must ensure this is a
     92      *        finger previously held down.
     93      */
     94     public MotionEvent obtainUpEvent(int id) {
     95         assert mFingerPositions.get(id) != null;
     96 
     97         int actionMasked = mFingerPositions.size() == 1
     98                 ? MotionEvent.ACTION_UP : MotionEvent.ACTION_POINTER_UP;
     99 
    100         MotionEvent event = obtainEvent(actionMasked, id);
    101         mFingerPositions.remove(id);
    102         return event;
    103     }
    104 
    105     /**
    106      * Obtains a finger-move event. This moves a single finger, keeping any others in the same
    107      * same position.
    108      *
    109      * @param id The device id of the finger being moved. The caller must ensure this is a
    110      *        finger previously held down.
    111      * @param x The x-coordinate of the new finger position.
    112      * @param y The y-coordinate of the new finger position.
    113      */
    114     public MotionEvent obtainMoveEvent(int id, float x, float y) {
    115         PointF position = mFingerPositions.get(id);
    116         assert position != null;
    117         position.x = x;
    118         position.y = y;
    119         return obtainEvent(MotionEvent.ACTION_MOVE, id);
    120     }
    121 }
    122