alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Android example source code file (KeyInputQueue.java)

This example Android source code file (KeyInputQueue.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Android by Example" TM.

Java - Android tags/keywords

android, bad_touch_hack, debug_pointers, debug_virtual_keys, hapticfeedbackcallback, inputdevice, io, keyevent, keyinputqueue, measure_latency, os, queuedevent, rawinputevent, sparsearray, string, ui, util, view, virtualkey, x

The KeyInputQueue.java Android example source code

/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server;

import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Environment;
import android.os.LatencyTimer;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
import android.view.Display;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.RawInputEvent;
import android.view.Surface;
import android.view.WindowManagerPolicy;

import com.android.internal.util.XmlUtils;

import org.xmlpull.v1.XmlPullParser;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;

public abstract class KeyInputQueue {
    static final String TAG = "KeyInputQueue";

    static final boolean DEBUG = false;
    static final boolean DEBUG_VIRTUAL_KEYS = false;
    static final boolean DEBUG_POINTERS = false;
    
    /**
     * Turn on some hacks we have to improve the touch interaction with a
     * certain device whose screen currently is not all that good.
     */
    static boolean BAD_TOUCH_HACK = false;
    
    /**
     * Turn on some hacks to improve touch interaction with another device
     * where touch coordinate data can get corrupted.
     */
    static boolean JUMPY_TOUCH_HACK = false;
    
    private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";

    final SparseArray<InputDevice> mDevices = new SparseArray();
    final SparseArray<InputDevice> mIgnoredDevices = new SparseArray();
    final ArrayList<VirtualKey> mVirtualKeys = new ArrayList();
    final HapticFeedbackCallback mHapticFeedbackCallback;
    
    int mGlobalMetaState = 0;
    boolean mHaveGlobalMetaState = false;
    
    final QueuedEvent mFirst;
    final QueuedEvent mLast;
    QueuedEvent mCache;
    int mCacheCount;

    Display mDisplay = null;
    int mDisplayWidth;
    int mDisplayHeight;
    
    int mOrientation = Surface.ROTATION_0;
    int[] mKeyRotationMap = null;
    
    VirtualKey mPressedVirtualKey = null;
    
    PowerManager.WakeLock mWakeLock;

    static final int[] KEY_90_MAP = new int[] {
        KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
        KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_UP,
        KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT,
        KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_DOWN,
    };
    
    static final int[] KEY_180_MAP = new int[] {
        KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_UP,
        KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT,
        KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
        KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
    };
    
    static final int[] KEY_270_MAP = new int[] {
        KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
        KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_UP,
        KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
        KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_DOWN,
    };
    
    public static final int FILTER_REMOVE = 0;
    public static final int FILTER_KEEP = 1;
    public static final int FILTER_ABORT = -1;

    private static final boolean MEASURE_LATENCY = false;
    private LatencyTimer lt;

    public interface FilterCallback {
        int filterEvent(QueuedEvent ev);
    }
    
    public interface HapticFeedbackCallback {
        void virtualKeyFeedback(KeyEvent event);
    }
    
    static class QueuedEvent {
        InputDevice inputDevice;
        long whenNano;
        int flags; // From the raw event
        int classType; // One of the class constants in InputEvent
        Object event;
        boolean inQueue;

        void copyFrom(QueuedEvent that) {
            this.inputDevice = that.inputDevice;
            this.whenNano = that.whenNano;
            this.flags = that.flags;
            this.classType = that.classType;
            this.event = that.event;
        }

        @Override
        public String toString() {
            return "QueuedEvent{"
                + Integer.toHexString(System.identityHashCode(this))
                + " " + event + "}";
        }
        
        // not copied
        QueuedEvent prev;
        QueuedEvent next;
    }

    /**
     * A key that exists as a part of the touch-screen, outside of the normal
     * display area of the screen.
     */
    static class VirtualKey {
        int scancode;
        int centerx;
        int centery;
        int width;
        int height;
        
        int hitLeft;
        int hitTop;
        int hitRight;
        int hitBottom;
        
        InputDevice lastDevice;
        int lastKeycode;
        
        boolean checkHit(int x, int y) {
            return (x >= hitLeft && x <= hitRight
                    && y >= hitTop && y <= hitBottom);
        }
        
        void computeHitRect(InputDevice dev, int dw, int dh) {
            if (dev == lastDevice) {
                return;
            }
            
            if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "computeHitRect for " + scancode
                    + ": dev=" + dev + " absX=" + dev.absX + " absY=" + dev.absY);
            
            lastDevice = dev;
            
            int minx = dev.absX.minValue;
            int maxx = dev.absX.maxValue;
            
            int halfw = width/2;
            int left = centerx - halfw;
            int right = centerx + halfw;
            hitLeft = minx + ((left*maxx-minx)/dw);
            hitRight = minx + ((right*maxx-minx)/dw);
            
            int miny = dev.absY.minValue;
            int maxy = dev.absY.maxValue;
            
            int halfh = height/2;
            int top = centery - halfh;
            int bottom = centery + halfh;
            hitTop = miny + ((top*maxy-miny)/dh);
            hitBottom = miny + ((bottom*maxy-miny)/dh);
        }
    }

    private void readVirtualKeys(String deviceName) {
        try {
            FileInputStream fis = new FileInputStream(
                    "/sys/board_properties/virtualkeys." + deviceName);
            InputStreamReader isr = new InputStreamReader(fis);
            BufferedReader br = new BufferedReader(isr, 2048);
            String str = br.readLine();
            if (str != null) {
                String[] it = str.split(":");
                if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "***** VIRTUAL KEYS: " + it);
                final int N = it.length-6;
                for (int i=0; i<=N; i+=6) {
                    if (!"0x01".equals(it[i])) {
                        Slog.w(TAG, "Unknown virtual key type at elem #" + i
                                + ": " + it[i]);
                        continue;
                    }
                    try {
                        VirtualKey sb = new VirtualKey();
                        sb.scancode = Integer.parseInt(it[i+1]);
                        sb.centerx = Integer.parseInt(it[i+2]);
                        sb.centery = Integer.parseInt(it[i+3]);
                        sb.width = Integer.parseInt(it[i+4]);
                        sb.height = Integer.parseInt(it[i+5]);
                        if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Virtual key "
                                + sb.scancode + ": center=" + sb.centerx + ","
                                + sb.centery + " size=" + sb.width + "x"
                                + sb.height);
                        mVirtualKeys.add(sb);
                    } catch (NumberFormatException e) {
                        Slog.w(TAG, "Bad number at region " + i + " in: "
                                + str, e);
                    }
                }
            }
            br.close();
        } catch (FileNotFoundException e) {
            Slog.i(TAG, "No virtual keys found");
        } catch (IOException e) {
            Slog.w(TAG, "Error reading virtual keys", e);
        }
    }

    private void readExcludedDevices() {
        // Read partner-provided list of excluded input devices
        XmlPullParser parser = null;
        // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
        File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
        FileReader confreader = null;
        try {
            confreader = new FileReader(confFile);
            parser = Xml.newPullParser();
            parser.setInput(confreader);
            XmlUtils.beginDocument(parser, "devices");

            while (true) {
                XmlUtils.nextElement(parser);
                if (!"device".equals(parser.getName())) {
                    break;
                }
                String name = parser.getAttributeValue(null, "name");
                if (name != null) {
                    if (DEBUG) Slog.v(TAG, "addExcludedDevice " + name);
                    addExcludedDevice(name);
                }
            }
        } catch (FileNotFoundException e) {
            // It's ok if the file does not exist.
        } catch (Exception e) {
            Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
        } finally {
            try { if (confreader != null) confreader.close(); } catch (IOException e) { }
        }
    }

    KeyInputQueue(Context context, HapticFeedbackCallback  hapticFeedbackCallback) {
        if (MEASURE_LATENCY) {
            lt = new LatencyTimer(100, 1000);
        }

        Resources r = context.getResources();
        BAD_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterTouchEvents);
        
        JUMPY_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterJumpyTouchEvents);
        
        mHapticFeedbackCallback = hapticFeedbackCallback;
        
        readExcludedDevices();
        
        PowerManager pm = (PowerManager)context.getSystemService(
                                                        Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                                                        "KeyInputQueue");
        mWakeLock.setReferenceCounted(false);

        mFirst = new QueuedEvent();
        mLast = new QueuedEvent();
        mFirst.next = mLast;
        mLast.prev = mFirst;
    }

    void start() {
        mThread.start();
    }

    public void setDisplay(Display display) {
        mDisplay = display;
        
        // We assume at this point that the display dimensions reflect the
        // natural, unrotated display.  We will perform hit tests for soft
        // buttons based on that display.
        mDisplayWidth = display.getWidth();
        mDisplayHeight = display.getHeight();
    }
    
    public void getInputConfiguration(Configuration config) {
        synchronized (mFirst) {
            config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
            config.keyboard = Configuration.KEYBOARD_NOKEYS;
            config.navigation = Configuration.NAVIGATION_NONAV;
            
            final int N = mDevices.size();
            for (int i=0; i<N; i++) {
                InputDevice d = mDevices.valueAt(i);
                if (d != null) {
                    if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
                        config.touchscreen
                                = Configuration.TOUCHSCREEN_FINGER;
                        //Slog.i("foo", "***** HAVE TOUCHSCREEN!");
                    }
                    if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) {
                        config.keyboard
                                = Configuration.KEYBOARD_QWERTY;
                        //Slog.i("foo", "***** HAVE QWERTY!");
                    }
                    if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
                        config.navigation
                                = Configuration.NAVIGATION_TRACKBALL;
                        //Slog.i("foo", "***** HAVE TRACKBALL!");
                    } else if ((d.classes&RawInputEvent.CLASS_DPAD) != 0) {
                        config.navigation
                                = Configuration.NAVIGATION_DPAD;
                        //Slog.i("foo", "***** HAVE DPAD!");
                    }
                }
            }
        }
    }
    
    public int getScancodeState(int code) {
        synchronized (mFirst) {
            VirtualKey vk = mPressedVirtualKey;
            if (vk != null) {
                if (vk.scancode == code) {
                    return 2;
                }
            }
            return nativeGetScancodeState(code);
        }
    }
    
    public int getScancodeState(int deviceId, int code) {
        synchronized (mFirst) {
            VirtualKey vk = mPressedVirtualKey;
            if (vk != null) {
                if (vk.scancode == code) {
                    return 2;
                }
            }
            return nativeGetScancodeState(deviceId, code);
        }
    }
    
    public int getTrackballScancodeState(int code) {
        synchronized (mFirst) {
            final int N = mDevices.size();
            for (int i=0; i<N; i++) {
                InputDevice dev = mDevices.valueAt(i);
                if ((dev.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
                    int res = nativeGetScancodeState(dev.id, code);
                    if (res > 0) {
                        return res;
                    }
                }
            }
        }
        
        return 0;
    }
    
    public int getDPadScancodeState(int code) {
        synchronized (mFirst) {
            final int N = mDevices.size();
            for (int i=0; i<N; i++) {
                InputDevice dev = mDevices.valueAt(i);
                if ((dev.classes&RawInputEvent.CLASS_DPAD) != 0) {
                    int res = nativeGetScancodeState(dev.id, code);
                    if (res > 0) {
                        return res;
                    }
                }
            }
        }
        
        return 0;
    }
    
    public int getKeycodeState(int code) {
        synchronized (mFirst) {
            VirtualKey vk = mPressedVirtualKey;
            if (vk != null) {
                if (vk.lastKeycode == code) {
                    return 2;
                }
            }
            return nativeGetKeycodeState(code);
        }
    }
    
    public int getKeycodeState(int deviceId, int code) {
        synchronized (mFirst) {
            VirtualKey vk = mPressedVirtualKey;
            if (vk != null) {
                if (vk.lastKeycode == code) {
                    return 2;
                }
            }
            return nativeGetKeycodeState(deviceId, code);
        }
    }
    
    public int getTrackballKeycodeState(int code) {
        synchronized (mFirst) {
            final int N = mDevices.size();
            for (int i=0; i<N; i++) {
                InputDevice dev = mDevices.valueAt(i);
                if ((dev.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
                    int res = nativeGetKeycodeState(dev.id, code);
                    if (res > 0) {
                        return res;
                    }
                }
            }
        }
        
        return 0;
    }
    
    public int getDPadKeycodeState(int code) {
        synchronized (mFirst) {
            final int N = mDevices.size();
            for (int i=0; i<N; i++) {
                InputDevice dev = mDevices.valueAt(i);
                if ((dev.classes&RawInputEvent.CLASS_DPAD) != 0) {
                    int res = nativeGetKeycodeState(dev.id, code);
                    if (res > 0) {
                        return res;
                    }
                }
            }
        }
        
        return 0;
    }
    
    public static native String getDeviceName(int deviceId);
    public static native int getDeviceClasses(int deviceId);
    public static native void addExcludedDevice(String deviceName);
    public static native boolean getAbsoluteInfo(int deviceId, int axis,
            InputDevice.AbsoluteInfo outInfo);
    public static native int getSwitchState(int sw);
    public static native int getSwitchState(int deviceId, int sw);
    public static native int nativeGetScancodeState(int code);
    public static native int nativeGetScancodeState(int deviceId, int code);
    public static native int nativeGetKeycodeState(int code);
    public static native int nativeGetKeycodeState(int deviceId, int code);
    public static native int scancodeToKeycode(int deviceId, int scancode);
    public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);
    
    public static KeyEvent newKeyEvent(InputDevice device, long downTime,
            long eventTime, boolean down, int keycode, int repeatCount,
            int scancode, int flags) {
        return new KeyEvent(
                downTime, eventTime,
                down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
                keycode, repeatCount,
                device != null ? device.mMetaKeysState : 0,
                device != null ? device.id : -1, scancode,
                flags | KeyEvent.FLAG_FROM_SYSTEM);
    }
    
    Thread mThread = new Thread("InputDeviceReader") {
        public void run() {
            if (DEBUG) Slog.v(TAG, "InputDeviceReader.run()");
            android.os.Process.setThreadPriority(
                    android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
            
            RawInputEvent ev = new RawInputEvent();
            while (true) {
                try {
                    InputDevice di;

                    // block, doesn't release the monitor
                    readEvent(ev);

                    boolean send = false;
                    boolean configChanged = false;
                    
                    if (false) {
                        Slog.i(TAG, "Input event: dev=0x"
                                + Integer.toHexString(ev.deviceId)
                                + " type=0x" + Integer.toHexString(ev.type)
                                + " scancode=" + ev.scancode
                                + " keycode=" + ev.keycode
                                + " value=" + ev.value);
                    }
                    
                    if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
                        synchronized (mFirst) {
                            di = newInputDevice(ev.deviceId);
                            if (di.classes != 0) {
                                // If this device is some kind of input class,
                                // we care about it.
                                mDevices.put(ev.deviceId, di);
                                if ((di.classes & RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
                                    readVirtualKeys(di.name);
                                }
                                // The configuration may have changed because
                                // of this device.
                                configChanged = true;
                            } else {
                                // We won't do anything with this device.
                                mIgnoredDevices.put(ev.deviceId, di);
                                Slog.i(TAG, "Ignoring non-input device: id=0x"
                                        + Integer.toHexString(di.id)
                                        + ", name=" + di.name);
                            }
                        }
                    } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
                        synchronized (mFirst) {
                            if (false) {
                                Slog.i(TAG, "Device removed: id=0x"
                                        + Integer.toHexString(ev.deviceId));
                            }
                            di = mDevices.get(ev.deviceId);
                            if (di != null) {
                                mDevices.delete(ev.deviceId);
                                // The configuration may have changed because
                                // of this device.
                                configChanged = true;
                            } else if ((di=mIgnoredDevices.get(ev.deviceId)) != null) {
                                mIgnoredDevices.remove(ev.deviceId);
                            } else {
                                Slog.w(TAG, "Removing bad device id: "
                                        + Integer.toHexString(ev.deviceId));
                                continue;
                            }
                        }
                    } else {
                        di = getInputDevice(ev.deviceId);
                        if (di == null) {
                            // This may be some junk from an ignored device.
                            continue;
                        }
                        
                        // first crack at it
                        send = preprocessEvent(di, ev);

                        if (ev.type == RawInputEvent.EV_KEY) {
                            di.mMetaKeysState = makeMetaState(ev.keycode,
                                    ev.value != 0, di.mMetaKeysState);
                            mHaveGlobalMetaState = false;
                        }
                    }

                    if (configChanged) {
                        synchronized (mFirst) {
                            addLocked(di, System.nanoTime(), 0,
                                    RawInputEvent.CLASS_CONFIGURATION_CHANGED,
                                    null);
                        }
                    }
                    
                    if (!send) {
                        continue;
                    }
                    
                    synchronized (mFirst) {
                        // NOTE: The event timebase absolutely must be the same
                        // timebase as SystemClock.uptimeMillis().
                        //curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
                        final long curTime = SystemClock.uptimeMillis();
                        final long curTimeNano = System.nanoTime();
                        //Slog.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
                        
                        final int classes = di.classes;
                        final int type = ev.type;
                        final int scancode = ev.scancode;
                        send = false;
                        
                        // Is it a key event?
                        if (type == RawInputEvent.EV_KEY &&
                                (classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
                                (scancode < RawInputEvent.BTN_FIRST ||
                                        scancode > RawInputEvent.BTN_LAST)) {
                            boolean down;
                            if (ev.value != 0) {
                                down = true;
                                di.mKeyDownTime = curTime;
                            } else {
                                down = false;
                            }
                            int keycode = rotateKeyCodeLocked(ev.keycode);
                            addLocked(di, curTimeNano, ev.flags,
                                    RawInputEvent.CLASS_KEYBOARD,
                                    newKeyEvent(di, di.mKeyDownTime, curTime, down,
                                            keycode, 0, scancode,
                                            ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
                                             ? KeyEvent.FLAG_WOKE_HERE : 0));
                            
                        } else if (ev.type == RawInputEvent.EV_KEY) {
                            // Single touch protocol: touch going down or up.
                            if (ev.scancode == RawInputEvent.BTN_TOUCH &&
                                    (classes&(RawInputEvent.CLASS_TOUCHSCREEN
                                            |RawInputEvent.CLASS_TOUCHSCREEN_MT))
                                            == RawInputEvent.CLASS_TOUCHSCREEN) {
                                di.mAbs.changed = true;
                                di.mAbs.mDown[0] = ev.value != 0;
                            
                            // Trackball (mouse) protocol: press down or up.
                            } else if (ev.scancode == RawInputEvent.BTN_MOUSE &&
                                    (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
                                di.mRel.changed = true;
                                di.mRel.mNextNumPointers = ev.value != 0 ? 1 : 0;
                                send = true;
                            }
    
                        // Process position events from multitouch protocol.
                        } else if (ev.type == RawInputEvent.EV_ABS &&
                                (classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
                            if (ev.scancode == RawInputEvent.ABS_MT_TOUCH_MAJOR) {
                                di.mAbs.changed = true;
                                di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
                                        + MotionEvent.SAMPLE_PRESSURE] = ev.value;
                            } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_X) {
                                di.mAbs.changed = true;
                                di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
                                    + MotionEvent.SAMPLE_X] = ev.value;
                                if (DEBUG_POINTERS) Slog.v(TAG, "MT @"
                                        + di.mAbs.mAddingPointerOffset
                                        + " X:" + ev.value);
                            } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_Y) {
                                di.mAbs.changed = true;
                                di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
                                    + MotionEvent.SAMPLE_Y] = ev.value;
                                if (DEBUG_POINTERS) Slog.v(TAG, "MT @"
                                        + di.mAbs.mAddingPointerOffset
                                        + " Y:" + ev.value);
                            } else if (ev.scancode == RawInputEvent.ABS_MT_WIDTH_MAJOR) {
                                di.mAbs.changed = true;
                                di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
                                    + MotionEvent.SAMPLE_SIZE] = ev.value;
                            }
                        
                        // Process position events from single touch protocol.
                        } else if (ev.type == RawInputEvent.EV_ABS &&
                                (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
                            if (ev.scancode == RawInputEvent.ABS_X) {
                                di.mAbs.changed = true;
                                di.curTouchVals[MotionEvent.SAMPLE_X] = ev.value;
                            } else if (ev.scancode == RawInputEvent.ABS_Y) {
                                di.mAbs.changed = true;
                                di.curTouchVals[MotionEvent.SAMPLE_Y] = ev.value;
                            } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
                                di.mAbs.changed = true;
                                di.curTouchVals[MotionEvent.SAMPLE_PRESSURE] = ev.value;
                                di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
                                                 + MotionEvent.SAMPLE_PRESSURE] = ev.value;
                            } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
                                di.mAbs.changed = true;
                                di.curTouchVals[MotionEvent.SAMPLE_SIZE] = ev.value;
                                di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
                                                 + MotionEvent.SAMPLE_SIZE] = ev.value;
                            }
    
                        // Process movement events from trackball (mouse) protocol.
                        } else if (ev.type == RawInputEvent.EV_REL &&
                                (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
                            // Add this relative movement into our totals.
                            if (ev.scancode == RawInputEvent.REL_X) {
                                di.mRel.changed = true;
                                di.mRel.mNextData[MotionEvent.SAMPLE_X] += ev.value;
                            } else if (ev.scancode == RawInputEvent.REL_Y) {
                                di.mRel.changed = true;
                                di.mRel.mNextData[MotionEvent.SAMPLE_Y] += ev.value;
                            }
                        }
                        
                        // Handle multitouch protocol sync: tells us that the
                        // driver has returned all data for -one- of the pointers
                        // that is currently down.
                        if (ev.type == RawInputEvent.EV_SYN
                                && ev.scancode == RawInputEvent.SYN_MT_REPORT
                                && di.mAbs != null) {
                            di.mAbs.changed = true;
                            if (di.mAbs.mNextData[MotionEvent.SAMPLE_PRESSURE] > 0) {
                                // If the value is <= 0, the pointer is not
                                // down, so keep it in the count.
                                
                                if (di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
                                                      + MotionEvent.SAMPLE_PRESSURE] != 0) {
                                    final int num = di.mAbs.mNextNumPointers+1;
                                    di.mAbs.mNextNumPointers = num;
                                    if (DEBUG_POINTERS) Slog.v(TAG,
                                            "MT_REPORT: now have " + num + " pointers");
                                    final int newOffset = (num <= InputDevice.MAX_POINTERS)
                                            ? (num * MotionEvent.NUM_SAMPLE_DATA)
                                            : (InputDevice.MAX_POINTERS *
                                                    MotionEvent.NUM_SAMPLE_DATA);
                                    di.mAbs.mAddingPointerOffset = newOffset;
                                    di.mAbs.mNextData[newOffset
                                            + MotionEvent.SAMPLE_PRESSURE] = 0;
                                } else {
                                    if (DEBUG_POINTERS) Slog.v(TAG, "MT_REPORT: no pointer");
                                }
                            }
                        
                        // Handle general event sync: all data for the current
                        // event update has been delivered.
                        } else if (send || (ev.type == RawInputEvent.EV_SYN
                                && ev.scancode == RawInputEvent.SYN_REPORT)) {
                            if (mDisplay != null) {
                                if (!mHaveGlobalMetaState) {
                                    computeGlobalMetaStateLocked();
                                }
                                
                                MotionEvent me;
                                
                                InputDevice.MotionState ms = di.mAbs;
                                if (ms.changed) {
                                    ms.everChanged = true;
                                    ms.changed = false;
                                    
                                    if ((classes&(RawInputEvent.CLASS_TOUCHSCREEN
                                            |RawInputEvent.CLASS_TOUCHSCREEN_MT))
                                            == RawInputEvent.CLASS_TOUCHSCREEN) {
                                        ms.mNextNumPointers = 0;
                                        if (ms.mDown[0]) {
                                            System.arraycopy(di.curTouchVals, 0,
                                                    ms.mNextData, 0,
                                                    MotionEvent.NUM_SAMPLE_DATA);
                                            ms.mNextNumPointers++;
                                        }
                                    }
                                    
                                    if (BAD_TOUCH_HACK) {
                                        ms.dropBadPoint(di);
                                    }
                                    if (JUMPY_TOUCH_HACK) {
                                        ms.dropJumpyPoint(di);
                                    }
                                    
                                    boolean doMotion = !monitorVirtualKey(di,
                                            ev, curTime, curTimeNano);
                                    
                                    if (doMotion && ms.mNextNumPointers > 0
                                            && (ms.mLastNumPointers == 0
                                                    || ms.mSkipLastPointers)) {
                                        doMotion = !generateVirtualKeyDown(di,
                                                ev, curTime, curTimeNano);
                                    }
                                    
                                    if (doMotion) {
                                        // XXX Need to be able to generate
                                        // multiple events here, for example
                                        // if two fingers change up/down state
                                        // at the same time.
                                        do {
                                            me = ms.generateAbsMotion(di, curTime,
                                                    curTimeNano, mDisplay,
                                                    mOrientation, mGlobalMetaState);
                                            if (DEBUG_POINTERS) Slog.v(TAG, "Absolute: x="
                                                    + di.mAbs.mNextData[MotionEvent.SAMPLE_X]
                                                    + " y="
                                                    + di.mAbs.mNextData[MotionEvent.SAMPLE_Y]
                                                    + " ev=" + me);
                                            if (me != null) {
                                                if (WindowManagerPolicy.WATCH_POINTER) {
                                                    Slog.i(TAG, "Enqueueing: " + me);
                                                }
                                                addLocked(di, curTimeNano, ev.flags,
                                                        RawInputEvent.CLASS_TOUCHSCREEN, me);
                                            }
                                        } while (ms.hasMore());
                                    } else {
                                        // We are consuming movement in the
                                        // virtual key area...  but still
                                        // propagate this to the previous
                                        // data for comparisons.
                                        int num = ms.mNextNumPointers;
                                        if (num > InputDevice.MAX_POINTERS) {
                                            num = InputDevice.MAX_POINTERS;
                                        }
                                        System.arraycopy(ms.mNextData, 0,
                                                ms.mLastData, 0,
                                                num * MotionEvent.NUM_SAMPLE_DATA);
                                        ms.mLastNumPointers = num;
                                        ms.mSkipLastPointers = true;
                                    }
                                    
                                    ms.finish();
                                }
                                
                                ms = di.mRel;
                                if (ms.changed) {
                                    ms.everChanged = true;
                                    ms.changed = false;
                                    
                                    me = ms.generateRelMotion(di, curTime,
                                            curTimeNano,
                                            mOrientation, mGlobalMetaState);
                                    if (false) Slog.v(TAG, "Relative: x="
                                            + di.mRel.mNextData[MotionEvent.SAMPLE_X]
                                            + " y="
                                            + di.mRel.mNextData[MotionEvent.SAMPLE_Y]
                                            + " ev=" + me);
                                    if (me != null) {
                                        addLocked(di, curTimeNano, ev.flags,
                                                RawInputEvent.CLASS_TRACKBALL, me);
                                    }
                                }
                            }
                        }
                    }
                
                } catch (RuntimeException exc) {
                    Slog.e(TAG, "InputReaderThread uncaught exception", exc);
                }
            }
        }
    };

    private boolean isInsideDisplay(InputDevice dev) {
        final InputDevice.AbsoluteInfo absx = dev.absX;
        final InputDevice.AbsoluteInfo absy = dev.absY;
        final InputDevice.MotionState absm = dev.mAbs;
        if (absx == null || absy == null || absm == null) {
            return true;
        }
        
        if (absm.mNextData[MotionEvent.SAMPLE_X] >= absx.minValue
                && absm.mNextData[MotionEvent.SAMPLE_X] <= absx.maxValue
                && absm.mNextData[MotionEvent.SAMPLE_Y] >= absy.minValue
                && absm.mNextData[MotionEvent.SAMPLE_Y] <= absy.maxValue) {
            if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Input ("
                    + absm.mNextData[MotionEvent.SAMPLE_X]
                    + "," + absm.mNextData[MotionEvent.SAMPLE_Y]
                    + ") inside of display");
            return true;
        }
        
        return false;
    }
    
    private VirtualKey findVirtualKey(InputDevice dev) {
        final int N = mVirtualKeys.size();
        if (N <= 0) {
            return null;
        }
        
        final InputDevice.MotionState absm = dev.mAbs;
        for (int i=0; i<N; i++) {
            VirtualKey sb = mVirtualKeys.get(i);
            sb.computeHitRect(dev, mDisplayWidth, mDisplayHeight);
            if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Hit test ("
                    + absm.mNextData[MotionEvent.SAMPLE_X] + ","
                    + absm.mNextData[MotionEvent.SAMPLE_Y] + ") in code "
                    + sb.scancode + " - (" + sb.hitLeft
                    + "," + sb.hitTop + ")-(" + sb.hitRight + ","
                    + sb.hitBottom + ")");
            if (sb.checkHit(absm.mNextData[MotionEvent.SAMPLE_X],
                    absm.mNextData[MotionEvent.SAMPLE_Y])) {
                if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Hit!");
                return sb;
            }
        }
        
        return null;
    }
    
    private boolean generateVirtualKeyDown(InputDevice di, RawInputEvent ev,
            long curTime, long curTimeNano) {
        if (isInsideDisplay(di)) {
            // Didn't consume event.
            return false;
        }
        
        
        VirtualKey vk = findVirtualKey(di);
        if (vk != null) {
            final InputDevice.MotionState ms = di.mAbs;
            mPressedVirtualKey = vk;
            vk.lastKeycode = scancodeToKeycode(di.id, vk.scancode);
            ms.mLastNumPointers = ms.mNextNumPointers;
            di.mKeyDownTime = curTime;
            if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG,
                    "Generate key down for: " + vk.scancode
                    + " (keycode=" + vk.lastKeycode + ")");
            KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, true,
                    vk.lastKeycode, 0, vk.scancode,
                    KeyEvent.FLAG_VIRTUAL_HARD_KEY);
            mHapticFeedbackCallback.virtualKeyFeedback(event);
            addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
                    event);
        }
        
        // We always consume the event, even if we didn't
        // generate a key event.  There are two reasons for
        // this: to avoid spurious touches when holding
        // the edges of the device near the touchscreen,
        // and to avoid reporting events if there are virtual
        // keys on the touchscreen outside of the display
        // area.
        // Note that for all of this we are only looking at the
        // first pointer, since what we are handling here is the
        // first pointer going down, and this is the coordinate
        // that will be used to dispatch the event.
        if (false) {
            final InputDevice.AbsoluteInfo absx = di.absX;
            final InputDevice.AbsoluteInfo absy = di.absY;
            final InputDevice.MotionState absm = di.mAbs;
            Slog.v(TAG, "Rejecting ("
                + absm.mNextData[MotionEvent.SAMPLE_X] + ","
                + absm.mNextData[MotionEvent.SAMPLE_Y] + "): outside of ("
                + absx.minValue + "," + absy.minValue
                + ")-(" + absx.maxValue + ","
                + absx.maxValue + ")");
        }
        return true;
    }
    
    private boolean monitorVirtualKey(InputDevice di, RawInputEvent ev,
            long curTime, long curTimeNano) {
        VirtualKey vk = mPressedVirtualKey;
        if (vk == null) {
            return false;
        }
        
        final InputDevice.MotionState ms = di.mAbs;
        if (ms.mNextNumPointers <= 0) {
            mPressedVirtualKey = null;
            ms.mLastNumPointers = 0;
            if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Generate key up for: " + vk.scancode);
            KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
                    vk.lastKeycode, 0, vk.scancode,
                    KeyEvent.FLAG_VIRTUAL_HARD_KEY);
            mHapticFeedbackCallback.virtualKeyFeedback(event);
            addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
                    event);
            return true;
            
        } else if (isInsideDisplay(di)) {
            // Whoops the pointer has moved into
            // the display area!  Cancel the
            // virtual key and start a pointer
            // motion.
            mPressedVirtualKey = null;
            if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Cancel key up for: " + vk.scancode);
            KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
                    vk.lastKeycode, 0, vk.scancode,
                    KeyEvent.FLAG_CANCELED | KeyEvent.FLAG_VIRTUAL_HARD_KEY);
            mHapticFeedbackCallback.virtualKeyFeedback(event);
            addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
                    event);
            ms.mLastNumPointers = 0;
            return false;
        }
        
        return true;
    }
    
    /**
     * Returns a new meta state for the given keys and old state.
     */
    private static final int makeMetaState(int keycode, boolean down, int old) {
        int mask;
        switch (keycode) {
        case KeyEvent.KEYCODE_ALT_LEFT:
            mask = KeyEvent.META_ALT_LEFT_ON;
            break;
        case KeyEvent.KEYCODE_ALT_RIGHT:
            mask = KeyEvent.META_ALT_RIGHT_ON;
            break;
        case KeyEvent.KEYCODE_SHIFT_LEFT:
            mask = KeyEvent.META_SHIFT_LEFT_ON;
            break;
        case KeyEvent.KEYCODE_SHIFT_RIGHT:
            mask = KeyEvent.META_SHIFT_RIGHT_ON;
            break;
        case KeyEvent.KEYCODE_SYM:
            mask = KeyEvent.META_SYM_ON;
            break;
        default:
            return old;
        }
        int result = ~(KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)
                    & (down ? (old | mask) : (old & ~mask));
        if (0 != (result & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON))) {
            result |= KeyEvent.META_ALT_ON;
        }
        if (0 != (result & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON))) {
            result |= KeyEvent.META_SHIFT_ON;
        }
        return result;
    }

    private void computeGlobalMetaStateLocked() {
        int i = mDevices.size();
        mGlobalMetaState = 0;
        while ((--i) >= 0) {
            mGlobalMetaState |= mDevices.valueAt(i).mMetaKeysState;
        }
        mHaveGlobalMetaState = true;
    }
    
    /*
     * Return true if you want the event to get passed on to the 
     * rest of the system, and false if you've handled it and want
     * it dropped.
     */
    abstract boolean preprocessEvent(InputDevice device, RawInputEvent event);

    InputDevice getInputDevice(int deviceId) {
        synchronized (mFirst) {
            return getInputDeviceLocked(deviceId);
        }
    }
    
    private InputDevice getInputDeviceLocked(int deviceId) {
        return mDevices.get(deviceId);
    }

    public void setOrientation(int orientation) {
        synchronized(mFirst) {
            mOrientation = orientation;
            switch (orientation) {
                case Surface.ROTATION_90:
                    mKeyRotationMap = KEY_90_MAP;
                    break;
                case Surface.ROTATION_180:
                    mKeyRotationMap = KEY_180_MAP;
                    break;
                case Surface.ROTATION_270:
                    mKeyRotationMap = KEY_270_MAP;
                    break;
                default:
                    mKeyRotationMap = null;
                    break;
            }
        }
    }
    
    public int rotateKeyCode(int keyCode) {
        synchronized(mFirst) {
            return rotateKeyCodeLocked(keyCode);
        }
    }
    
    private int rotateKeyCodeLocked(int keyCode) {
        int[] map = mKeyRotationMap;
        if (map != null) {
            final int N = map.length;
            for (int i=0; i<N; i+=2) {
                if (map[i] == keyCode) {
                    return map[i+1];
                }
            }
        }
        return keyCode;
    }
    
    boolean hasEvents() {
        synchronized (mFirst) {
            return mFirst.next != mLast;
        }
    }
    
    /*
     * returns true if we returned an event, and false if we timed out
     */
    QueuedEvent getEvent(long timeoutMS) {
        long begin = SystemClock.uptimeMillis();
        final long end = begin+timeoutMS;
        long now = begin;
        synchronized (mFirst) {
            while (mFirst.next == mLast && end > now) {
                try {
                    mWakeLock.release();
                    mFirst.wait(end-now);
                }
                catch (InterruptedException e) {
                }
                now = SystemClock.uptimeMillis();
                if (begin > now) {
                    begin = now;
                }
            }
            if (mFirst.next == mLast) {
                return null;
            }
            QueuedEvent p = mFirst.next;
            mFirst.next = p.next;
            mFirst.next.prev = mFirst;
            p.inQueue = false;
            return p;
        }
    }

    /**
     * Return true if the queue has an up event pending that corresponds
     * to the same key as the given key event.
     */
    boolean hasKeyUpEvent(KeyEvent origEvent) {
        synchronized (mFirst) {
            final int keyCode = origEvent.getKeyCode();
            QueuedEvent cur = mLast.prev;
            while (cur.prev != null) {
                if (cur.classType == RawInputEvent.CLASS_KEYBOARD) {
                    KeyEvent ke = (KeyEvent)cur.event;
                    if (ke.getAction() == KeyEvent.ACTION_UP
                            && ke.getKeyCode() == keyCode) {
                        return true;
                    }
                }
                cur = cur.prev;
            }
        }
        
        return false;
    }
    
    void recycleEvent(QueuedEvent ev) {
        synchronized (mFirst) {
            //Slog.i(TAG, "Recycle event: " + ev);
            if (ev.event == ev.inputDevice.mAbs.currentMove) {
                ev.inputDevice.mAbs.currentMove = null;
            }
            if (ev.event == ev.inputDevice.mRel.currentMove) {
                if (false) Slog.i(TAG, "Detach rel " + ev.event);
                ev.inputDevice.mRel.currentMove = null;
                ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_X] = 0;
                ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_Y] = 0;
            }
            recycleLocked(ev);
        }
    }
    
    void filterQueue(FilterCallback cb) {
        synchronized (mFirst) {
            QueuedEvent cur = mLast.prev;
            while (cur.prev != null) {
                switch (cb.filterEvent(cur)) {
                    case FILTER_REMOVE:
                        cur.prev.next = cur.next;
                        cur.next.prev = cur.prev;
                        break;
                    case FILTER_ABORT:
                        return;
                }
                cur = cur.prev;
            }
        }
    }
    
    private QueuedEvent obtainLocked(InputDevice device, long whenNano,
            int flags, int classType, Object event) {
        QueuedEvent ev;
        if (mCacheCount == 0) {
            ev = new QueuedEvent();
        } else {
            ev = mCache;
            ev.inQueue = false;
            mCache = ev.next;
            mCacheCount--;
        }
        ev.inputDevice = device;
        ev.whenNano = whenNano;
        ev.flags = flags;
        ev.classType = classType;
        ev.event = event;
        return ev;
    }

    private void recycleLocked(QueuedEvent ev) {
        if (ev.inQueue) {
            throw new RuntimeException("Event already in queue!");
        }
        if (mCacheCount < 10) {
            mCacheCount++;
            ev.next = mCache;
            mCache = ev;
            ev.inQueue = true;
        }
    }

    private void addLocked(InputDevice device, long whenNano, int flags,
            int classType, Object event) {
        boolean poke = mFirst.next == mLast;

        QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event);
        QueuedEvent p = mLast.prev;
        while (p != mFirst && ev.whenNano < p.whenNano) {
            p = p.prev;
        }

        ev.next = p.next;
        ev.prev = p;
        p.next = ev;
        ev.next.prev = ev;
        ev.inQueue = true;

        if (poke) {
            long time;
            if (MEASURE_LATENCY) {
                time = System.nanoTime();
            }
            mFirst.notify();
            mWakeLock.acquire();
            if (MEASURE_LATENCY) {
                lt.sample("1 addLocked-queued event ", System.nanoTime() - time);
            }
        }
    }

    private InputDevice newInputDevice(int deviceId) {
        int classes = getDeviceClasses(deviceId);
        String name = getDeviceName(deviceId);
        InputDevice.AbsoluteInfo absX = null;
        InputDevice.AbsoluteInfo absY = null;
        InputDevice.AbsoluteInfo absPressure = null;
        InputDevice.AbsoluteInfo absSize = null;
        if (classes != 0) {
            Slog.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
                    + ", name=" + name
                    + ", classes=" + Integer.toHexString(classes));
            if ((classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
                absX = loadAbsoluteInfo(deviceId,
                        RawInputEvent.ABS_MT_POSITION_X, "X");
                absY = loadAbsoluteInfo(deviceId,
                        RawInputEvent.ABS_MT_POSITION_Y, "Y");
                absPressure = loadAbsoluteInfo(deviceId,
                        RawInputEvent.ABS_MT_TOUCH_MAJOR, "Pressure");
                absSize = loadAbsoluteInfo(deviceId,
                        RawInputEvent.ABS_MT_WIDTH_MAJOR, "Size");
            } else if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
                absX = loadAbsoluteInfo(deviceId,
                        RawInputEvent.ABS_X, "X");
                absY = loadAbsoluteInfo(deviceId,
                        RawInputEvent.ABS_Y, "Y");
                absPressure = loadAbsoluteInfo(deviceId,
                        RawInputEvent.ABS_PRESSURE, "Pressure");
                absSize = loadAbsoluteInfo(deviceId,
                        RawInputEvent.ABS_TOOL_WIDTH, "Size");
            }
        }
        
        return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);
    }
    
    private InputDevice.AbsoluteInfo loadAbsoluteInfo(int id, int channel,
            String name) {
        InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo();
        if (getAbsoluteInfo(id, channel, info)
                && info.minValue != info.maxValue) {
            Slog.i(TAG, "  " + name + ": min=" + info.minValue
                    + " max=" + info.maxValue
                    + " flat=" + info.flat
                    + " fuzz=" + info.fuzz);
            info.range = info.maxValue-info.minValue;
            return info;
        }
        Slog.i(TAG, "  " + name + ": unknown values");
        return null;
    }
    private static native boolean readEvent(RawInputEvent outEvent);
    
    void dump(PrintWriter pw, String prefix) {
        synchronized (mFirst) {
            for (int i=0; i<mDevices.size(); i++) {
                InputDevice dev = mDevices.valueAt(i);
                pw.print(prefix); pw.print("Device #");
                        pw.print(mDevices.keyAt(i)); pw.print(" ");
                        pw.print(dev.name); pw.print(" (classes=0x");
                        pw.print(Integer.toHexString(dev.classes));
                        pw.println("):");
                pw.print(prefix); pw.print("  mKeyDownTime=");
                        pw.print(dev.mKeyDownTime); pw.print(" mMetaKeysState=");
                        pw.println(dev.mMetaKeysState);
                if (dev.absX != null) {
                    pw.print(prefix); pw.print("  absX: "); dev.absX.dump(pw);
                            pw.println("");
                }
                if (dev.absY != null) {
                    pw.print(prefix); pw.print("  absY: "); dev.absY.dump(pw);
                            pw.println("");
                }
                if (dev.absPressure != null) {
                    pw.print(prefix); pw.print("  absPressure: ");
                            dev.absPressure.dump(pw); pw.println("");
                }
                if (dev.absSize != null) {
                    pw.print(prefix); pw.print("  absSize: ");
                            dev.absSize.dump(pw); pw.println("");
                }
                if (dev.mAbs.everChanged) {
                    pw.print(prefix); pw.println("  mAbs:");
                    dev.mAbs.dump(pw, prefix + "    ");
                }
                if (dev.mRel.everChanged) {
                    pw.print(prefix); pw.println("  mRel:");
                    dev.mRel.dump(pw, prefix + "    ");
                }
            }
            pw.println(" ");
            for (int i=0; i<mIgnoredDevices.size(); i++) {
                InputDevice dev = mIgnoredDevices.valueAt(i);
                pw.print(prefix); pw.print("Ignored Device #");
                        pw.print(mIgnoredDevices.keyAt(i)); pw.print(" ");
                        pw.print(dev.name); pw.print(" (classes=0x");
                        pw.print(Integer.toHexString(dev.classes));
                        pw.println(")");
            }
            pw.println(" ");
            for (int i=0; i<mVirtualKeys.size(); i++) {
                VirtualKey vk = mVirtualKeys.get(i);
                pw.print(prefix); pw.print("Virtual Key #");
                        pw.print(i); pw.println(":");
                pw.print(prefix); pw.print("  scancode="); pw.println(vk.scancode);
                pw.print(prefix); pw.print("  centerx="); pw.print(vk.centerx);
                        pw.print(" centery="); pw.print(vk.centery);
                        pw.print(" width="); pw.print(vk.width);
                        pw.print(" height="); pw.println(vk.height);
                pw.print(prefix); pw.print("  hitLeft="); pw.print(vk.hitLeft);
                        pw.print(" hitTop="); pw.print(vk.hitTop);
                        pw.print(" hitRight="); pw.print(vk.hitRight);
                        pw.print(" hitBottom="); pw.println(vk.hitBottom);
                if (vk.lastDevice != null) {
                    pw.print(prefix); pw.print("  lastDevice=#");
                            pw.println(vk.lastDevice.id);
                }
                if (vk.lastKeycode != 0) {
                    pw.print(prefix); pw.print("  lastKeycode=");
                            pw.println(vk.lastKeycode);
                }
            }
            pw.println(" ");
            pw.print(prefix); pw.print("  Default keyboard: ");
                    pw.println(SystemProperties.get("hw.keyboards.0.devname"));
            pw.print(prefix); pw.print("  mGlobalMetaState=");
                    pw.print(mGlobalMetaState); pw.print(" mHaveGlobalMetaState=");
                    pw.println(mHaveGlobalMetaState);
            pw.print(prefix); pw.print("  mDisplayWidth=");
                    pw.print(mDisplayWidth); pw.print(" mDisplayHeight=");
                    pw.println(mDisplayHeight);
            pw.print(prefix); pw.print("  mOrientation=");
                    pw.println(mOrientation);
            if (mPressedVirtualKey != null) {
                pw.print(prefix); pw.print("  mPressedVirtualKey.scancode=");
                        pw.println(mPressedVirtualKey.scancode);
            }
        }
    }
}

Other Android examples (source code examples)

Here is a short list of links related to this Android KeyInputQueue.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.