android 模拟触摸

来源:互联网 发布:虚拟机安装mac卡 编辑:程序博客网 时间:2024/06/06 05:40
package android.car.server.input;import android.car.utils.InputIoctl;import android.car.utils.touch.TouchWriter;import android.os.SystemClock;import android.util.Log;import android.view.InputDevice;import android.view.MotionEvent;import android.view.MotionEvent.PointerCoords;import java.util.ArrayList;import static android.car.define.MainConfig.MCU_DEBUG;import static android.view.MotionEvent.ACTION_DOWN;import static android.view.MotionEvent.ACTION_MOVE;import static android.view.MotionEvent.ACTION_POINTER_DOWN;import static android.view.MotionEvent.ACTION_POINTER_INDEX_MASK;import static android.view.MotionEvent.ACTION_POINTER_INDEX_SHIFT;import static android.view.MotionEvent.ACTION_POINTER_UP;import static android.view.MotionEvent.ACTION_UP;public class MultiTouchInject {    static final String TAG = "Car-MultiTouchInject";    private static final int MAX_TOUCH_COUNT = 10;    public static boolean USE_TOUCH_WRITER = true;    private int[] mPointerIds = new int[MAX_TOUCH_COUNT];    private PointerCoords[] mPointerCoords = new PointerCoords[MAX_TOUCH_COUNT];    private long mMultiTouchDownTime = 0;    private TouchWriter mTouchWriter;    static class PointerState {        public boolean valid;        public int id;        public int action;        public int x;        public int y;        public void clear() {            valid = false;            id = -1;            action = -1;            x = 0;            y = 0;        }    }    private PointerState[] mPointers = new PointerState[MAX_TOUCH_COUNT];    private ArrayList<PointerState> mActivePointers = new ArrayList<>();    public MultiTouchInject() {        for (int i = 0; i < MAX_TOUCH_COUNT; ++i) {            mPointerCoords[i] = new PointerCoords();            mPointers[i] = new PointerState();        }        String touchInputDevice = InputIoctl.getTouchInputDevicePath();        Log.e(TAG, "touchInputDevice=" + touchInputDevice);        mTouchWriter = new TouchWriter(touchInputDevice);        mTouchWriter.init();    }    /**     * 生成多点action     */    private int makeMtAction(int id, int action) {        return ((id << ACTION_POINTER_INDEX_SHIFT) & ACTION_POINTER_INDEX_MASK) | action;    }    /**     * 通过id和action生成多点时用的action     *     * @param id     * @param action     * @return     */    private int getMultiAction(int id, int action) {        // 如果是第一个点或MOVE事件, 则直接使用        switch (action) {            case ACTION_MOVE:                return ACTION_MOVE;            case ACTION_DOWN:                return makeMtAction(id, ACTION_POINTER_DOWN);            case ACTION_UP:                return makeMtAction(id, ACTION_POINTER_UP);        }        return -1;    }    private void stateToCoords(PointerState ps, PointerCoords pc) {        pc.clear();        pc.x = ps.x;        pc.y = ps.y;        pc.pressure = 1.0f;        pc.size = 1.0f;    }    // 使用TouchWrite方式写入, 注意和另一个函数不能同时使用    public void addTouchEventRaw(int id, int action, int x, int y, int w, int h) {        if (id < 0 || id >= MAX_TOUCH_COUNT) {            return;        }        final PointerState ps = mPointers[id];        if (ps.id == id                && ACTION_MOVE == action                && ps.x == x                && ps.y == y) {            return;        }        ps.valid = true;        ps.id = id;        ps.action = action;        ps.x = x;        ps.y = y;        // 当第一次DOWN时, 产生DOWN事件        if (mActivePointers.size() == 0 && action == ACTION_DOWN) {            mTouchWriter.writeDown();        }        if (!mActivePointers.contains(ps)) {            mActivePointers.add(ps);        }        int count = mActivePointers.size();        // 产生点事件        for (PointerState i : mActivePointers) {            if (i.valid && i.id >= 0 && i.id < MAX_TOUCH_COUNT) {                mTouchWriter.writePoint(i.id, i.x, i.y, w, h);            }        }        mTouchWriter.writeSync();        if (count == 1 && action == ACTION_UP) {            // 只有一个点并且为UP时, 产生UP事件            mTouchWriter.writeUp();        }        if (action == ACTION_UP) {            ps.clear();            if (mActivePointers.contains(ps)) {                mActivePointers.remove(ps);            }        }    }    public MotionEvent addTouchEvent(int id, int action, int x, int y) {        if (id < 0 || id >= MAX_TOUCH_COUNT) {            return null;        }        long eventTime = SystemClock.uptimeMillis();        final PointerState ps = mPointers[id];//        if (ps.id == id//                && ps.action == action//                && action == ACTION_MOVE//                && ps.x == x//                && ps.y == y) {//            return null;//        }        ps.valid = true;        ps.id = id;        ps.action = action;        ps.x = x;        ps.y = y;        long downTime = eventTime;        // 当第一次DOWN时, 记录downTime        if (mActivePointers.size() == 0 && action == ACTION_DOWN) {            mMultiTouchDownTime = downTime;        } else {            downTime = mMultiTouchDownTime;        }        if (!mActivePointers.contains(ps)) {            mActivePointers.add(ps);        }        int count = mActivePointers.size();        // 对Action做处理        // 1.单点时, 使用ACTION_DOWN/UP        // 2.当多点时, 使用ACTION_POINTER_DOWN/UP        // 3.最后一个点UP时, 使用ACTION_UP        int mtAction = getMultiAction(id, action);        int finalAction = mtAction;        // 只有一个点并且id为0时, 使用原本的action        if (count == 1 && id == 0) {            finalAction = action;        } else if (count == 1 && action == ACTION_UP) { // 只有一个点并且为UP时, 使用ACTION_UP            finalAction = action;        }        if (MCU_DEBUG)            Log.e(TAG, "addTouchEvent count=" + count                    + ", id=" + id                    + ", x=" + x                    + "  y=" + y                    + ", action=" + MotionEvent.actionToString(action)                    + ", mtAction=" + MotionEvent.actionToString(mtAction)                    + ", finalAction=" + MotionEvent.actionToString(finalAction));        MotionEvent event = null;        if (USE_TOUCH_WRITER) {        } else {            event = createMotionEvent(finalAction, downTime, eventTime);        }        // 当为UP事件时, 将此点清空        if (action == ACTION_UP) {            ps.clear();            if (mActivePointers.contains(ps)) {                mActivePointers.remove(ps);            }        }        return event;    }    private MotionEvent createMotionEvent(int action, long downTime, long eventTime) {        int pointerCount = mActivePointers.size();        // 找出有效的并配置 count, ids, coords        for (int i = 0; i < pointerCount; ++i) {            PointerState ps = mActivePointers.get(i);            if (ps.valid && ps.id >= 0 && ps.id < MAX_TOUCH_COUNT) {                mPointerIds[i] = ps.id;                stateToCoords(ps, mPointerCoords[i]);            }        }        MotionEvent event = MotionEvent.obtain(downTime,                eventTime,                action,                pointerCount,                mPointerIds,                mPointerCoords,                1 /*metaState*/,                1.0f /* xPrecision*/,                1.0f /*yPrecision*/,                0 /*deviceId*/,                0 /*edgeFlags*/,                InputDevice.SOURCE_TOUCHSCREEN /*source*/,                0 /*flags*/);        return event;    }}


package android.car.utils.touch;import android.car.utils.InputIoctl;import android.car.utils.IoctlHelper;import android.os.SystemClock;import android.os.SystemProperties;import android.text.TextUtils;import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStream;import java.nio.ByteOrder;import libcore.io.Memory;import static android.car.utils.InputIoctl.ABS_MT_POSITION_X;import static android.car.utils.InputIoctl.ABS_MT_POSITION_Y;import static android.car.utils.InputIoctl.ABS_MT_TOUCH_MAJOR;import static android.car.utils.InputIoctl.ABS_MT_TRACKING_ID;import static android.car.utils.InputIoctl.ABS_MT_WIDTH_MAJOR;import static android.car.utils.InputIoctl.BTN_TOUCH;import static android.car.utils.InputIoctl.EV_ABS;import static android.car.utils.InputIoctl.EV_KEY;import static android.car.utils.InputIoctl.EV_SYN;import static android.car.utils.InputIoctl.SYN_MT_REPORT;import static android.car.utils.InputIoctl.SYN_REPORT;import static android.car.utils.InputIoctl.TOUCH_DOWN;import static android.car.utils.InputIoctl.TOUCH_UP;/** * 读取输入设备原生数据 */public class TouchWriter {    private static final String TAG = "Car-TouchInputReader";    private static final boolean DEBUG_RAWDATA = false;    static final boolean IS_64BIT = !TextUtils.isEmpty(SystemProperties.get("ro.product.cpu.abilist64", null));    /* 数据大小定义 */    static final int RAW_EVENT_TIME_SIZE = IS_64BIT ? 16 : 8;    static final int RAW_EVENT_SIZE = RAW_EVENT_TIME_SIZE + 8;    private String mDevicePath;    private int mAbsXMax;    private int mAbsYMax;    private OutputStream mDeviceStream;    public TouchWriter(String devicePath) {        mDevicePath = devicePath;    }    public void init() {        int fd = -1;        try {            fd = IoctlHelper.open(mDevicePath, IoctlHelper.O_RDWR);            InputIoctl.AbsInfo xInfo = InputIoctl.getAbsInfo(fd, ABS_MT_POSITION_X);            if (xInfo != null) {                mAbsXMax = xInfo.max;            }            InputIoctl.AbsInfo yInfo = InputIoctl.getAbsInfo(fd, ABS_MT_POSITION_Y);            if (yInfo != null) {                mAbsYMax = yInfo.max;            }            mDeviceStream = new FileOutputStream(mDevicePath);        } catch (Exception e) {            e.printStackTrace();        } finally {            IoctlHelper.close(fd);        }    }    public int getAbsXMax() {        return mAbsXMax;    }    public int getAbsYMax() {        return mAbsYMax;    }    // 原生数据格式    // struct input_event {    // struct timeval time;    // __u16 type;    // __u16 code;    // __s32 value;    // };    // struct timeval {    // long tv_sec; /* seconds */    // long tv_usec; /* and microseconds */    // };    private final byte[] mWriteBuff = new byte[RAW_EVENT_SIZE];    private void writeEvent(long sec, long microSec, int type, int code, int value) {        // TODO 对64位系统做处理        synchronized (mWriteBuff) {            int pos = 0;            Memory.pokeLong(mWriteBuff, pos, sec, ByteOrder.nativeOrder());            pos += 4;            Memory.pokeLong(mWriteBuff, pos, microSec, ByteOrder.nativeOrder());            pos += 4;            Memory.pokeShort(mWriteBuff, pos, (short) type, ByteOrder.nativeOrder());            pos += 2;            Memory.pokeShort(mWriteBuff, pos, (short) code, ByteOrder.nativeOrder());            pos += 2;            Memory.pokeShort(mWriteBuff, pos, (short) value, ByteOrder.nativeOrder());            if (mDeviceStream != null) {                try {                    mDeviceStream.write(mWriteBuff);                } catch (Exception e) {                    e.printStackTrace();                }            }        }    }    private void writeEvent(int type, int code, int value) {        long time = SystemClock.currentTimeMicro();        long sec = time / 1000;        long microSec = time % 1000 * 1000;        writeEvent(sec, microSec, type, code, value);    }    public void writeSync() {        writeEvent(EV_SYN, SYN_REPORT, 0);    }    // 按下事件, 只有第一个点的按下需要发送这个    // EV_KEY       BTN_TOUCH            DOWN    // 触摸点    public void writeDown() {        writeEvent(EV_KEY, BTN_TOUCH, TOUCH_DOWN);    }    public void writePoint(int id, int x, int y, int w, int h) {        int absX = x * mAbsXMax / w;        int absY = y * mAbsYMax / h;        writeEvent(EV_ABS, ABS_MT_POSITION_X, absX);        writeEvent(EV_ABS, ABS_MT_POSITION_Y, absY);        writeEvent(EV_ABS, ABS_MT_TOUCH_MAJOR, 0x20);        writeEvent(EV_ABS, ABS_MT_WIDTH_MAJOR, 0x20);        writeEvent(EV_ABS, ABS_MT_TRACKING_ID, id);        writeEvent(EV_SYN, SYN_MT_REPORT, 0);    }    // 抬起事件, 只有最后一个抬起的点需要发送这个    // EV_KEY       BTN_TOUCH            UP    public void writeUp() {        writeEvent(EV_KEY, BTN_TOUCH, TOUCH_UP);        writeSync();    }}


    MultiTouchInject mMultiTouchInject = new MultiTouchInject();    /**     * 模拟触摸事件     * @param id 触摸点的id(0为第一个)     * @param type 事件类型     * @param x x坐标     * @param y y坐标     */    private void onMultiTouchEvent(int id, int type, int x, int y) {        if (USE_TOUCH_WRITER) {            //更底层的模拟触摸            mMultiTouchInject.addTouchEventRaw(id, type, x, y, SystemHelper.getScreenWidth(), SystemHelper.getScreenHeight());        } else {            //调用系统接口            MotionEvent event = mMultiTouchInject.addTouchEvent(id, type, x, y);            if (event != null) {                doInjectInputEventAsync(event);            }        }    }

代码如上。


参考资料:http://blog.csdn.net/liyi_cs_dn/article/details/7347832