summaryrefslogtreecommitdiffstats
path: root/platform/android/java/src/org/godotengine/godot/input
diff options
context:
space:
mode:
authorhondres <liu.gam3@gmail.com>2016-01-24 05:11:59 +0100
committerhondres <liu.gam3@gmail.com>2016-01-24 05:29:09 +0100
commite7c920fdbabd65a86864ec9610f895bee82f05ba (patch)
treed97aa6da8b4ff05a1164d0bdeadff4c83b3be1cd /platform/android/java/src/org/godotengine/godot/input
parent6c27df8df609337867c108c5adb66174393e190b (diff)
downloadredot-engine-e7c920fdbabd65a86864ec9610f895bee82f05ba.tar.gz
support gamepad remapping on android
Diffstat (limited to 'platform/android/java/src/org/godotengine/godot/input')
-rw-r--r--platform/android/java/src/org/godotengine/godot/input/InputManagerCompat.java140
-rw-r--r--platform/android/java/src/org/godotengine/godot/input/InputManagerV16.java107
-rw-r--r--platform/android/java/src/org/godotengine/godot/input/InputManagerV9.java211
3 files changed, 458 insertions, 0 deletions
diff --git a/platform/android/java/src/org/godotengine/godot/input/InputManagerCompat.java b/platform/android/java/src/org/godotengine/godot/input/InputManagerCompat.java
new file mode 100644
index 0000000000..4615d2fbb5
--- /dev/null
+++ b/platform/android/java/src/org/godotengine/godot/input/InputManagerCompat.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.godotengine.godot.input;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.Handler;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+
+public interface InputManagerCompat {
+ /**
+ * Gets information about the input device with the specified id.
+ *
+ * @param id The device id
+ * @return The input device or null if not found
+ */
+ public InputDevice getInputDevice(int id);
+
+ /**
+ * Gets the ids of all input devices in the system.
+ *
+ * @return The input device ids.
+ */
+ public int[] getInputDeviceIds();
+
+ /**
+ * Registers an input device listener to receive notifications about when
+ * input devices are added, removed or changed.
+ *
+ * @param listener The listener to register.
+ * @param handler The handler on which the listener should be invoked, or
+ * null if the listener should be invoked on the calling thread's
+ * looper.
+ */
+ public void registerInputDeviceListener(InputManagerCompat.InputDeviceListener listener,
+ Handler handler);
+
+ /**
+ * Unregisters an input device listener.
+ *
+ * @param listener The listener to unregister.
+ */
+ public void unregisterInputDeviceListener(InputManagerCompat.InputDeviceListener listener);
+
+ /*
+ * The following three calls are to simulate V16 behavior on pre-Jellybean
+ * devices. If you don't call them, your callback will never be called
+ * pre-API 16.
+ */
+
+ /**
+ * Pass the motion events to the InputManagerCompat. This is used to
+ * optimize for polling for controllers. If you do not pass these events in,
+ * polling will cause regular object creation.
+ *
+ * @param event the motion event from the app
+ */
+ public void onGenericMotionEvent(MotionEvent event);
+
+ /**
+ * Tell the V9 input manager that it should stop polling for disconnected
+ * devices. You can call this during onPause in your activity, although you
+ * might want to call it whenever your game is not active (or whenever you
+ * don't care about being notified of new input devices)
+ */
+ public void onPause();
+
+ /**
+ * Tell the V9 input manager that it should start polling for disconnected
+ * devices. You can call this during onResume in your activity, although you
+ * might want to call it less often (only when the gameplay is actually
+ * active)
+ */
+ public void onResume();
+
+ public interface InputDeviceListener {
+ /**
+ * Called whenever the input manager detects that a device has been
+ * added. This will only be called in the V9 version when a motion event
+ * is detected.
+ *
+ * @param deviceId The id of the input device that was added.
+ */
+ void onInputDeviceAdded(int deviceId);
+
+ /**
+ * Called whenever the properties of an input device have changed since
+ * they were last queried. This will not be called for the V9 version of
+ * the API.
+ *
+ * @param deviceId The id of the input device that changed.
+ */
+ void onInputDeviceChanged(int deviceId);
+
+ /**
+ * Called whenever the input manager detects that a device has been
+ * removed. For the V9 version, this can take some time depending on the
+ * poll rate.
+ *
+ * @param deviceId The id of the input device that was removed.
+ */
+ void onInputDeviceRemoved(int deviceId);
+ }
+
+ /**
+ * Use this to construct a compatible InputManager.
+ */
+ public static class Factory {
+
+ /**
+ * Constructs and returns a compatible InputManger
+ *
+ * @param context the Context that will be used to get the system
+ * service from
+ * @return a compatible implementation of InputManager
+ */
+ public static InputManagerCompat getInputManager(Context context) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ return new InputManagerV16(context);
+ } else {
+ return new InputManagerV9();
+ }
+ }
+ }
+}
diff --git a/platform/android/java/src/org/godotengine/godot/input/InputManagerV16.java b/platform/android/java/src/org/godotengine/godot/input/InputManagerV16.java
new file mode 100644
index 0000000000..f05701f455
--- /dev/null
+++ b/platform/android/java/src/org/godotengine/godot/input/InputManagerV16.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.godotengine.godot.input;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.hardware.input.InputManager;
+import android.os.Build;
+import android.os.Handler;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+public class InputManagerV16 implements InputManagerCompat {
+
+ private final InputManager mInputManager;
+ private final Map<InputManagerCompat.InputDeviceListener, V16InputDeviceListener> mListeners;
+
+ public InputManagerV16(Context context) {
+ mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
+ mListeners = new HashMap<InputManagerCompat.InputDeviceListener, V16InputDeviceListener>();
+ }
+
+ @Override
+ public InputDevice getInputDevice(int id) {
+ return mInputManager.getInputDevice(id);
+ }
+
+ @Override
+ public int[] getInputDeviceIds() {
+ return mInputManager.getInputDeviceIds();
+ }
+
+ static class V16InputDeviceListener implements InputManager.InputDeviceListener {
+ final InputManagerCompat.InputDeviceListener mIDL;
+
+ public V16InputDeviceListener(InputDeviceListener idl) {
+ mIDL = idl;
+ }
+
+ @Override
+ public void onInputDeviceAdded(int deviceId) {
+ mIDL.onInputDeviceAdded(deviceId);
+ }
+
+ @Override
+ public void onInputDeviceChanged(int deviceId) {
+ mIDL.onInputDeviceChanged(deviceId);
+ }
+
+ @Override
+ public void onInputDeviceRemoved(int deviceId) {
+ mIDL.onInputDeviceRemoved(deviceId);
+ }
+
+ }
+
+ @Override
+ public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) {
+ V16InputDeviceListener v16Listener = new V16InputDeviceListener(listener);
+ mInputManager.registerInputDeviceListener(v16Listener, handler);
+ mListeners.put(listener, v16Listener);
+ }
+
+ @Override
+ public void unregisterInputDeviceListener(InputDeviceListener listener) {
+ V16InputDeviceListener curListener = mListeners.remove(listener);
+ if (null != curListener)
+ {
+ mInputManager.unregisterInputDeviceListener(curListener);
+ }
+
+ }
+
+ @Override
+ public void onGenericMotionEvent(MotionEvent event) {
+ // unused in V16
+ }
+
+ @Override
+ public void onPause() {
+ // unused in V16
+ }
+
+ @Override
+ public void onResume() {
+ // unused in V16
+ }
+
+}
diff --git a/platform/android/java/src/org/godotengine/godot/input/InputManagerV9.java b/platform/android/java/src/org/godotengine/godot/input/InputManagerV9.java
new file mode 100644
index 0000000000..0334c00997
--- /dev/null
+++ b/platform/android/java/src/org/godotengine/godot/input/InputManagerV9.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.godotengine.godot.input;
+
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayDeque;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Queue;
+
+public class InputManagerV9 implements InputManagerCompat {
+ private static final String LOG_TAG = "InputManagerV9";
+ private static final int MESSAGE_TEST_FOR_DISCONNECT = 101;
+ private static final long CHECK_ELAPSED_TIME = 3000L;
+
+ private static final int ON_DEVICE_ADDED = 0;
+ private static final int ON_DEVICE_CHANGED = 1;
+ private static final int ON_DEVICE_REMOVED = 2;
+
+ private final SparseArray<long[]> mDevices;
+ private final Map<InputDeviceListener, Handler> mListeners;
+ private final Handler mDefaultHandler;
+
+ private static class PollingMessageHandler extends Handler {
+ private final WeakReference<InputManagerV9> mInputManager;
+
+ PollingMessageHandler(InputManagerV9 im) {
+ mInputManager = new WeakReference<InputManagerV9>(im);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+ switch (msg.what) {
+ case MESSAGE_TEST_FOR_DISCONNECT:
+ InputManagerV9 imv = mInputManager.get();
+ if (null != imv) {
+ long time = SystemClock.elapsedRealtime();
+ int size = imv.mDevices.size();
+ for (int i = 0; i < size; i++) {
+ long[] lastContact = imv.mDevices.valueAt(i);
+ if (null != lastContact) {
+ if (time - lastContact[0] > CHECK_ELAPSED_TIME) {
+ // check to see if the device has been
+ // disconnected
+ int id = imv.mDevices.keyAt(i);
+ if (null == InputDevice.getDevice(id)) {
+ // disconnected!
+ imv.notifyListeners(ON_DEVICE_REMOVED, id);
+ imv.mDevices.remove(id);
+ } else {
+ lastContact[0] = time;
+ }
+ }
+ }
+ }
+ sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT,
+ CHECK_ELAPSED_TIME);
+ }
+ break;
+ }
+ }
+
+ }
+
+ public InputManagerV9() {
+ mDevices = new SparseArray<long[]>();
+ mListeners = new HashMap<InputDeviceListener, Handler>();
+ mDefaultHandler = new PollingMessageHandler(this);
+ // as a side-effect, populates our collection of watched
+ // input devices
+ getInputDeviceIds();
+ }
+
+ @Override
+ public InputDevice getInputDevice(int id) {
+ return InputDevice.getDevice(id);
+ }
+
+ @Override
+ public int[] getInputDeviceIds() {
+ // add any hitherto unknown devices to our
+ // collection of watched input devices
+ int[] activeDevices = InputDevice.getDeviceIds();
+ long time = SystemClock.elapsedRealtime();
+ for ( int id : activeDevices ) {
+ long[] lastContact = mDevices.get(id);
+ if ( null == lastContact ) {
+ // we have a new device
+ mDevices.put(id, new long[] { time });
+ }
+ }
+ return activeDevices;
+ }
+
+ @Override
+ public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) {
+ mListeners.remove(listener);
+ if (handler == null) {
+ handler = mDefaultHandler;
+ }
+ mListeners.put(listener, handler);
+ }
+
+ @Override
+ public void unregisterInputDeviceListener(InputDeviceListener listener) {
+ mListeners.remove(listener);
+ }
+
+ private void notifyListeners(int why, int deviceId) {
+ // the state of some device has changed
+ if (!mListeners.isEmpty()) {
+ // yes... this will cause an object to get created... hopefully
+ // it won't happen very often
+ for (InputDeviceListener listener : mListeners.keySet()) {
+ Handler handler = mListeners.get(listener);
+ DeviceEvent odc = DeviceEvent.getDeviceEvent(why, deviceId, listener);
+ handler.post(odc);
+ }
+ }
+ }
+
+ private static class DeviceEvent implements Runnable {
+ private int mMessageType;
+ private int mId;
+ private InputDeviceListener mListener;
+ private static Queue<DeviceEvent> sEventQueue = new ArrayDeque<DeviceEvent>();
+
+ private DeviceEvent() {
+ }
+
+ static DeviceEvent getDeviceEvent(int messageType, int id,
+ InputDeviceListener listener) {
+ DeviceEvent curChanged = sEventQueue.poll();
+ if (null == curChanged) {
+ curChanged = new DeviceEvent();
+ }
+ curChanged.mMessageType = messageType;
+ curChanged.mId = id;
+ curChanged.mListener = listener;
+ return curChanged;
+ }
+
+ @Override
+ public void run() {
+ switch (mMessageType) {
+ case ON_DEVICE_ADDED:
+ mListener.onInputDeviceAdded(mId);
+ break;
+ case ON_DEVICE_CHANGED:
+ mListener.onInputDeviceChanged(mId);
+ break;
+ case ON_DEVICE_REMOVED:
+ mListener.onInputDeviceRemoved(mId);
+ break;
+ default:
+ Log.e(LOG_TAG, "Unknown Message Type");
+ break;
+ }
+ // dump this runnable back in the queue
+ sEventQueue.offer(this);
+ }
+ }
+
+ @Override
+ public void onGenericMotionEvent(MotionEvent event) {
+ // detect new devices
+ int id = event.getDeviceId();
+ long[] timeArray = mDevices.get(id);
+ if (null == timeArray) {
+ notifyListeners(ON_DEVICE_ADDED, id);
+ timeArray = new long[1];
+ mDevices.put(id, timeArray);
+ }
+ long time = SystemClock.elapsedRealtime();
+ timeArray[0] = time;
+ }
+
+ @Override
+ public void onPause() {
+ mDefaultHandler.removeMessages(MESSAGE_TEST_FOR_DISCONNECT);
+ }
+
+ @Override
+ public void onResume() {
+ mDefaultHandler.sendEmptyMessage(MESSAGE_TEST_FOR_DISCONNECT);
+ }
+
+}