You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-05 12:10:55 +00:00
support gamepad remapping on android
This commit is contained in:
@@ -54,6 +54,8 @@ public class GodotLib {
|
||||
public static native void key(int p_scancode, int p_unicode_char, boolean p_pressed);
|
||||
public static native void joybutton(int p_device, int p_but, boolean p_pressed);
|
||||
public static native void joyaxis(int p_device, int p_axis, float p_value);
|
||||
public static native void joyhat(int p_device, int p_hat_x, int p_hat_y);
|
||||
public static native void joyconnectionchanged(int p_device, boolean p_connected, String p_name);
|
||||
public static native void focusin();
|
||||
public static native void focusout();
|
||||
public static native void audio();
|
||||
|
||||
@@ -36,14 +36,21 @@ import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.content.ContextWrapper;
|
||||
import android.view.InputDevice;
|
||||
import android.hardware.input.InputManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import javax.microedition.khronos.egl.EGL10;
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.egl.EGLContext;
|
||||
import javax.microedition.khronos.egl.EGLDisplay;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
import org.godotengine.godot.input.InputManagerCompat;
|
||||
import org.godotengine.godot.input.InputManagerCompat.InputDeviceListener;
|
||||
/**
|
||||
* A simple GLSurfaceView sub-class that demonstrate how to perform
|
||||
* OpenGL ES 2.0 rendering into a GL Surface. Note the following important
|
||||
@@ -62,7 +69,7 @@ import javax.microedition.khronos.opengles.GL10;
|
||||
* that matches it exactly (with regards to red/green/blue/alpha channels
|
||||
* bit depths). Failure to do so would result in an EGL_BAD_MATCH error.
|
||||
*/
|
||||
public class GodotView extends GLSurfaceView {
|
||||
public class GodotView extends GLSurfaceView implements InputDeviceListener {
|
||||
|
||||
private static String TAG = "GodotView";
|
||||
private static final boolean DEBUG = false;
|
||||
@@ -75,6 +82,8 @@ public class GodotView extends GLSurfaceView {
|
||||
|
||||
private Godot activity;
|
||||
|
||||
|
||||
private InputManagerCompat mInputManager;
|
||||
public GodotView(Context context,GodotIO p_io,boolean p_use_gl2, boolean p_use_32_bits, Godot p_activity) {
|
||||
super(context);
|
||||
ctx=context;
|
||||
@@ -88,7 +97,8 @@ public class GodotView extends GLSurfaceView {
|
||||
//will only work on SDK 11+!!
|
||||
setPreserveEGLContextOnPause(true);
|
||||
}
|
||||
|
||||
mInputManager = InputManagerCompat.Factory.getInputManager(this.getContext());
|
||||
mInputManager.registerInputDeviceListener(this, null);
|
||||
init(false, 16, 0);
|
||||
}
|
||||
|
||||
@@ -119,50 +129,112 @@ public class GodotView extends GLSurfaceView {
|
||||
button = 3;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_L1:
|
||||
button = 4;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_L2:
|
||||
button = 6;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_R1:
|
||||
button = 5;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_R2:
|
||||
button = 7;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_SELECT:
|
||||
button = 10;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_START:
|
||||
button = 11;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_THUMBL:
|
||||
button = 8;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_THUMBR:
|
||||
button = 9;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_L2:
|
||||
button = 15;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_R1:
|
||||
button = 10;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_R2:
|
||||
button = 16;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_SELECT:
|
||||
button = 4;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_START:
|
||||
button = 6;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_THUMBL:
|
||||
button = 7;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_THUMBR:
|
||||
button = 8;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_UP:
|
||||
button = 12;
|
||||
button = 11;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||
button = 13;
|
||||
button = 12;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||
button = 14;
|
||||
button = 13;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||
button = 15;
|
||||
button = 14;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_C:
|
||||
button = 17;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_Z:
|
||||
button = 18;
|
||||
break;
|
||||
|
||||
default:
|
||||
button = keyCode - KeyEvent.KEYCODE_BUTTON_1;
|
||||
button = keyCode - KeyEvent.KEYCODE_BUTTON_1 + 20;
|
||||
break;
|
||||
};
|
||||
|
||||
return button;
|
||||
};
|
||||
|
||||
private static class joystick {
|
||||
public int device_id;
|
||||
public String name;
|
||||
public ArrayList<InputDevice.MotionRange> axes;
|
||||
public ArrayList<InputDevice.MotionRange> hats;
|
||||
}
|
||||
|
||||
private static class RangeComparator implements Comparator<InputDevice.MotionRange> {
|
||||
@Override
|
||||
public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
|
||||
return arg0.getAxis() - arg1.getAxis();
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<joystick> joy_devices = new ArrayList<joystick>();
|
||||
|
||||
private int find_joy_device(int device_id) {
|
||||
for (int i=0; i<joy_devices.size(); i++) {
|
||||
if (joy_devices.get(i).device_id == device_id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
onInputDeviceAdded(device_id);
|
||||
return joy_devices.size() - 1;
|
||||
}
|
||||
|
||||
@Override public void onInputDeviceAdded(int deviceId) {
|
||||
joystick joy = new joystick();
|
||||
joy.device_id = deviceId;
|
||||
int id = joy_devices.size();
|
||||
InputDevice device = mInputManager.getInputDevice(deviceId);
|
||||
joy.name = device.getName();
|
||||
joy.axes = new ArrayList<InputDevice.MotionRange>();
|
||||
joy.hats = new ArrayList<InputDevice.MotionRange>();
|
||||
List<InputDevice.MotionRange> ranges = device.getMotionRanges();
|
||||
Collections.sort(ranges, new RangeComparator());
|
||||
for (InputDevice.MotionRange range : ranges) {
|
||||
if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
|
||||
joy.hats.add(range);
|
||||
}
|
||||
else {
|
||||
joy.axes.add(range);
|
||||
}
|
||||
}
|
||||
joy_devices.add(joy);
|
||||
GodotLib.joyconnectionchanged(id, true, joy.name);
|
||||
}
|
||||
|
||||
@Override public void onInputDeviceRemoved(int deviceId) {
|
||||
int id = find_joy_device(deviceId);
|
||||
joy_devices.remove(id);
|
||||
GodotLib.joyconnectionchanged(id, false, "");
|
||||
}
|
||||
|
||||
@Override public void onInputDeviceChanged(int deviceId) {
|
||||
|
||||
}
|
||||
@Override public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
@@ -177,7 +249,7 @@ public class GodotView extends GLSurfaceView {
|
||||
if ((source & InputDevice.SOURCE_JOYSTICK) != 0 || (source & InputDevice.SOURCE_DPAD) != 0 || (source & InputDevice.SOURCE_GAMEPAD) != 0) {
|
||||
|
||||
int button = get_godot_button(keyCode);
|
||||
int device = event.getDeviceId();
|
||||
int device = find_joy_device(event.getDeviceId());
|
||||
|
||||
GodotLib.joybutton(device, button, false);
|
||||
return true;
|
||||
@@ -209,7 +281,8 @@ public class GodotView extends GLSurfaceView {
|
||||
if (event.getRepeatCount() > 0) // ignore key echo
|
||||
return true;
|
||||
int button = get_godot_button(keyCode);
|
||||
int device = event.getDeviceId();
|
||||
int device = find_joy_device(event.getDeviceId());
|
||||
|
||||
//Log.e(TAG, String.format("joy button down! button %x, %d, device %d", keyCode, button, device));
|
||||
|
||||
GodotLib.joybutton(device, button, true);
|
||||
@@ -221,125 +294,27 @@ public class GodotView extends GLSurfaceView {
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
public float axis_value(MotionEvent p_event, InputDevice p_device, int p_axis, int p_pos) {
|
||||
|
||||
final InputDevice.MotionRange range = p_device.getMotionRange(p_axis, p_event.getSource());
|
||||
if (range == null)
|
||||
return 0;
|
||||
|
||||
//Log.e(TAG, String.format("axis ranges %f, %f, %f", range.getRange(), range.getMin(), range.getMax()));
|
||||
|
||||
final float flat = range.getFlat();
|
||||
final float value =
|
||||
p_pos < 0 ? p_event.getAxisValue(p_axis):
|
||||
p_event.getHistoricalAxisValue(p_axis, p_pos);
|
||||
|
||||
final float absval = Math.abs(value);
|
||||
if (absval <= flat) {
|
||||
return 0;
|
||||
};
|
||||
|
||||
final float ret = (value - range.getMin()) / range.getRange() * 2 - 1.0f;
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
float[] last_axis_values = { 0, 0, 0, 0, -1, -1 };
|
||||
boolean[] last_axis_buttons = { false, false, false, false, false, false }; // dpad up down left right, ltrigger, rtrigger
|
||||
|
||||
public void process_axis_state(MotionEvent p_event, int p_pos) {
|
||||
|
||||
int device_id = p_event.getDeviceId();
|
||||
InputDevice device = p_event.getDevice();
|
||||
float val;
|
||||
|
||||
val = axis_value(p_event, device, MotionEvent.AXIS_X, p_pos);
|
||||
if (val != last_axis_values[0]) {
|
||||
last_axis_values[0] = val;
|
||||
//Log.e(TAG, String.format("axis moved! axis %d, value %f", 0, val));
|
||||
GodotLib.joyaxis(device_id, 0, val);
|
||||
};
|
||||
|
||||
val = axis_value(p_event, device, MotionEvent.AXIS_Y, p_pos);
|
||||
if (val != last_axis_values[1]) {
|
||||
last_axis_values[1] = val;
|
||||
//Log.e(TAG, String.format("axis moved! axis %d, value %f", 1, val));
|
||||
GodotLib.joyaxis(device_id, 1, val);
|
||||
};
|
||||
|
||||
val = axis_value(p_event, device, MotionEvent.AXIS_Z, p_pos);
|
||||
if (val != last_axis_values[2]) {
|
||||
last_axis_values[2] = val;
|
||||
//Log.e(TAG, String.format("axis moved! axis %d, value %f", 2, val));
|
||||
GodotLib.joyaxis(device_id, 2, val);
|
||||
};
|
||||
|
||||
val = axis_value(p_event, device, MotionEvent.AXIS_RZ, p_pos);
|
||||
if (val != last_axis_values[3]) {
|
||||
last_axis_values[3] = val;
|
||||
//Log.e(TAG, String.format("axis moved! axis %d, value %f", 3, val));
|
||||
GodotLib.joyaxis(device_id, 3, val);
|
||||
};
|
||||
|
||||
val = axis_value(p_event, device, MotionEvent.AXIS_LTRIGGER, p_pos);
|
||||
if (val != last_axis_values[4]) {
|
||||
last_axis_values[4] = val;
|
||||
if ((val != 0) != (last_axis_buttons[4])) {
|
||||
last_axis_buttons[4] = (val != 0);
|
||||
GodotLib.joybutton(device_id, 6, (val != 0));
|
||||
};
|
||||
};
|
||||
|
||||
val = axis_value(p_event, device, MotionEvent.AXIS_RTRIGGER, p_pos);
|
||||
if (val != last_axis_values[5]) {
|
||||
last_axis_values[5] = val;
|
||||
if ((val != 0) != (last_axis_buttons[5])) {
|
||||
last_axis_buttons[5] = (val != 0);
|
||||
GodotLib.joybutton(device_id, 7, (val != 0));
|
||||
};
|
||||
};
|
||||
|
||||
val = axis_value(p_event, device, MotionEvent.AXIS_HAT_Y, p_pos);
|
||||
|
||||
if (last_axis_buttons[0] != (val > 0)) {
|
||||
last_axis_buttons[0] = val > 0;
|
||||
GodotLib.joybutton(device_id, 12, val > 0);
|
||||
};
|
||||
if (last_axis_buttons[1] != (val < 0)) {
|
||||
last_axis_buttons[1] = val < 0;
|
||||
GodotLib.joybutton(device_id, 13, val > 0);
|
||||
};
|
||||
|
||||
val = axis_value(p_event, device, MotionEvent.AXIS_HAT_X, p_pos);
|
||||
if (last_axis_buttons[2] != (val < 0)) {
|
||||
last_axis_buttons[2] = val < 0;
|
||||
GodotLib.joybutton(device_id, 14, val < 0);
|
||||
};
|
||||
if (last_axis_buttons[3] != (val > 0)) {
|
||||
last_axis_buttons[3] = val > 0;
|
||||
GodotLib.joybutton(device_id, 15, val > 0);
|
||||
};
|
||||
};
|
||||
|
||||
@Override public boolean onGenericMotionEvent(MotionEvent event) {
|
||||
|
||||
if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE) {
|
||||
|
||||
// Process all historical movement samples in the batch
|
||||
final int historySize = event.getHistorySize();
|
||||
int device_id = find_joy_device(event.getDeviceId());
|
||||
joystick joy = joy_devices.get(device_id);
|
||||
|
||||
// Process the movements starting from the
|
||||
// earliest historical position in the batch
|
||||
for (int i = 0; i < historySize; i++) {
|
||||
// Process the event at historical position i
|
||||
process_axis_state(event, i);
|
||||
for (int i = 0; i < joy.axes.size(); i++) {
|
||||
InputDevice.MotionRange range = joy.axes.get(i);
|
||||
float value = (event.getAxisValue(range.getAxis()) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
|
||||
//Log.e(TAG, String.format("axis event: %d, value %f", i, value));
|
||||
GodotLib.joyaxis(device_id, i, value);
|
||||
}
|
||||
|
||||
// Process the current movement sample in the batch (position -1)
|
||||
process_axis_state(event, -1);
|
||||
for (int i = 0; i < joy.hats.size(); i+=2) {
|
||||
int hatX = Math.round(event.getAxisValue(joy.hats.get(i).getAxis()));
|
||||
int hatY = Math.round(event.getAxisValue(joy.hats.get(i+1).getAxis()));
|
||||
//Log.e(TAG, String.format("HAT EVENT %d, %d", hatX, hatY));
|
||||
GodotLib.joyhat(device_id, hatX, hatY);
|
||||
}
|
||||
return true;
|
||||
|
||||
|
||||
};
|
||||
|
||||
return super.onGenericMotionEvent(event);
|
||||
@@ -413,12 +388,12 @@ public class GodotView extends GLSurfaceView {
|
||||
/* Fallback if 32bit View is not supported*/
|
||||
private static class FallbackConfigChooser extends ConfigChooser {
|
||||
private ConfigChooser fallback;
|
||||
|
||||
|
||||
public FallbackConfigChooser(int r, int g, int b, int a, int depth, int stencil, ConfigChooser fallback) {
|
||||
super(r, g, b, a, depth, stencil);
|
||||
this.fallback = fallback;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
|
||||
EGLConfig ec = super.chooseConfig(egl, display, configs);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user