--- /dev/null
+/*\r
+ * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com)\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the\r
+ * License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"\r
+ * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language\r
+ * governing permissions and limitations under the License.\r
+ */\r
+\r
+package com.badlogic.gdx.backends.android;\r
+\r
+import java.util.ArrayList;\r
+import java.util.HashSet;\r
+\r
+import android.app.AlertDialog;\r
+import android.content.Context;\r
+import android.content.DialogInterface;\r
+import android.hardware.Sensor;\r
+import android.hardware.SensorEvent;\r
+import android.hardware.SensorEventListener;\r
+import android.hardware.SensorManager;\r
+import android.os.Handler;\r
+import android.util.Log;\r
+import android.view.MotionEvent;\r
+import android.view.View;\r
+import android.view.View.OnKeyListener;\r
+import android.view.View.OnTouchListener;\r
+import android.view.inputmethod.InputMethodManager;\r
+import android.widget.EditText;\r
+\r
+import com.badlogic.gdx.Input;\r
+import com.badlogic.gdx.InputProcessor;\r
+import com.badlogic.gdx.utils.AtomicQueue;\r
+import com.badlogic.gdx.utils.Pool;\r
+import com.badlogic.gdx.utils.Pool.PoolObjectFactory;\r
+\r
+/**\r
+ * An implementation of the {@link Input} interface for Android.\r
+ * \r
+ * @author mzechner\r
+ * \r
+ */\r
+public final class AndroidLocklessInput implements Input, OnKeyListener, OnTouchListener, SensorEventListener {\r
+ static class KeyEvent {\r
+ static final int KEY_DOWN = 0;\r
+ static final int KEY_UP = 1;\r
+ static final int KEY_TYPED = 2;\r
+\r
+ long timeStamp;\r
+ int type;\r
+ int keyCode;\r
+ char keyChar;\r
+ }\r
+\r
+ static class TouchEvent {\r
+ static final int TOUCH_DOWN = 0;\r
+ static final int TOUCH_UP = 1;\r
+ static final int TOUCH_DRAGGED = 2;\r
+\r
+ long timeStamp;\r
+ int type;\r
+ int x;\r
+ int y;\r
+ int pointer;\r
+ }\r
+\r
+ AtomicQueue<KeyEvent> keyEvents = new AtomicQueue<KeyEvent>();\r
+ AtomicQueue<KeyEvent> freeKeyEvents = new AtomicQueue<KeyEvent>();\r
+\r
+ AtomicQueue<TouchEvent> touchEvents = new AtomicQueue<TouchEvent>();\r
+ AtomicQueue<TouchEvent> freeTouchEvents = new AtomicQueue<TouchEvent>();\r
+\r
+ int[] touchX = new int[10];\r
+ int[] touchY = new int[10];\r
+ boolean[] touched = new boolean[10];\r
+ final boolean hasMultitouch;\r
+ private HashSet<Integer> keys = new HashSet<Integer>();\r
+ private SensorManager manager;\r
+ public boolean accelerometerAvailable = false;\r
+ private final float[] accelerometerValues = new float[3];\r
+ private String text = null;\r
+ private TextInputListener textListener = null;\r
+ private Handler handle;\r
+ final AndroidApplication app;\r
+ private final AndroidLocklessTouchHandler touchHandler;\r
+ private int sleepTime = 0;\r
+ private boolean catchBack = false;\r
+\r
+ private InputProcessor processor;\r
+\r
+ public AndroidLocklessInput (AndroidApplication activity, View view, int sleepTime) {\r
+ view.setOnKeyListener(this);\r
+ view.setOnTouchListener(this);\r
+ view.setFocusable(true);\r
+ view.setFocusableInTouchMode(true);\r
+ view.requestFocus();\r
+ view.requestFocusFromTouch();\r
+\r
+ manager = (SensorManager)activity.getSystemService(Context.SENSOR_SERVICE);\r
+ if (manager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() == 0) {\r
+ accelerometerAvailable = false;\r
+ } else {\r
+ Sensor accelerometer = manager.getSensorList(Sensor.TYPE_ACCELEROMETER).get(0);\r
+ if (!manager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_GAME))\r
+ accelerometerAvailable = false;\r
+ else\r
+ accelerometerAvailable = true;\r
+ }\r
+\r
+ handle = new Handler();\r
+ this.app = activity;\r
+ this.sleepTime = sleepTime;\r
+ int sdkVersion = Integer.parseInt(android.os.Build.VERSION.SDK);\r
+ if (sdkVersion >= 5)\r
+ touchHandler = new AndroidLocklessMultiTouchHandler();\r
+ else\r
+ touchHandler = new AndroidLocklessSingleTouchHandler();\r
+ hasMultitouch = touchHandler instanceof AndroidLocklessMultiTouchHandler\r
+ && ((AndroidLocklessMultiTouchHandler)touchHandler).supportsMultitouch(activity);\r
+ }\r
+\r
+ @Override public float getAccelerometerX () {\r
+ return accelerometerValues[0];\r
+ }\r
+\r
+ @Override public float getAccelerometerY () {\r
+ return accelerometerValues[1];\r
+ }\r
+\r
+ @Override public float getAccelerometerZ () {\r
+ return accelerometerValues[2];\r
+ }\r
+\r
+ @Override public void getTextInput (final TextInputListener listener, final String title, final String text) {\r
+ handle.post(new Runnable() {\r
+ public void run () {\r
+ AlertDialog.Builder alert = new AlertDialog.Builder(AndroidLocklessInput.this.app);\r
+ alert.setTitle(title);\r
+ final EditText input = new EditText(AndroidLocklessInput.this.app);\r
+ input.setText(text);\r
+ input.setSingleLine();\r
+ alert.setView(input);\r
+ alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() {\r
+ public void onClick (DialogInterface dialog, int whichButton) {\r
+ listener.input(input.getText().toString());\r
+ }\r
+ });\r
+ alert.show();\r
+ }\r
+ });\r
+ }\r
+\r
+ @Override public int getX () {\r
+ return touchX[0];\r
+ }\r
+\r
+ @Override public int getY () {\r
+ return touchY[0];\r
+ }\r
+\r
+ @Override public int getX (int pointer) {\r
+ return touchX[pointer];\r
+ }\r
+\r
+ @Override public int getY (int pointer) {\r
+ return touchY[pointer];\r
+ }\r
+\r
+ public boolean isTouched (int pointer) {\r
+ return touched[pointer];\r
+ }\r
+\r
+ @Override public boolean isAccelerometerAvailable () {\r
+ return accelerometerAvailable;\r
+ }\r
+\r
+ @Override public boolean isKeyPressed (int key) {\r
+ synchronized (this) {\r
+ if (key == Input.Keys.ANY_KEY)\r
+ return keys.size() > 0;\r
+ else\r
+ return keys.contains(key);\r
+ }\r
+ }\r
+\r
+ @Override public boolean isTouched () {\r
+ return touched[0];\r
+ }\r
+\r
+ public void setInputProcessor (InputProcessor processor) {\r
+ synchronized (this) {\r
+ this.processor = processor;\r
+ }\r
+ }\r
+\r
+ void processEvents () {\r
+ InputProcessor processor;\r
+ synchronized (this) {\r
+ processor = this.processor;\r
+ }\r
+\r
+ if (processor != null) {\r
+ KeyEvent e;\r
+ while ((e = keyEvents.poll()) != null) {\r
+ switch (e.type) {\r
+ case KeyEvent.KEY_DOWN:\r
+ processor.keyDown(e.keyCode);\r
+ break;\r
+ case KeyEvent.KEY_UP:\r
+ processor.keyUp(e.keyCode);\r
+ break;\r
+ case KeyEvent.KEY_TYPED:\r
+ processor.keyTyped(e.keyChar);\r
+ }\r
+ freeKeyEvents.put(e);\r
+ }\r
+\r
+ TouchEvent te = null;\r
+ while ((te = touchEvents.poll()) != null) {\r
+ switch (te.type) {\r
+ case TouchEvent.TOUCH_DOWN:\r
+ processor.touchDown(te.x, te.y, te.pointer);\r
+ break;\r
+ case TouchEvent.TOUCH_UP:\r
+ processor.touchUp(te.x, te.y, te.pointer);\r
+ break;\r
+ case TouchEvent.TOUCH_DRAGGED:\r
+ processor.touchDragged(te.x, te.y, te.pointer);\r
+ }\r
+ freeTouchEvents.put(te);\r
+ }\r
+ } else {\r
+ TouchEvent e = null;\r
+ while ((e = touchEvents.poll()) != null) {\r
+ Log.d("AndroidInput", "lockless touch: " + (System.nanoTime() - e.timeStamp) / 1000000.0f);\r
+ freeTouchEvents.put(e);\r
+ }\r
+\r
+ KeyEvent ke = null;\r
+ while ((ke = keyEvents.poll()) != null) {\r
+ freeKeyEvents.put(ke);\r
+ }\r
+ }\r
+ }\r
+\r
+ boolean requestFocus = true;\r
+\r
+ @Override public boolean onTouch (View view, MotionEvent event) {\r
+ if (requestFocus) {\r
+ view.requestFocus();\r
+ view.requestFocusFromTouch();\r
+ requestFocus = false;\r
+ }\r
+\r
+ touchHandler.onTouch(event, this);\r
+\r
+ if (sleepTime != 0) {\r
+ try {\r
+ Thread.sleep(sleepTime);\r
+ } catch (InterruptedException e) {\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+\r
+ @Override public boolean onKey (View v, int keyCode, android.view.KeyEvent e) {\r
+ char character = (char)e.getUnicodeChar();\r
+ // Android doesn't report a unicode char for back space. hrm...\r
+ if (keyCode == 67) character = '\b';\r
+\r
+ KeyEvent event = null;\r
+ switch (e.getAction()) {\r
+ case android.view.KeyEvent.ACTION_DOWN:\r
+ event = freeKeyEvents.poll();\r
+ if (event == null) event = new KeyEvent();\r
+ event.keyChar = 0;\r
+ event.keyCode = e.getKeyCode();\r
+ event.type = KeyEvent.KEY_DOWN;\r
+ keyEvents.put(event);\r
+ synchronized(this) {\r
+ keys.add(event.keyCode);\r
+ }\r
+ break;\r
+ case android.view.KeyEvent.ACTION_UP:\r
+ event = freeKeyEvents.poll();\r
+ if (event == null) event = new KeyEvent();\r
+ event.keyChar = 0;\r
+ event.keyCode = e.getKeyCode();\r
+ event.type = KeyEvent.KEY_UP;\r
+ keyEvents.put(event);\r
+\r
+ event = freeKeyEvents.poll();\r
+ if (event == null) event = new KeyEvent();\r
+ event.keyChar = character;\r
+ event.keyCode = 0;\r
+ event.type = KeyEvent.KEY_TYPED;\r
+ keyEvents.put(event);\r
+\r
+ synchronized(this) {\r
+ keys.remove(e.getKeyCode());\r
+ }\r
+ }\r
+\r
+ if (catchBack && keyCode == android.view.KeyEvent.KEYCODE_BACK) return true;\r
+ return false;\r
+ }\r
+\r
+ @Override public void onAccuracyChanged (Sensor arg0, int arg1) {\r
+\r
+ }\r
+\r
+ @Override public void onSensorChanged (SensorEvent event) {\r
+ if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {\r
+ System.arraycopy(event.values, 0, accelerometerValues, 0, accelerometerValues.length);\r
+ }\r
+ }\r
+\r
+ @Override public boolean supportsMultitouch () {\r
+ return hasMultitouch;\r
+ }\r
+\r
+ @Override public void setOnscreenKeyboardVisible (boolean visible) {\r
+ InputMethodManager manager = (InputMethodManager)app.getSystemService(Context.INPUT_METHOD_SERVICE);\r
+ if (visible) {\r
+ manager.showSoftInput(((AndroidGraphics)app.getGraphics()).getView(), 0);\r
+ } else {\r
+ manager.hideSoftInputFromWindow(((AndroidGraphics)app.getGraphics()).getView().getWindowToken(), 0);\r
+ }\r
+ }\r
+\r
+ @Override public boolean supportsOnscreenKeyboard () {\r
+ return true;\r
+ }\r
+\r
+ @Override public void setCatchBackKey (boolean catchBack) {\r
+ this.catchBack = catchBack;\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com)\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the\r
+ * License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"\r
+ * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language\r
+ * governing permissions and limitations under the License.\r
+ */\r
+\r
+package com.badlogic.gdx.backends.android;\r
+\r
+import android.view.MotionEvent;\r
+\r
+import com.badlogic.gdx.backends.android.AndroidLocklessInput.TouchEvent;\r
+\r
+\r
+/**\r
+ * Multitouch handler for devices running Android >= 2.0. If device is capable of (fake) multitouch this will report additional\r
+ * pointers.\r
+ * \r
+ * @author badlogicgames@gmail.com\r
+ * \r
+ */\r
+public class AndroidLocklessMultiTouchHandler implements AndroidLocklessTouchHandler {\r
+ public void onTouch (MotionEvent event, AndroidLocklessInput input) {\r
+ final int action = event.getAction() & MotionEvent.ACTION_MASK;\r
+ int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;\r
+ int pointerId = event.getPointerId(pointerIndex);\r
+\r
+ int x = 0, y = 0; \r
+\r
+ switch (action) {\r
+ case MotionEvent.ACTION_DOWN:\r
+ case MotionEvent.ACTION_POINTER_DOWN:\r
+ x = (int)event.getX(pointerIndex);\r
+ y = (int)event.getY(pointerIndex);\r
+ postTouchEvent(input, TouchEvent.TOUCH_DOWN, x, y, pointerId);\r
+ input.touchX[pointerId] = x;\r
+ input.touchY[pointerId] = y;\r
+ input.touched[pointerId] = true;\r
+ break;\r
+\r
+ case MotionEvent.ACTION_UP:\r
+ case MotionEvent.ACTION_POINTER_UP:\r
+ case MotionEvent.ACTION_OUTSIDE:\r
+ case MotionEvent.ACTION_CANCEL:\r
+ x = (int)event.getX(pointerIndex);\r
+ y = (int)event.getY(pointerIndex);\r
+ postTouchEvent(input, TouchEvent.TOUCH_UP, x, y, pointerId);\r
+ input.touchX[pointerId] = x;\r
+ input.touchY[pointerId] = y;\r
+ input.touched[pointerId] = false;\r
+ break;\r
+\r
+ case MotionEvent.ACTION_MOVE:\r
+ int pointerCount = event.getPointerCount();\r
+ for (int i = 0; i < pointerCount; i++) {\r
+ pointerIndex = i;\r
+ pointerId = event.getPointerId(pointerIndex);\r
+ x = (int)event.getX(pointerIndex);\r
+ y = (int)event.getY(pointerIndex);\r
+ postTouchEvent(input, TouchEvent.TOUCH_DRAGGED, x, y, pointerId);\r
+ input.touchX[pointerId] = x;\r
+ input.touchY[pointerId] = y;\r
+ }\r
+ break;\r
+ }\r
+ }\r
+\r
+ private void postTouchEvent (AndroidLocklessInput input, int type, int x, int y, int pointer) {\r
+ TouchEvent event = input.freeTouchEvents.poll();\r
+ if(event == null)\r
+ event = new TouchEvent();\r
+ event.timeStamp = System.nanoTime();\r
+ event.pointer = pointer;\r
+ event.x = x;\r
+ event.y = y;\r
+ event.type = type; \r
+ input.touchEvents.put(event); \r
+ }\r
+\r
+ public boolean supportsMultitouch (AndroidApplication activity) {\r
+ return activity.getPackageManager().hasSystemFeature("android.hardware.touchscreen.multitouch");\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com)\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the\r
+ * License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"\r
+ * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language\r
+ * governing permissions and limitations under the License.\r
+ */\r
+\r
+package com.badlogic.gdx.backends.android;\r
+\r
+import com.badlogic.gdx.backends.android.AndroidLocklessInput.TouchEvent;\r
+\r
+import android.view.MotionEvent;\r
+\r
+\r
+/**\r
+ * Single touch handler for devices running <= 1.6\r
+ * \r
+ * @author badlogicgames@gmail.com\r
+ * \r
+ */\r
+public class AndroidLocklessSingleTouchHandler implements AndroidLocklessTouchHandler {\r
+ public void onTouch (MotionEvent event, AndroidLocklessInput input) {\r
+ int x = (int)event.getX();\r
+ int y = (int)event.getY();\r
+ input.touchX[0] = x;\r
+ input.touchY[0] = y;\r
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {\r
+ postTouchEvent(input, TouchEvent.TOUCH_DOWN, x, y, 0);\r
+ input.touched[0] = true;\r
+ }\r
+\r
+ if (event.getAction() == MotionEvent.ACTION_MOVE) {\r
+ postTouchEvent(input, TouchEvent.TOUCH_DRAGGED, x, y, 0);\r
+ input.touched[0] = true;\r
+ }\r
+ if (event.getAction() == MotionEvent.ACTION_UP) {\r
+ postTouchEvent(input, TouchEvent.TOUCH_UP, x, y, 0);\r
+ input.touched[0] = false;\r
+ }\r
+\r
+ if (event.getAction() == MotionEvent.ACTION_CANCEL) {\r
+ postTouchEvent(input, TouchEvent.TOUCH_UP, x, y, 0);\r
+ input.touched[0] = false;\r
+ }\r
+ }\r
+\r
+ private void postTouchEvent (AndroidLocklessInput input, int type, int x, int y, int pointer) { \r
+ TouchEvent event = input.freeTouchEvents.poll();\r
+ if(event == null)\r
+ event = new TouchEvent();\r
+ event.timeStamp = System.nanoTime();\r
+ event.pointer = 0;\r
+ event.x = x;\r
+ event.y = y;\r
+ event.type = type; \r
+ input.touchEvents.put(event); \r
+ }\r
+}\r