1 /* 2 * Copyright (C) 2013 The Android Open Source Project 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 org.godotengine.godot.input; 18 19 import android.os.Handler; 20 import android.os.Message; 21 import android.os.SystemClock; 22 import android.util.Log; 23 import android.util.SparseArray; 24 import android.view.InputDevice; 25 import android.view.MotionEvent; 26 27 import java.lang.ref.WeakReference; 28 import java.util.ArrayDeque; 29 import java.util.HashMap; 30 import java.util.Map; 31 import java.util.Queue; 32 33 public class InputManagerV9 implements InputManagerCompat { 34 private static final String TAG = "InputManagerV9"; 35 private static final int MESSAGE_TEST_FOR_DISCONNECT = 101; 36 private static final long CHECK_ELAPSED_TIME = 3000L; 37 38 private static final int ON_DEVICE_ADDED = 0; 39 private static final int ON_DEVICE_CHANGED = 1; 40 private static final int ON_DEVICE_REMOVED = 2; 41 42 private final SparseArray<long[]> mDevices; 43 private final Map<InputDeviceListener, Handler> mListeners; 44 private final Handler mDefaultHandler; 45 46 private static class PollingMessageHandler extends Handler { 47 private final WeakReference<InputManagerV9> mInputManager; 48 PollingMessageHandler(InputManagerV9 im)49 PollingMessageHandler(InputManagerV9 im) { 50 mInputManager = new WeakReference<InputManagerV9>(im); 51 } 52 53 @Override handleMessage(Message msg)54 public void handleMessage(Message msg) { 55 super.handleMessage(msg); 56 switch (msg.what) { 57 case MESSAGE_TEST_FOR_DISCONNECT: 58 InputManagerV9 imv = mInputManager.get(); 59 if (null != imv) { 60 long time = SystemClock.elapsedRealtime(); 61 int size = imv.mDevices.size(); 62 for (int i = 0; i < size; i++) { 63 long[] lastContact = imv.mDevices.valueAt(i); 64 if (null != lastContact) { 65 if (time - lastContact[0] > CHECK_ELAPSED_TIME) { 66 // check to see if the device has been 67 // disconnected 68 int id = imv.mDevices.keyAt(i); 69 if (null == InputDevice.getDevice(id)) { 70 // disconnected! 71 imv.notifyListeners(ON_DEVICE_REMOVED, id); 72 imv.mDevices.remove(id); 73 } else { 74 lastContact[0] = time; 75 } 76 } 77 } 78 } 79 sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT, 80 CHECK_ELAPSED_TIME); 81 } 82 break; 83 } 84 } 85 } 86 InputManagerV9()87 public InputManagerV9() { 88 mDevices = new SparseArray<long[]>(); 89 mListeners = new HashMap<InputDeviceListener, Handler>(); 90 mDefaultHandler = new PollingMessageHandler(this); 91 // as a side-effect, populates our collection of watched 92 // input devices 93 getInputDeviceIds(); 94 } 95 96 @Override getInputDevice(int id)97 public InputDevice getInputDevice(int id) { 98 return InputDevice.getDevice(id); 99 } 100 101 @Override getInputDeviceIds()102 public int[] getInputDeviceIds() { 103 // add any hitherto unknown devices to our 104 // collection of watched input devices 105 int[] activeDevices = InputDevice.getDeviceIds(); 106 long time = SystemClock.elapsedRealtime(); 107 for (int id : activeDevices) { 108 long[] lastContact = mDevices.get(id); 109 if (null == lastContact) { 110 // we have a new device 111 mDevices.put(id, new long[] { time }); 112 } 113 } 114 return activeDevices; 115 } 116 117 @Override registerInputDeviceListener(InputDeviceListener listener, Handler handler)118 public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) { 119 mListeners.remove(listener); 120 if (handler == null) { 121 handler = mDefaultHandler; 122 } 123 mListeners.put(listener, handler); 124 } 125 126 @Override unregisterInputDeviceListener(InputDeviceListener listener)127 public void unregisterInputDeviceListener(InputDeviceListener listener) { 128 mListeners.remove(listener); 129 } 130 notifyListeners(int why, int deviceId)131 private void notifyListeners(int why, int deviceId) { 132 // the state of some device has changed 133 if (!mListeners.isEmpty()) { 134 // yes... this will cause an object to get created... hopefully 135 // it won't happen very often 136 for (InputDeviceListener listener : mListeners.keySet()) { 137 Handler handler = mListeners.get(listener); 138 DeviceEvent odc = DeviceEvent.getDeviceEvent(why, deviceId, listener); 139 handler.post(odc); 140 } 141 } 142 } 143 144 private static class DeviceEvent implements Runnable { 145 private int mMessageType; 146 private int mId; 147 private InputDeviceListener mListener; 148 private static Queue<DeviceEvent> sEventQueue = new ArrayDeque<DeviceEvent>(); 149 DeviceEvent()150 private DeviceEvent() { 151 } 152 getDeviceEvent(int messageType, int id, InputDeviceListener listener)153 static DeviceEvent getDeviceEvent(int messageType, int id, 154 InputDeviceListener listener) { 155 DeviceEvent curChanged = sEventQueue.poll(); 156 if (null == curChanged) { 157 curChanged = new DeviceEvent(); 158 } 159 curChanged.mMessageType = messageType; 160 curChanged.mId = id; 161 curChanged.mListener = listener; 162 return curChanged; 163 } 164 165 @Override run()166 public void run() { 167 switch (mMessageType) { 168 case ON_DEVICE_ADDED: 169 mListener.onInputDeviceAdded(mId); 170 break; 171 case ON_DEVICE_CHANGED: 172 mListener.onInputDeviceChanged(mId); 173 break; 174 case ON_DEVICE_REMOVED: 175 mListener.onInputDeviceRemoved(mId); 176 break; 177 default: 178 Log.e(TAG, "Unknown Message Type"); 179 break; 180 } 181 // dump this runnable back in the queue 182 sEventQueue.offer(this); 183 } 184 } 185 186 @Override onGenericMotionEvent(MotionEvent event)187 public void onGenericMotionEvent(MotionEvent event) { 188 // detect new devices 189 int id = event.getDeviceId(); 190 long[] timeArray = mDevices.get(id); 191 if (null == timeArray) { 192 notifyListeners(ON_DEVICE_ADDED, id); 193 timeArray = new long[1]; 194 mDevices.put(id, timeArray); 195 } 196 long time = SystemClock.elapsedRealtime(); 197 timeArray[0] = time; 198 } 199 200 @Override onPause()201 public void onPause() { 202 mDefaultHandler.removeMessages(MESSAGE_TEST_FOR_DISCONNECT); 203 } 204 205 @Override onResume()206 public void onResume() { 207 mDefaultHandler.sendEmptyMessage(MESSAGE_TEST_FOR_DISCONNECT); 208 } 209 } 210