Guest totiadrenalin Posted September 25, 2010 Report Posted September 25, 2010 http://android.modaco.com/content-page/295...heory/page/800/
Guest totiadrenalin Posted September 26, 2010 Report Posted September 26, 2010 (edited) http://android.modaco.com/content-page/295...heory/page/800/ BB. I really think that the multitouch is near the finale. Because this is single touch, and gesture second touch. So now you need to hack a beet more the kernel, so the second finger will stay at the exact point where you put it, and the first finger will gesture the movements. And I still think that the problem is where I already told you in the framework two. Because with the hacked demo MT maps, browser, and MT photosotr, I can get really not that bad pich&zoom. Maybe if I try to restore the firmware of the touchcontroller I'll get even better second finger GESTURE. So old PALL! It's about time to put an and of this suffering about the MT. :lol: EDIT: I found the secret of the G1 MT in the browser. /** * MultiTouchController.java * * © Luke Hutchison ([email protected]) * * Released under the Apache License v2. */ package com.android.browser; import android.content.res.Configuration; import android.content.res.Resources; import android.util.DisplayMetrics; import android.view.MotionEvent; /** * A class that simplifies the implementation of multitouch in applications. Subclass this and read the fields here as needed in * subclasses. * * @author Luke Hutchison */ public class MultiTouchController<T> { /** * Time in ms required after a change in event status (e.g. putting down or lifting off the second finger) before events * actually do anything -- helps eliminate noisy jumps that happen on change of status */ private static final long EVENT_SETTLE_TIME_INTERVAL = 100; // The biggest possible abs val of the change in x or y between multitouch events // (larger dx/dy events are ignored) -- helps eliminate jumping on finger 2 up/down private static final float MAX_MULTITOUCH_POS_JUMP_SIZE = 30.0f; // The biggest possible abs val of the change in multitouchWidth or multitouchHeight between // multitouch events (larger-jump events are ignored) -- helps eliminate jumping on finger 2 up/down private static final float MAX_MULTITOUCH_DIM_JUMP_SIZE = 40.0f; // The smallest possible distance between multitouch points (used to avoid div-by-zero errors) private static final float MIN_MULTITOUCH_SEPARATION = 10.0f; // -- MultiTouchObjectCanvas<T> objectCanvas; private PointInfo currPt, prevPt; // -- private T draggedObject = null; private long dragStartTime, dragSettleTime; // Conversion from object coords to screen coords, and from drag width to object scale private float objDraggedPointX, objDraggedPointY, objStartScale; private PositionAndScale objPosAndScale = new PositionAndScale(); // -- /** Whether to handle single-touch events/drags before multi-touch is initiated or not; if not, they are handled by subclasses */ private boolean handleSingleTouchEvents; // -- private static final int MODE_NOTHING = 0; private static final int MODE_DRAG = 1; private static final int MODE_STRETCH = 2; private int dragMode = MODE_NOTHING; // ------------------------------------------------------------------------------------ /** Constructor that sets handleSingleTouchEvents to true */ public MultiTouchController(MultiTouchObjectCanvas<T> objectCanvas, Resources res) { this(objectCanvas, res, true); } /** Full constructor */ public MultiTouchController(MultiTouchObjectCanvas<T> objectCanvas, Resources res, boolean handleSingleTouchEvents) { this.currPt = new PointInfo(res); this.prevPt = new PointInfo(res); this.handleSingleTouchEvents = handleSingleTouchEvents; this.objectCanvas = objectCanvas; } // ------------------------------------------------------------------------------------ /** * Whether to handle single-touch events/drags before multi-touch is initiated or not; if not, they are handled by subclasses. * Default: true */ protected void setHandleSingleTouchEvents(boolean handleSingleTouchEvents) { this.handleSingleTouchEvents = handleSingleTouchEvents; } /** * Whether to handle single-touch events/drags before multi-touch is initiated or not; if not, they are handled by subclasses. * Default: true */ protected boolean getHandleSingleTouchEvents() { return handleSingleTouchEvents; } // ------------------------------------------------------------------------------------ /** Process incoming touch events */ public boolean onTouchEvent(MotionEvent event) { if (dragMode == MODE_NOTHING && !handleSingleTouchEvents && event.getSize() <= 1.0f) // Not handling initial single touch events, just pass them on return false; // Handle history first, if any (we sometimes get history with ACTION_MOVE events) int histLen = event.getHistorySize(); for (int i = 0; i < histLen; i++) decodeTouchEvent(event.getHistoricalX(i), event.getHistoricalY(i), event.getHistoricalPressure(i), event .getHistoricalSize(i), true, MotionEvent.ACTION_MOVE, event.getHistoricalEventTime(i)); // Handle actual event at end of history decodeTouchEvent(event.getX(), event.getY(), event.getPressure(), event.getSize(), event.getAction() != MotionEvent.ACTION_UP && event.getAction() != MotionEvent.ACTION_CANCEL, event.getAction(), event.getEventTime()); return true; } private void decodeTouchEvent(float x, float y, float pressure, float undecodedSize, boolean down, int action, long eventTime) { // Decode size field for multitouch events and then handle the event prevPt.set(currPt); currPt.set(x, y, pressure, undecodedSize, down, action, eventTime); multiTouchController(); } // ------------------------------------------------------------------------------------ /** Start dragging/stretching, or reset drag/stretch to current point if something goes out of range */ private void resetDrag() { if (draggedObject == null) return; // Get dragged object position and scale objectCanvas.getPositionAndScale(draggedObject, objPosAndScale); // Figure out the object coords of the drag start point's screen coords. // All stretching should be around this point in object-coord-space. float scaleInv = (objPosAndScale.scale == 0.0f ? 1.0f : 1.0f / objPosAndScale.scale); objDraggedPointX = (currPt.getX() - objPosAndScale.xOff) * scaleInv; objDraggedPointY = (currPt.getY() - objPosAndScale.yOff) * scaleInv; // Figure out ratio between object scale factor and multitouch diameter (they are linearly correlated) float diam = currPt.getMultiTouchDiameter(); objStartScale = objPosAndScale.scale / (diam == 0.0f ? 1.0f : diam); } /** Drag/stretch the dragged object to the current touch position and diameter */ private void performDrag() { // Don't do anything if we're not dragging anything if (draggedObject == null) return; // Calc new position of dragged object float scale = (objPosAndScale.scale == 0.0f ? 1.0f : objPosAndScale.scale); float newObjPosX = currPt.getX() - objDraggedPointX * scale; float newObjPosY = currPt.getY() - objDraggedPointY * scale; // Get new drag diameter (avoiding divsion by zero), and calculate new drag scale float diam; if (!currPt.isMultiTouch) { // Single-touch, no change in scale diam = 1.0f; } else { diam = currPt.getMultiTouchDiameter(); if (diam < MIN_MULTITOUCH_SEPARATION) diam = MIN_MULTITOUCH_SEPARATION; } float newScale = diam * objStartScale; // Get the new obj coords and scale, and set them (notifying the subclass of the change) objPosAndScale.set(newObjPosX, newObjPosY, newScale); boolean success = objectCanvas.setPositionAndScale(draggedObject, objPosAndScale, currPt); if (!success) ; // If we could't set those params, do nothing currently } /** The main single-touch and multi-touch logic */ private void multiTouchController() { switch (dragMode) { case MODE_NOTHING: // Not doing anything currently if (currPt.isDown() && !currPt.isDownPrev()) { // Start a new single-point drag draggedObject = objectCanvas.getDraggableObjectAtPoint(currPt); if (draggedObject != null) { // Started a new single-point drag dragMode = MODE_DRAG; objectCanvas.selectObject(draggedObject, currPt); resetDrag(); // Don't need any settling time if just placing one finger, there is no noise dragStartTime = dragSettleTime = currPt.getEventTime(); } } break; case MODE_DRAG: // Currently in a single-point drag if (!currPt.isDown()) { // First finger was released, stop dragging dragMode = MODE_NOTHING; objectCanvas.selectObject((draggedObject = null), currPt); } else if (currPt.isMultiTouch()) { // Point 1 was already down and point 2 was just placed down dragMode = MODE_STRETCH; // Restart the drag with the new drag position (that is at the midpoint between the touchpoints) resetDrag(); // Need to let events settle before moving things, to help with event noise on touchdown dragStartTime = currPt.getEventTime(); dragSettleTime = dragStartTime + EVENT_SETTLE_TIME_INTERVAL; } else { // Point 1 is still down and point 2 did not change state, just do single-point drag to new location if (currPt.getEventTime() < dragSettleTime) { // Ignore the first few events if we just stopped stretching, because if finger 2 was kept down while // finger 1 is lifted, then point 1 gets mapped to finger 2. Restart the drag from the new position. resetDrag(); } else { // Keep dragging, move to new point performDrag(); } } break; case MODE_STRETCH: // Two-point stretch if (!currPt.isMultiTouch() || !currPt.isDown()) { // Dropped one or both points, stop stretching if (!currPt.isDown()) { // Dropped both points, go back to doing nothing dragMode = MODE_NOTHING; objectCanvas.selectObject((draggedObject = null), currPt); } else { // Just dropped point 2, downgrade to a single-point drag dragMode = MODE_DRAG; // Restart the drag with the single-finger position resetDrag(); // Ignore the first few events after the drop, in case we dropped finger 1 and left finger 2 down dragStartTime = currPt.getEventTime(); dragSettleTime = dragStartTime + EVENT_SETTLE_TIME_INTERVAL; } } else { // Keep stretching if (Math.abs(currPt.getX() - prevPt.getX()) > MAX_MULTITOUCH_POS_JUMP_SIZE || Math.abs(currPt.getY() - prevPt.getY()) > MAX_MULTITOUCH_POS_JUMP_SIZE || Math.abs(currPt.getMultiTouchWidth() - prevPt.getMultiTouchWidth()) * .5f > MAX_MULTITOUCH_DIM_JUMP_SIZE || Math.abs(currPt.getMultiTouchHeight() - prevPt.getMultiTouchHeight()) * .5f > MAX_MULTITOUCH_DIM_JUMP_SIZE) { // Jumped too far, probably event noise, reset and ignore events for a bit resetDrag(); dragStartTime = currPt.getEventTime(); dragSettleTime = dragStartTime + EVENT_SETTLE_TIME_INTERVAL; } else if (currPt.eventTime < dragSettleTime) { // Events have not yet settled, reset resetDrag(); } else { // Stretch to new position and size performDrag(); } } break; } } // ------------------------------------------------------------------------------------ /** A class that packages up all MotionEvent information with all derived multitouch information (if available) */ public static class PointInfo { private float x, y, dx, dy, size, diameter, diameterSq, angle, pressure; private boolean down, downPrev, isMultiTouch, diameterSqIsCalculated, diameterIsCalculated, angleIsCalculated; private int action; private long eventTime; // -- private Resources res; private int displayWidth, displayHeight; // -- public PointInfo(Resources res) { this.res = res; DisplayMetrics metrics = res.getDisplayMetrics(); this.displayWidth = metrics.widthPixels; this.displayHeight = metrics.heightPixels; } // -- public PointInfo(PointInfo other) { this.set(other); } /** Copy all fields */ public void set(PointInfo other) { this.displayWidth = other.displayWidth; this.displayHeight = other.displayHeight; this.x = other.x; this.y = other.y; this.dx = other.dx; this.dy = other.dy; this.size = other.size; this.diameter = other.diameter; this.diameterSq = other.diameterSq; this.angle = other.angle; this.pressure = other.pressure; this.down = other.down; this.action = other.action; this.downPrev = other.downPrev; this.isMultiTouch = other.isMultiTouch; this.diameterIsCalculated = other.diameterIsCalculated; this.diameterSqIsCalculated = other.diameterSqIsCalculated; this.angleIsCalculated = other.angleIsCalculated; this.eventTime = other.eventTime; } /** Setter for use in event decoding */ private void set(float x, float y, float pressure, float undecodedSize, boolean down, int action, long eventTime) { this.x = x; this.y = y; this.pressure = pressure; this.downPrev = this.down; this.down = down; this.action = action; this.eventTime = eventTime; // 1.0f is the max value for size on an unpatched kernel -- make this backwards-compatible this.isMultiTouch = (undecodedSize > 1.0f); if (isMultiTouch) { // Multitouch event, decipher size field, which contains dx and dy // dx and dy come packaged in a 12-pt fixed-point number that has been converted into a float int szi = (int) undecodedSize; int dxi = szi >> 12; int dyi = szi & ((1 << 12) - 1); // Get display size and rotation, scale fixed point to screen coords dx = Math.min(displayWidth, displayHeight) * dxi / (float) ((1 << 12) - 1); dy = Math.max(displayWidth, displayHeight) * dyi / (float) ((1 << 12) - 1); if (screenIsRotated()) { // Swap x,y if screen is rotated float tmp = dx; dx = dy; dy = tmp; } } else { // Single-touch event dx = dy = diameter = 0.0f; // undecodedSize == 0 for single-touch when the multitouch kernel patch is applied this.size = undecodedSize; } // Need to re-calculate the expensive params if they're needed diameterSqIsCalculated = diameterIsCalculated = angleIsCalculated = false; } private boolean screenIsRotated() { return res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; } // Fast integer sqrt, by Jim Ulery. Should be faster than Math.sqrt() private int julery_isqrt(int val) { int temp, g = 0, b = 0x8000, bshft = 15; do { if (val >= (temp = (((g << 1) + B) << bshft--))) { g += b; val -= temp; } } while ((b >>= 1) > 0); return g; } /** Calculate the squared diameter of the multitouch event, and cache it. Use this if you don't need to perform the sqrt. */ public float getMultiTouchDiameterSq() { if (!diameterSqIsCalculated) { diameterSq = (isMultiTouch ? dx * dx + dy * dy : 0.0f); diameterSqIsCalculated = true; } return diameterSq; } /** Calculate the diameter of the multitouch event, and cache it. Uses fast int sqrt but gives accuracy to 1/16px. */ public float getMultiTouchDiameter() { if (!diameterIsCalculated) { // Get 1/16 pixel's worth of subpixel accuracy, works on screens up to 2048x2048 // before we get overflow (at which point you can reduce or eliminate subpix // accuracy, or use longs in julery_isqrt()) float diamSq = getMultiTouchDiameterSq(); diameter = (diamSq == 0.0f ? 0.0f : (float) julery_isqrt((int) (256 * diamSq)) / 16.0f); // Make sure diameter is never less than dx or dy, for trig purposes if (diameter < dx) diameter = dx; if (diameter < dy) diameter = dy; diameterIsCalculated = true; } return diameter; } /** * Calculate the angle of a multitouch event, and cache it. Actually gives the smaller of the two angles between the x axis * and the line between the two touchpoints, so range is [0,Math.PI/2]. Uses Math.atan2(). */ public float getMultiTouchAngle() { if (!angleIsCalculated) { angle = (float) Math.atan2(dy, dx); angleIsCalculated = true; } return angle; } public float getX() { return x; } public float getY() { return y; } public float getMultiTouchWidth() { return dx; } public float getMultiTouchHeight() { return dy; } public float getSize() { return size; } public float getPressure() { return pressure; } public boolean isDown() { return down; } public boolean isDownPrev() { return downPrev; } public int getAction() { return action; } public boolean isMultiTouch() { return isMultiTouch; } public long getEventTime() { return eventTime; } } // ------------------------------------------------------------------------------------ /** A class that is used to store scroll offsets and scale information for objects that are managed by the multitouch controller */ public static class PositionAndScale { private float xOff, yOff, scale; public PositionAndScale() { } public void set(float xOff, float yOff, float scale) { this.xOff = xOff; this.yOff = yOff; this.scale = scale; } public float getXOff() { return xOff; } public float getYOff() { return yOff; } public float getScale() { return scale; } } // ------------------------------------------------------------------------------------ public static interface MultiTouchObjectCanvas<T> { /** See if there is a draggable object at the current point. Returns the object at the point, or null if nothing to drag. */ public T getDraggableObjectAtPoint(PointInfo pt); /** * Get the screen coords of the dragged object's origin, and scale multiplier to convert screen coords to obj coords. Call * the .set() method on the passed PositionAndScale object. */ public void getPositionAndScale(T obj, PositionAndScale objPosAndScaleOut); /** * Set the position and scale of the dragged object, in object coords. Return true for success, or false if those parameters * are out of range. */ public boolean setPositionAndScale(T obj, PositionAndScale newObjPosAndScale, PointInfo touchPoint); /** * Select an object at the given point. Can be used to bring the object to top etc. Only called when first touchpoint goes * down, not when multitouch is initiated. Also called with null when drag op stops. */ public void selectObject(T obj, PointInfo pt); } } [/codebox] EDIT2: And the Final Product. The patched KeyInputQueue.java [codebox] /* * Copyright © 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 * * [url="http://www.apache.org/licenses/LICENSE-2.0"]http://www.apache.org/licenses/LICENSE-2.0[/url] * * 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.os.SystemClock; import android.os.PowerManager; import android.util.Log; import android.util.SparseArray; import android.view.Display; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.RawInputEvent; import android.view.Surface; import android.view.WindowManagerPolicy; public abstract class KeyInputQueue { static final String TAG = "KeyInputQueue"; SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>(); int mGlobalMetaState = 0; boolean mHaveGlobalMetaState = false; final QueuedEvent mFirst; final QueuedEvent mLast; QueuedEvent mCache; int mCacheCount; Display mDisplay = null; int mOrientation = Surface.ROTATION_0; int[] mKeyRotationMap = 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; public interface FilterCallback { int filterEvent(QueuedEvent ev); } static class QueuedEvent { InputDevice inputDevice; long when; 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.when = that.when; 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; } KeyInputQueue(Context context) { 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; mThread.start(); } public void setDisplay(Display display) { mDisplay = display; } 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; //Log.i("foo", "***** HAVE TOUCHSCREEN!"); } if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) { config.keyboard = Configuration.KEYBOARD_QWERTY; //Log.i("foo", "***** HAVE QWERTY!"); } if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) { config.navigation = Configuration.NAVIGATION_TRACKBALL; //Log.i("foo", "***** HAVE TRACKBALL!"); } } } } } public static native String getDeviceName(int deviceId); public static native int getDeviceClasses(int deviceId); 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 getScancodeState(int sw); public static native int getScancodeState(int deviceId, int sw); public static native int getKeycodeState(int sw); public static native int getKeycodeState(int deviceId, int sw); 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() { android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY); try { RawInputEvent ev = new RawInputEvent(); while (true) { InputDevice di; // block, doesn't release the monitor readEvent(ev); boolean send = false; boolean configChanged = false; if (false) { Log.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); mDevices.put(ev.deviceId, di); configChanged = true; } } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) { synchronized (mFirst) { Log.i(TAG, "Device removed: id=0x" + Integer.toHexString(ev.deviceId)); di = mDevices.get(ev.deviceId); if (di != null) { mDevices.delete(ev.deviceId); configChanged = true; } else { Log.w(TAG, "Bad device id: " + ev.deviceId); } } } else { di = getInputDevice(ev.deviceId); // 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 (di == null) { continue; } if (configChanged) { synchronized (mFirst) { addLocked(di, SystemClock.uptimeMillis(), 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(); //Log.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.mDownTime = curTime; } else { down = false; } int keycode = rotateKeyCodeLocked(ev.keycode); addLocked(di, curTime, ev.flags, RawInputEvent.CLASS_KEYBOARD, newKeyEvent(di, di.mDownTime, curTime, down, keycode, 0, scancode, ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0) ? KeyEvent.FLAG_WOKE_HERE : 0)); } else if (ev.type == RawInputEvent.EV_KEY) { if (ev.scancode == RawInputEvent.BTN_TOUCH && (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) { di.mAbs.changed = true; di.mAbs.down = ev.value != 0; } if (ev.scancode == RawInputEvent.BTN_MOUSE && (classes&RawInputEvent.CLASS_TRACKBALL) != 0) { di.mRel.changed = true; di.mRel.down = ev.value != 0; send = true; } } else if (ev.type == RawInputEvent.EV_ABS && (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) { if (ev.scancode == RawInputEvent.ABS_X) { di.mAbs.changed = true; di.mAbs.x = ev.value; } else if (ev.scancode == RawInputEvent.ABS_Y) { di.mAbs.changed = true; di.mAbs.y = ev.value; } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) { di.mAbs.changed = true; di.mAbs.pressure = ev.value; } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) { di.mAbs.changed = true; di.mAbs.size = ev.value; } } 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.x += ev.value; } else if (ev.scancode == RawInputEvent.REL_Y) { di.mRel.changed = true; di.mRel.y += ev.value; } } if (send || ev.type == RawInputEvent.EV_SYN) { if (mDisplay != null) { if (!mHaveGlobalMetaState) { computeGlobalMetaStateLocked(); } MotionEvent me; me = di.mAbs.generateMotion(di, curTime, true, mDisplay, mOrientation, mGlobalMetaState); if (false) Log.v(TAG, "Absolute: x=" + di.mAbs.x + " y=" + di.mAbs.y + " ev=" + me); if (me != null) { if (WindowManagerPolicy.WATCH_POINTER) { Log.i(TAG, "Enqueueing: " + me); } addLocked(di, curTime, ev.flags, RawInputEvent.CLASS_TOUCHSCREEN, me); } me = di.mRel.generateMotion(di, curTime, false, mDisplay, mOrientation, mGlobalMetaState); if (false) Log.v(TAG, "Relative: x=" + di.mRel.x + " y=" + di.mRel.y + " ev=" + me); if (me != null) { addLocked(di, curTime, ev.flags, RawInputEvent.CLASS_TRACKBALL, me); } } } } } } catch (RuntimeException exc) { Log.e(TAG, "InputReaderThread uncaught exception", exc); } } }; /** * 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; } } void recycleEvent(QueuedEvent ev) { synchronized (mFirst) { //Log.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) Log.i(TAG, "Detach rel " + ev.event); ev.inputDevice.mRel.currentMove = null; ev.inputDevice.mRel.x = 0; ev.inputDevice.mRel.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 when, 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.when = when; 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 when, int flags, int classType, Object event) { boolean poke = mFirst.next == mLast; QueuedEvent ev = obtainLocked(device, when, flags, classType, event); QueuedEvent p = mLast.prev; while (p != mFirst && ev.when < p.when) { p = p.prev; } ev.next = p.next; ev.prev = p; p.next = ev; ev.next.prev = ev; ev.inQueue = true; if (poke) { mFirst.notify(); mWakeLock.acquire(); } } private InputDevice newInputDevice(int deviceId) { int classes = getDeviceClasses(deviceId); String name = getDeviceName(deviceId); Log.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId) + ", name=" + name + ", classes=" + Integer.toHexString(classes)); InputDevice.AbsoluteInfo absX; InputDevice.AbsoluteInfo absY; InputDevice.AbsoluteInfo absPressure; InputDevice.AbsoluteInfo absSize; 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"); } else { absX = null; absY = null; absPressure = null; absSize = null; } 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) { Log.i(TAG, " " + name + ": min=" + info.minValue + " max=" + info.maxValue + " flat=" + info.flat + " fuzz=" + info.fuzz); info.range = info.maxValue-info.minValue; return info; } Log.i(TAG, " " + name + ": unknown values"); return null; } private static native boolean readEvent(RawInputEvent outEvent); } syntax highlighted by Code2HTML, v. 0.9.1 Just put it in place: ANDROID_HOME/frameworks/base/services/java/com/android/server/ And recompile the Android. That's all we need. Maybe this isn't exact what we need, because this file is from G1, But at least we get some point. KeyInputQueue.java Best regards TOTIMultiTouchController.zip Edited September 26, 2010 by totiadrenalin
Guest thesinger Posted September 26, 2010 Report Posted September 26, 2010 (edited) Guys i just tried to move the home screen on the froyo with 2 fingers and worked really nice it worked even with 3 fingers on the screan and showed me no problem. :lol: And i have nothing other installed then the Froyo 0.51 from fibb. Edited September 26, 2010 by thesinger
Guest whackster Posted September 26, 2010 Report Posted September 26, 2010 Guys i just tried to move the home screen on the froyo with 2 fingers and worked really nice it worked even with 3 fingers on the screan and showed me no problem. B) And i have nothing other installed then the Froyo 0.51 from fibb. Holy s%#t its working!!!!!!! B) Seriously, why don't you use an app like multitouch tester, there are a couple different ones on the market and they provide a much better oversight on what's going on with the touchscreen. Sorry, couldn't resist going OT. Oh, and Dan? Where's Dan? :lol:
Guest sfuportuguese Posted September 26, 2010 Report Posted September 26, 2010 only for froyo or controler works for 2.1 too?
Guest DanWilson Posted September 26, 2010 Report Posted September 26, 2010 Holy s%#t its working!!!!!!! B) Seriously, why don't you use an app like multitouch tester, there are a couple different ones on the market and they provide a much better oversight on what's going on with the touchscreen. Sorry, couldn't resist going OT. Oh, and Dan? Where's Dan? :lol: Huh? I think he's just using all 3 fingers at once, causing the screen to think he has one huge finger, and as such, it works. Prove me wrong.
Guest flip360 Posted September 26, 2010 Report Posted September 26, 2010 (edited) Holy s%#t its working!!!!!!! B) Seriously, why don't you use an app like multitouch tester, there are a couple different ones on the market and they provide a much better oversight on what's going on with the touchscreen. Sorry, couldn't resist going OT. Oh, and Dan? Where's Dan? B) What? Where?How? :lol: where do i push this? Edited September 26, 2010 by flip360
Guest whackster Posted September 26, 2010 Report Posted September 26, 2010 Huh? I think he's just using all 3 fingers at once, causing the screen to think he has one huge finger, and as such, it works. Prove me wrong. Just try it, works with four fingers too! Five even if you squeeze them together, but that could be a limit of the screen size :lol: Oh, and B) <--- click the smiley. Just feeling bored this Sunday, don't hold it against me, Toti and BB are doing an amazing job with MT, I have no doubts in them.
Guest thesinger Posted September 26, 2010 Report Posted September 26, 2010 Huh? I think he's just using all 3 fingers at once, causing the screen to think he has one huge finger, and as such, it works. Prove me wrong. Maybe but i tried the pointer location and its working like in the video http://www.youtube.com/user/adrenalinmkd#p/a/u/1/zPqJ-sexHTw even beter in the froyo. Don't give up! :lol:
Guest thesinger Posted September 26, 2010 Report Posted September 26, 2010 But where the hell i can find multitouch visualizer? Can someone tell me becouse he isn't in the market for the 2.2 android (froyo).
Guest DanWilson Posted September 26, 2010 Report Posted September 26, 2010 But where the hell i can find multitouch visualizer? Can someone tell me becouse he isn't in the market for the 2.2 android (froyo). Try Dev Tools.
Guest whackster Posted September 26, 2010 Report Posted September 26, 2010 But where the hell i can find multitouch visualizer? Can someone tell me becouse he isn't in the market for the 2.2 android (froyo). Use market enabler or market access. :lol:
Guest thesinger Posted September 26, 2010 Report Posted September 26, 2010 OOhhh i forgot put 3 fingers in the pointer location in the dev tools and it will show the one of the fingers location.i think that is becouse it needs an other firmwere which mt is suported in the softwere.proof for this is the count of the fingers in the pointer location in the top of the screan 0/1 that means the software suports only one touch.
Guest DanWilson Posted September 26, 2010 Report Posted September 26, 2010 OOhhh i forgot put 3 fingers in the pointer location in the dev tools and it will show the one of the fingers location.i think that is becouse it needs an other firmwere which mt is suported in the softwere.proof for this is the count of the fingers in the pointer location in the top of the screan 0/1 that means the software suports only one touch. Hence why we need a custom kernel and custom touch controller firmware.
Guest thesinger Posted September 26, 2010 Report Posted September 26, 2010 Hence why we need a custom kernel and custom touch controller firmware. I don't now i'm not a pro like bb and toti but i think we need a android version that is built in multitouch like the one in the htc hero.
Guest thisweb Posted September 26, 2010 Report Posted September 26, 2010 I'm confused, are people saying we all have multitouch if we use those demo apps without modification?
Guest DanWilson Posted September 26, 2010 Report Posted September 26, 2010 I don't now i'm not a pro like bb and toti but i think we need a android version that is built in multitouch like the one in the htc hero. Yah but we'll need the kernel and controller first. The version of Android would come after that.
Guest totiadrenalin Posted September 26, 2010 Report Posted September 26, 2010 Hence why we need a custom kernel and custom touch controller firmware. No! Only the Patched Kernel. Because This isn't really multitouch screen, But a singletouch with second finger gesture. So maybe it recognize up to 3 fingers, but it's still singletouch. Because It only gesture the second finger. That's why you can move the screen of the launch app with 3 fingers. But without the patched drivers there's no chance do provide second finger gesture. So don't rush, and let BB correct the kernel, and ad what I post in my previews post. You will get then a real second finger gesture. A Dual touch.
Guest totiadrenalin Posted September 26, 2010 Report Posted September 26, 2010 (edited) BB. I really think that the multitouch is near the finale. Because this is single touch, and gesture second touch. So now you need to hack a beet more the kernel, so the second finger will stay at the exact point where you put it, and the first finger will gesture the movements. And I still think that the problem is where I already told you in the framework two. Because with the hacked demo MT maps, browser, and MT photosotr, I can get really not that bad pich&zoom. Maybe if I try to restore the firmware of the touchcontroller I'll get even better second finger GESTURE. So old PALL! It's about time to put an and of this suffering about the MT. :lol: EDIT: I found the secret of the G1 MT in the browser. /** * MultiTouchController.java * * © Luke Hutchison ([email protected]) * * Released under the Apache License v2. */ package com.android.browser; import android.content.res.Configuration; import android.content.res.Resources; import android.util.DisplayMetrics; import android.view.MotionEvent; /** * A class that simplifies the implementation of multitouch in applications. Subclass this and read the fields here as needed in * subclasses. * * @author Luke Hutchison */ public class MultiTouchController<T> { /** * Time in ms required after a change in event status (e.g. putting down or lifting off the second finger) before events * actually do anything -- helps eliminate noisy jumps that happen on change of status */ private static final long EVENT_SETTLE_TIME_INTERVAL = 100; // The biggest possible abs val of the change in x or y between multitouch events // (larger dx/dy events are ignored) -- helps eliminate jumping on finger 2 up/down private static final float MAX_MULTITOUCH_POS_JUMP_SIZE = 30.0f; // The biggest possible abs val of the change in multitouchWidth or multitouchHeight between // multitouch events (larger-jump events are ignored) -- helps eliminate jumping on finger 2 up/down private static final float MAX_MULTITOUCH_DIM_JUMP_SIZE = 40.0f; // The smallest possible distance between multitouch points (used to avoid div-by-zero errors) private static final float MIN_MULTITOUCH_SEPARATION = 10.0f; // -- MultiTouchObjectCanvas<T> objectCanvas; private PointInfo currPt, prevPt; // -- private T draggedObject = null; private long dragStartTime, dragSettleTime; // Conversion from object coords to screen coords, and from drag width to object scale private float objDraggedPointX, objDraggedPointY, objStartScale; private PositionAndScale objPosAndScale = new PositionAndScale(); // -- /** Whether to handle single-touch events/drags before multi-touch is initiated or not; if not, they are handled by subclasses */ private boolean handleSingleTouchEvents; // -- private static final int MODE_NOTHING = 0; private static final int MODE_DRAG = 1; private static final int MODE_STRETCH = 2; private int dragMode = MODE_NOTHING; // ------------------------------------------------------------------------------------ /** Constructor that sets handleSingleTouchEvents to true */ public MultiTouchController(MultiTouchObjectCanvas<T> objectCanvas, Resources res) { this(objectCanvas, res, true); } /** Full constructor */ public MultiTouchController(MultiTouchObjectCanvas<T> objectCanvas, Resources res, boolean handleSingleTouchEvents) { this.currPt = new PointInfo(res); this.prevPt = new PointInfo(res); this.handleSingleTouchEvents = handleSingleTouchEvents; this.objectCanvas = objectCanvas; } // ------------------------------------------------------------------------------------ /** * Whether to handle single-touch events/drags before multi-touch is initiated or not; if not, they are handled by subclasses. * Default: true */ protected void setHandleSingleTouchEvents(boolean handleSingleTouchEvents) { this.handleSingleTouchEvents = handleSingleTouchEvents; } /** * Whether to handle single-touch events/drags before multi-touch is initiated or not; if not, they are handled by subclasses. * Default: true */ protected boolean getHandleSingleTouchEvents() { return handleSingleTouchEvents; } // ------------------------------------------------------------------------------------ /** Process incoming touch events */ public boolean onTouchEvent(MotionEvent event) { if (dragMode == MODE_NOTHING && !handleSingleTouchEvents && event.getSize() <= 1.0f) // Not handling initial single touch events, just pass them on return false; // Handle history first, if any (we sometimes get history with ACTION_MOVE events) int histLen = event.getHistorySize(); for (int i = 0; i < histLen; i++) decodeTouchEvent(event.getHistoricalX(i), event.getHistoricalY(i), event.getHistoricalPressure(i), event .getHistoricalSize(i), true, MotionEvent.ACTION_MOVE, event.getHistoricalEventTime(i)); // Handle actual event at end of history decodeTouchEvent(event.getX(), event.getY(), event.getPressure(), event.getSize(), event.getAction() != MotionEvent.ACTION_UP && event.getAction() != MotionEvent.ACTION_CANCEL, event.getAction(), event.getEventTime()); return true; } private void decodeTouchEvent(float x, float y, float pressure, float undecodedSize, boolean down, int action, long eventTime) { // Decode size field for multitouch events and then handle the event prevPt.set(currPt); currPt.set(x, y, pressure, undecodedSize, down, action, eventTime); multiTouchController(); } // ------------------------------------------------------------------------------------ /** Start dragging/stretching, or reset drag/stretch to current point if something goes out of range */ private void resetDrag() { if (draggedObject == null) return; // Get dragged object position and scale objectCanvas.getPositionAndScale(draggedObject, objPosAndScale); // Figure out the object coords of the drag start point's screen coords. // All stretching should be around this point in object-coord-space. float scaleInv = (objPosAndScale.scale == 0.0f ? 1.0f : 1.0f / objPosAndScale.scale); objDraggedPointX = (currPt.getX() - objPosAndScale.xOff) * scaleInv; objDraggedPointY = (currPt.getY() - objPosAndScale.yOff) * scaleInv; // Figure out ratio between object scale factor and multitouch diameter (they are linearly correlated) float diam = currPt.getMultiTouchDiameter(); objStartScale = objPosAndScale.scale / (diam == 0.0f ? 1.0f : diam); } /** Drag/stretch the dragged object to the current touch position and diameter */ private void performDrag() { // Don't do anything if we're not dragging anything if (draggedObject == null) return; // Calc new position of dragged object float scale = (objPosAndScale.scale == 0.0f ? 1.0f : objPosAndScale.scale); float newObjPosX = currPt.getX() - objDraggedPointX * scale; float newObjPosY = currPt.getY() - objDraggedPointY * scale; // Get new drag diameter (avoiding divsion by zero), and calculate new drag scale float diam; if (!currPt.isMultiTouch) { // Single-touch, no change in scale diam = 1.0f; } else { diam = currPt.getMultiTouchDiameter(); if (diam < MIN_MULTITOUCH_SEPARATION) diam = MIN_MULTITOUCH_SEPARATION; } float newScale = diam * objStartScale; // Get the new obj coords and scale, and set them (notifying the subclass of the change) objPosAndScale.set(newObjPosX, newObjPosY, newScale); boolean success = objectCanvas.setPositionAndScale(draggedObject, objPosAndScale, currPt); if (!success) ; // If we could't set those params, do nothing currently } /** The main single-touch and multi-touch logic */ private void multiTouchController() { switch (dragMode) { case MODE_NOTHING: // Not doing anything currently if (currPt.isDown() && !currPt.isDownPrev()) { // Start a new single-point drag draggedObject = objectCanvas.getDraggableObjectAtPoint(currPt); if (draggedObject != null) { // Started a new single-point drag dragMode = MODE_DRAG; objectCanvas.selectObject(draggedObject, currPt); resetDrag(); // Don't need any settling time if just placing one finger, there is no noise dragStartTime = dragSettleTime = currPt.getEventTime(); } } break; case MODE_DRAG: // Currently in a single-point drag if (!currPt.isDown()) { // First finger was released, stop dragging dragMode = MODE_NOTHING; objectCanvas.selectObject((draggedObject = null), currPt); } else if (currPt.isMultiTouch()) { // Point 1 was already down and point 2 was just placed down dragMode = MODE_STRETCH; // Restart the drag with the new drag position (that is at the midpoint between the touchpoints) resetDrag(); // Need to let events settle before moving things, to help with event noise on touchdown dragStartTime = currPt.getEventTime(); dragSettleTime = dragStartTime + EVENT_SETTLE_TIME_INTERVAL; } else { // Point 1 is still down and point 2 did not change state, just do single-point drag to new location if (currPt.getEventTime() < dragSettleTime) { // Ignore the first few events if we just stopped stretching, because if finger 2 was kept down while // finger 1 is lifted, then point 1 gets mapped to finger 2. Restart the drag from the new position. resetDrag(); } else { // Keep dragging, move to new point performDrag(); } } break; case MODE_STRETCH: // Two-point stretch if (!currPt.isMultiTouch() || !currPt.isDown()) { // Dropped one or both points, stop stretching if (!currPt.isDown()) { // Dropped both points, go back to doing nothing dragMode = MODE_NOTHING; objectCanvas.selectObject((draggedObject = null), currPt); } else { // Just dropped point 2, downgrade to a single-point drag dragMode = MODE_DRAG; // Restart the drag with the single-finger position resetDrag(); // Ignore the first few events after the drop, in case we dropped finger 1 and left finger 2 down dragStartTime = currPt.getEventTime(); dragSettleTime = dragStartTime + EVENT_SETTLE_TIME_INTERVAL; } } else { // Keep stretching if (Math.abs(currPt.getX() - prevPt.getX()) > MAX_MULTITOUCH_POS_JUMP_SIZE || Math.abs(currPt.getY() - prevPt.getY()) > MAX_MULTITOUCH_POS_JUMP_SIZE || Math.abs(currPt.getMultiTouchWidth() - prevPt.getMultiTouchWidth()) * .5f > MAX_MULTITOUCH_DIM_JUMP_SIZE || Math.abs(currPt.getMultiTouchHeight() - prevPt.getMultiTouchHeight()) * .5f > MAX_MULTITOUCH_DIM_JUMP_SIZE) { // Jumped too far, probably event noise, reset and ignore events for a bit resetDrag(); dragStartTime = currPt.getEventTime(); dragSettleTime = dragStartTime + EVENT_SETTLE_TIME_INTERVAL; } else if (currPt.eventTime < dragSettleTime) { // Events have not yet settled, reset resetDrag(); } else { // Stretch to new position and size performDrag(); } } break; } } // ------------------------------------------------------------------------------------ /** A class that packages up all MotionEvent information with all derived multitouch information (if available) */ public static class PointInfo { private float x, y, dx, dy, size, diameter, diameterSq, angle, pressure; private boolean down, downPrev, isMultiTouch, diameterSqIsCalculated, diameterIsCalculated, angleIsCalculated; private int action; private long eventTime; // -- private Resources res; private int displayWidth, displayHeight; // -- public PointInfo(Resources res) { this.res = res; DisplayMetrics metrics = res.getDisplayMetrics(); this.displayWidth = metrics.widthPixels; this.displayHeight = metrics.heightPixels; } // -- public PointInfo(PointInfo other) { this.set(other); } /** Copy all fields */ public void set(PointInfo other) { this.displayWidth = other.displayWidth; this.displayHeight = other.displayHeight; this.x = other.x; this.y = other.y; this.dx = other.dx; this.dy = other.dy; this.size = other.size; this.diameter = other.diameter; this.diameterSq = other.diameterSq; this.angle = other.angle; this.pressure = other.pressure; this.down = other.down; this.action = other.action; this.downPrev = other.downPrev; this.isMultiTouch = other.isMultiTouch; this.diameterIsCalculated = other.diameterIsCalculated; this.diameterSqIsCalculated = other.diameterSqIsCalculated; this.angleIsCalculated = other.angleIsCalculated; this.eventTime = other.eventTime; } /** Setter for use in event decoding */ private void set(float x, float y, float pressure, float undecodedSize, boolean down, int action, long eventTime) { this.x = x; this.y = y; this.pressure = pressure; this.downPrev = this.down; this.down = down; this.action = action; this.eventTime = eventTime; // 1.0f is the max value for size on an unpatched kernel -- make this backwards-compatible this.isMultiTouch = (undecodedSize > 1.0f); if (isMultiTouch) { // Multitouch event, decipher size field, which contains dx and dy // dx and dy come packaged in a 12-pt fixed-point number that has been converted into a float int szi = (int) undecodedSize; int dxi = szi >> 12; int dyi = szi & ((1 << 12) - 1); // Get display size and rotation, scale fixed point to screen coords dx = Math.min(displayWidth, displayHeight) * dxi / (float) ((1 << 12) - 1); dy = Math.max(displayWidth, displayHeight) * dyi / (float) ((1 << 12) - 1); if (screenIsRotated()) { // Swap x,y if screen is rotated float tmp = dx; dx = dy; dy = tmp; } } else { // Single-touch event dx = dy = diameter = 0.0f; // undecodedSize == 0 for single-touch when the multitouch kernel patch is applied this.size = undecodedSize; } // Need to re-calculate the expensive params if they're needed diameterSqIsCalculated = diameterIsCalculated = angleIsCalculated = false; } private boolean screenIsRotated() { return res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; } // Fast integer sqrt, by Jim Ulery. Should be faster than Math.sqrt() private int julery_isqrt(int val) { int temp, g = 0, b = 0x8000, bshft = 15; do { if (val >= (temp = (((g << 1) + B) << bshft--))) { g += b; val -= temp; } } while ((b >>= 1) > 0); return g; } /** Calculate the squared diameter of the multitouch event, and cache it. Use this if you don't need to perform the sqrt. */ public float getMultiTouchDiameterSq() { if (!diameterSqIsCalculated) { diameterSq = (isMultiTouch ? dx * dx + dy * dy : 0.0f); diameterSqIsCalculated = true; } return diameterSq; } /** Calculate the diameter of the multitouch event, and cache it. Uses fast int sqrt but gives accuracy to 1/16px. */ public float getMultiTouchDiameter() { if (!diameterIsCalculated) { // Get 1/16 pixel's worth of subpixel accuracy, works on screens up to 2048x2048 // before we get overflow (at which point you can reduce or eliminate subpix // accuracy, or use longs in julery_isqrt()) float diamSq = getMultiTouchDiameterSq(); diameter = (diamSq == 0.0f ? 0.0f : (float) julery_isqrt((int) (256 * diamSq)) / 16.0f); // Make sure diameter is never less than dx or dy, for trig purposes if (diameter < dx) diameter = dx; if (diameter < dy) diameter = dy; diameterIsCalculated = true; } return diameter; } /** * Calculate the angle of a multitouch event, and cache it. Actually gives the smaller of the two angles between the x axis * and the line between the two touchpoints, so range is [0,Math.PI/2]. Uses Math.atan2(). */ public float getMultiTouchAngle() { if (!angleIsCalculated) { angle = (float) Math.atan2(dy, dx); angleIsCalculated = true; } return angle; } public float getX() { return x; } public float getY() { return y; } public float getMultiTouchWidth() { return dx; } public float getMultiTouchHeight() { return dy; } public float getSize() { return size; } public float getPressure() { return pressure; } public boolean isDown() { return down; } public boolean isDownPrev() { return downPrev; } public int getAction() { return action; } public boolean isMultiTouch() { return isMultiTouch; } public long getEventTime() { return eventTime; } } // ------------------------------------------------------------------------------------ /** A class that is used to store scroll offsets and scale information for objects that are managed by the multitouch controller */ public static class PositionAndScale { private float xOff, yOff, scale; public PositionAndScale() { } public void set(float xOff, float yOff, float scale) { this.xOff = xOff; this.yOff = yOff; this.scale = scale; } public float getXOff() { return xOff; } public float getYOff() { return yOff; } public float getScale() { return scale; } } // ------------------------------------------------------------------------------------ public static interface MultiTouchObjectCanvas<T> { /** See if there is a draggable object at the current point. Returns the object at the point, or null if nothing to drag. */ public T getDraggableObjectAtPoint(PointInfo pt); /** * Get the screen coords of the dragged object's origin, and scale multiplier to convert screen coords to obj coords. Call * the .set() method on the passed PositionAndScale object. */ public void getPositionAndScale(T obj, PositionAndScale objPosAndScaleOut); /** * Set the position and scale of the dragged object, in object coords. Return true for success, or false if those parameters * are out of range. */ public boolean setPositionAndScale(T obj, PositionAndScale newObjPosAndScale, PointInfo touchPoint); /** * Select an object at the given point. Can be used to bring the object to top etc. Only called when first touchpoint goes * down, not when multitouch is initiated. Also called with null when drag op stops. */ public void selectObject(T obj, PointInfo pt); } } [/codebox] EDIT2: And the Final Product. The patched KeyInputQueue.java [codebox] /* * Copyright © 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 * * [url="http://www.apache.org/licenses/LICENSE-2.0"]http://www.apache.org/licenses/LICENSE-2.0[/url] * * 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.os.SystemClock; import android.os.PowerManager; import android.util.Log; import android.util.SparseArray; import android.view.Display; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.RawInputEvent; import android.view.Surface; import android.view.WindowManagerPolicy; public abstract class KeyInputQueue { static final String TAG = "KeyInputQueue"; SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>(); int mGlobalMetaState = 0; boolean mHaveGlobalMetaState = false; final QueuedEvent mFirst; final QueuedEvent mLast; QueuedEvent mCache; int mCacheCount; Display mDisplay = null; int mOrientation = Surface.ROTATION_0; int[] mKeyRotationMap = 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; public interface FilterCallback { int filterEvent(QueuedEvent ev); } static class QueuedEvent { InputDevice inputDevice; long when; 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.when = that.when; 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; } KeyInputQueue(Context context) { 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; mThread.start(); } public void setDisplay(Display display) { mDisplay = display; } 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; //Log.i("foo", "***** HAVE TOUCHSCREEN!"); } if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) { config.keyboard = Configuration.KEYBOARD_QWERTY; //Log.i("foo", "***** HAVE QWERTY!"); } if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) { config.navigation = Configuration.NAVIGATION_TRACKBALL; //Log.i("foo", "***** HAVE TRACKBALL!"); } } } } } public static native String getDeviceName(int deviceId); public static native int getDeviceClasses(int deviceId); 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 getScancodeState(int sw); public static native int getScancodeState(int deviceId, int sw); public static native int getKeycodeState(int sw); public static native int getKeycodeState(int deviceId, int sw); 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() { android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY); try { RawInputEvent ev = new RawInputEvent(); while (true) { InputDevice di; // block, doesn't release the monitor readEvent(ev); boolean send = false; boolean configChanged = false; if (false) { Log.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); mDevices.put(ev.deviceId, di); configChanged = true; } } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) { synchronized (mFirst) { Log.i(TAG, "Device removed: id=0x" + Integer.toHexString(ev.deviceId)); di = mDevices.get(ev.deviceId); if (di != null) { mDevices.delete(ev.deviceId); configChanged = true; } else { Log.w(TAG, "Bad device id: " + ev.deviceId); } } } else { di = getInputDevice(ev.deviceId); // 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 (di == null) { continue; } if (configChanged) { synchronized (mFirst) { addLocked(di, SystemClock.uptimeMillis(), 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(); //Log.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.mDownTime = curTime; } else { down = false; } int keycode = rotateKeyCodeLocked(ev.keycode); addLocked(di, curTime, ev.flags, RawInputEvent.CLASS_KEYBOARD, newKeyEvent(di, di.mDownTime, curTime, down, keycode, 0, scancode, ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0) ? KeyEvent.FLAG_WOKE_HERE : 0)); } else if (ev.type == RawInputEvent.EV_KEY) { if (ev.scancode == RawInputEvent.BTN_TOUCH && (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) { di.mAbs.changed = true; di.mAbs.down = ev.value != 0; } if (ev.scancode == RawInputEvent.BTN_MOUSE && (classes&RawInputEvent.CLASS_TRACKBALL) != 0) { di.mRel.changed = true; di.mRel.down = ev.value != 0; send = true; } } else if (ev.type == RawInputEvent.EV_ABS && (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) { if (ev.scancode == RawInputEvent.ABS_X) { di.mAbs.changed = true; di.mAbs.x = ev.value; } else if (ev.scancode == RawInputEvent.ABS_Y) { di.mAbs.changed = true; di.mAbs.y = ev.value; } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) { di.mAbs.changed = true; di.mAbs.pressure = ev.value; } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) { di.mAbs.changed = true; di.mAbs.size = ev.value; } } 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.x += ev.value; } else if (ev.scancode == RawInputEvent.REL_Y) { di.mRel.changed = true; di.mRel.y += ev.value; } } if (send || ev.type == RawInputEvent.EV_SYN) { if (mDisplay != null) { if (!mHaveGlobalMetaState) { computeGlobalMetaStateLocked(); } MotionEvent me; me = di.mAbs.generateMotion(di, curTime, true, mDisplay, mOrientation, mGlobalMetaState); if (false) Log.v(TAG, "Absolute: x=" + di.mAbs.x + " y=" + di.mAbs.y + " ev=" + me); if (me != null) { if (WindowManagerPolicy.WATCH_POINTER) { Log.i(TAG, "Enqueueing: " + me); } addLocked(di, curTime, ev.flags, RawInputEvent.CLASS_TOUCHSCREEN, me); } me = di.mRel.generateMotion(di, curTime, false, mDisplay, mOrientation, mGlobalMetaState); if (false) Log.v(TAG, "Relative: x=" + di.mRel.x + " y=" + di.mRel.y + " ev=" + me); if (me != null) { addLocked(di, curTime, ev.flags, RawInputEvent.CLASS_TRACKBALL, me); } } } } } } catch (RuntimeException exc) { Log.e(TAG, "InputReaderThread uncaught exception", exc); } } }; /** * 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; } } void recycleEvent(QueuedEvent ev) { synchronized (mFirst) { //Log.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) Log.i(TAG, "Detach rel " + ev.event); ev.inputDevice.mRel.currentMove = null; ev.inputDevice.mRel.x = 0; ev.inputDevice.mRel.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 when, 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.when = when; 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 when, int flags, int classType, Object event) { boolean poke = mFirst.next == mLast; QueuedEvent ev = obtainLocked(device, when, flags, classType, event); QueuedEvent p = mLast.prev; while (p != mFirst && ev.when < p.when) { p = p.prev; } ev.next = p.next; ev.prev = p; p.next = ev; ev.next.prev = ev; ev.inQueue = true; if (poke) { mFirst.notify(); mWakeLock.acquire(); } } private InputDevice newInputDevice(int deviceId) { int classes = getDeviceClasses(deviceId); String name = getDeviceName(deviceId); Log.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId) + ", name=" + name + ", classes=" + Integer.toHexString(classes)); InputDevice.AbsoluteInfo absX; InputDevice.AbsoluteInfo absY; InputDevice.AbsoluteInfo absPressure; InputDevice.AbsoluteInfo absSize; 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"); } else { absX = null; absY = null; absPressure = null; absSize = null; } 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) { Log.i(TAG, " " + name + ": min=" + info.minValue + " max=" + info.maxValue + " flat=" + info.flat + " fuzz=" + info.fuzz); info.range = info.maxValue-info.minValue; return info; } Log.i(TAG, " " + name + ": unknown values"); return null; } private static native boolean readEvent(RawInputEvent outEvent); } syntax highlighted by Code2HTML, v. 0.9.1 Just put it in place: ANDROID_HOME/frameworks/base/services/java/com/android/server/ And recompile the Android. That's all we need. Maybe this isn't exact what we need, because this file is from G1, But at least we get some point. KeyInputQueue.java Best regards TOTI This is All he need. So don't rush for nothing. Just please be patient, and wait not for long. I'm sure that he'll do it in no time. Edited September 26, 2010 by totiadrenalin
Guest DanWilson Posted September 26, 2010 Report Posted September 26, 2010 No! Only the Patched Kernel. Because This isn't really multitouch screen, But a singletouch with second finger gesture. So maybe it recognize up to 3 fingers, but it's still singletouch. Because It only gesture the second finger. That's why you can move the screen of the launch app with 3 fingers. But without the patched drivers there's no chance do provide second finger gesture. So don't rush, and let BB correct the kernel, and ad what I post in my previews post. You will get then a real second finger gesture. A Dual touch. This is all I really want. But I thought for anything other than one finger in the corner, other finger moving needed the custom drivers / firmware. Or have I missed it?
Guest totiadrenalin Posted September 26, 2010 Report Posted September 26, 2010 This is all I really want. But I thought for anything other than one finger in the corner, other finger moving needed the custom drivers / firmware. Or have I missed it? Maybe you are right? But what if I flash the touchcontroller? I didn't get what I tough that I should get.
Guest thesinger Posted September 26, 2010 Report Posted September 26, 2010 No! Only the Patched Kernel. Because This isn't really multitouch screen, But a singletouch with second finger gesture. So maybe it recognize up to 3 fingers, but it's still singletouch. Because It only gesture the second finger. That's why you can move the screen of the launch app with 3 fingers. But without the patched drivers there's no chance do provide second finger gesture. So don't rush, and let BB correct the kernel, and ad what I post in my previews post. You will get then a real second finger gesture. A Dual touch. Okey so then we are going to let BB finish the kernel and try it out.Good work guys. :lol:
Guest totiadrenalin Posted September 26, 2010 Report Posted September 26, 2010 I don't now i'm not a pro like bb and toti but i think we need a android version that is built in multitouch like the one in the htc hero. HEHE!! I'm not a pro programer two. This is my hobby, Like several others. I'm graduated Guitarist. - a Musician. Sorry that I disappoint you, but I just want to tell the truth. Best regards TOTI
Guest totiadrenalin Posted September 26, 2010 Report Posted September 26, 2010 Sorry, but a little off topic.
Recommended Posts
Please sign in to comment
You will be able to leave a comment after signing in
Sign In Now