1 /******************************************************************************* 2 * Copyright 2011 See AUTHORS file. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 ******************************************************************************/ 16 17 package com.badlogic.gdx.backends.android; 18 19 import android.content.Context; 20 import android.view.MotionEvent; 21 22 import com.badlogic.gdx.Gdx; 23 import com.badlogic.gdx.Input.Buttons; 24 import com.badlogic.gdx.backends.android.AndroidInput.TouchEvent; 25 26 /** Multitouch handler for devices running Android >= 2.0. If device is capable of (fake) multitouch this will report additional 27 * pointers. 28 * 29 * @author badlogicgames (at) gmail.com */ 30 public class AndroidMultiTouchHandler implements AndroidTouchHandler { 31 public void onTouch (MotionEvent event, AndroidInput input) { 32 final int action = event.getAction() & MotionEvent.ACTION_MASK; 33 int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 34 int pointerId = event.getPointerId(pointerIndex); 35 36 int x = 0, y = 0; 37 int realPointerIndex = 0; 38 int button = Buttons.LEFT; 39 40 long timeStamp = System.nanoTime(); 41 synchronized (input) { 42 switch (action) { 43 case MotionEvent.ACTION_DOWN: 44 case MotionEvent.ACTION_POINTER_DOWN: 45 realPointerIndex = input.getFreePointerIndex(); // get a free pointer index as reported by Input.getX() etc. 46 if (realPointerIndex >= AndroidInput.NUM_TOUCHES) break; 47 input.realId[realPointerIndex] = pointerId; 48 x = (int)event.getX(pointerIndex); 49 y = (int)event.getY(pointerIndex); 50 if (android.os.Build.VERSION.SDK_INT >= 14) button = toGdxButton(event.getButtonState()); 51 if (button != -1) postTouchEvent(input, TouchEvent.TOUCH_DOWN, x, y, realPointerIndex, button, timeStamp); 52 input.touchX[realPointerIndex] = x; 53 input.touchY[realPointerIndex] = y; 54 input.deltaX[realPointerIndex] = 0; 55 input.deltaY[realPointerIndex] = 0; 56 input.touched[realPointerIndex] = (button != -1); 57 input.button[realPointerIndex] = button; 58 break; 59 60 case MotionEvent.ACTION_UP: 61 case MotionEvent.ACTION_POINTER_UP: 62 case MotionEvent.ACTION_OUTSIDE: 63 case MotionEvent.ACTION_CANCEL: 64 realPointerIndex = input.lookUpPointerIndex(pointerId); 65 if (realPointerIndex == -1) break; 66 if (realPointerIndex >= AndroidInput.NUM_TOUCHES) break; 67 input.realId[realPointerIndex] = -1; 68 x = (int)event.getX(pointerIndex); 69 y = (int)event.getY(pointerIndex); 70 button = input.button[realPointerIndex]; 71 if (button != -1) postTouchEvent(input, TouchEvent.TOUCH_UP, x, y, realPointerIndex, button, timeStamp); 72 input.touchX[realPointerIndex] = x; 73 input.touchY[realPointerIndex] = y; 74 input.deltaX[realPointerIndex] = 0; 75 input.deltaY[realPointerIndex] = 0; 76 input.touched[realPointerIndex] = false; 77 input.button[realPointerIndex] = 0; 78 break; 79 80 case MotionEvent.ACTION_MOVE: 81 int pointerCount = event.getPointerCount(); 82 for (int i = 0; i < pointerCount; i++) { 83 pointerIndex = i; 84 pointerId = event.getPointerId(pointerIndex); 85 x = (int)event.getX(pointerIndex); 86 y = (int)event.getY(pointerIndex); 87 realPointerIndex = input.lookUpPointerIndex(pointerId); 88 if (realPointerIndex == -1) continue; 89 if (realPointerIndex >= AndroidInput.NUM_TOUCHES) break; 90 button = input.button[realPointerIndex]; 91 if (button != -1) 92 postTouchEvent(input, TouchEvent.TOUCH_DRAGGED, x, y, realPointerIndex, button, timeStamp); 93 else 94 postTouchEvent(input, TouchEvent.TOUCH_MOVED, x, y, realPointerIndex, 0, timeStamp); 95 input.deltaX[realPointerIndex] = x - input.touchX[realPointerIndex]; 96 input.deltaY[realPointerIndex] = y - input.touchY[realPointerIndex]; 97 input.touchX[realPointerIndex] = x; 98 input.touchY[realPointerIndex] = y; 99 } 100 break; 101 } 102 } 103 Gdx.app.getGraphics().requestRendering(); 104 } 105 106 private void logAction (int action, int pointer) { 107 String actionStr = ""; 108 if (action == MotionEvent.ACTION_DOWN) 109 actionStr = "DOWN"; 110 else if (action == MotionEvent.ACTION_POINTER_DOWN) 111 actionStr = "POINTER DOWN"; 112 else if (action == MotionEvent.ACTION_UP) 113 actionStr = "UP"; 114 else if (action == MotionEvent.ACTION_POINTER_UP) 115 actionStr = "POINTER UP"; 116 else if (action == MotionEvent.ACTION_OUTSIDE) 117 actionStr = "OUTSIDE"; 118 else if (action == MotionEvent.ACTION_CANCEL) 119 actionStr = "CANCEL"; 120 else if (action == MotionEvent.ACTION_MOVE) 121 actionStr = "MOVE"; 122 else 123 actionStr = "UNKNOWN (" + action + ")"; 124 Gdx.app.log("AndroidMultiTouchHandler", "action " + actionStr + ", Android pointer id: " + pointer); 125 } 126 127 private int toGdxButton (int button) { 128 if (button == 0 || button == 1) return Buttons.LEFT; 129 if (button == 2) return Buttons.RIGHT; 130 if (button == 4) return Buttons.MIDDLE; 131 if (button == 8) return Buttons.BACK; 132 if (button == 16) return Buttons.FORWARD; 133 return -1; 134 } 135 136 private void postTouchEvent (AndroidInput input, int type, int x, int y, int pointer, int button, long timeStamp) { 137 TouchEvent event = input.usedTouchEvents.obtain(); 138 event.timeStamp = timeStamp; 139 event.pointer = pointer; 140 event.x = x; 141 event.y = y; 142 event.type = type; 143 event.button = button; 144 input.touchEvents.add(event); 145 } 146 147 public boolean supportsMultitouch (Context activity) { 148 return activity.getPackageManager().hasSystemFeature("android.hardware.touchscreen.multitouch"); 149 } 150 } 151