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 com.example.inputmanagercompat; 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 LOG_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 } 87 InputManagerV9()88 public InputManagerV9() { 89 mDevices = new SparseArray<long[]>(); 90 mListeners = new HashMap<InputDeviceListener, Handler>(); 91 mDefaultHandler = new PollingMessageHandler(this); 92 // as a side-effect, populates our collection of watched 93 // input devices 94 getInputDeviceIds(); 95 } 96 97 @Override getInputDevice(int id)98 public InputDevice getInputDevice(int id) { 99 return InputDevice.getDevice(id); 100 } 101 102 @Override getInputDeviceIds()103 public int[] getInputDeviceIds() { 104 // add any hitherto unknown devices to our 105 // collection of watched input devices 106 int[] activeDevices = InputDevice.getDeviceIds(); 107 long time = SystemClock.elapsedRealtime(); 108 for ( int id : activeDevices ) { 109 long[] lastContact = mDevices.get(id); 110 if ( null == lastContact ) { 111 // we have a new device 112 mDevices.put(id, new long[] { time }); 113 } 114 } 115 return activeDevices; 116 } 117 118 @Override registerInputDeviceListener(InputDeviceListener listener, Handler handler)119 public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) { 120 mListeners.remove(listener); 121 if (handler == null) { 122 handler = mDefaultHandler; 123 } 124 mListeners.put(listener, handler); 125 } 126 127 @Override unregisterInputDeviceListener(InputDeviceListener listener)128 public void unregisterInputDeviceListener(InputDeviceListener listener) { 129 mListeners.remove(listener); 130 } 131 notifyListeners(int why, int deviceId)132 private void notifyListeners(int why, int deviceId) { 133 // the state of some device has changed 134 if (!mListeners.isEmpty()) { 135 // yes... this will cause an object to get created... hopefully 136 // it won't happen very often 137 for (InputDeviceListener listener : mListeners.keySet()) { 138 Handler handler = mListeners.get(listener); 139 DeviceEvent odc = DeviceEvent.getDeviceEvent(why, deviceId, listener); 140 handler.post(odc); 141 } 142 } 143 } 144 145 private static class DeviceEvent implements Runnable { 146 private int mMessageType; 147 private int mId; 148 private InputDeviceListener mListener; 149 private static Queue<DeviceEvent> sEventQueue = new ArrayDeque<DeviceEvent>(); 150 DeviceEvent()151 private DeviceEvent() { 152 } 153 getDeviceEvent(int messageType, int id, InputDeviceListener listener)154 static DeviceEvent getDeviceEvent(int messageType, int id, 155 InputDeviceListener listener) { 156 DeviceEvent curChanged = sEventQueue.poll(); 157 if (null == curChanged) { 158 curChanged = new DeviceEvent(); 159 } 160 curChanged.mMessageType = messageType; 161 curChanged.mId = id; 162 curChanged.mListener = listener; 163 return curChanged; 164 } 165 166 @Override run()167 public void run() { 168 switch (mMessageType) { 169 case ON_DEVICE_ADDED: 170 mListener.onInputDeviceAdded(mId); 171 break; 172 case ON_DEVICE_CHANGED: 173 mListener.onInputDeviceChanged(mId); 174 break; 175 case ON_DEVICE_REMOVED: 176 mListener.onInputDeviceRemoved(mId); 177 break; 178 default: 179 Log.e(LOG_TAG, "Unknown Message Type"); 180 break; 181 } 182 // dump this runnable back in the queue 183 sEventQueue.offer(this); 184 } 185 } 186 187 @Override onGenericMotionEvent(MotionEvent event)188 public void onGenericMotionEvent(MotionEvent event) { 189 // detect new devices 190 int id = event.getDeviceId(); 191 long[] timeArray = mDevices.get(id); 192 if (null == timeArray) { 193 notifyListeners(ON_DEVICE_ADDED, id); 194 timeArray = new long[1]; 195 mDevices.put(id, timeArray); 196 } 197 long time = SystemClock.elapsedRealtime(); 198 timeArray[0] = time; 199 } 200 201 @Override onPause()202 public void onPause() { 203 mDefaultHandler.removeMessages(MESSAGE_TEST_FOR_DISCONNECT); 204 } 205 206 @Override onResume()207 public void onResume() { 208 mDefaultHandler.sendEmptyMessage(MESSAGE_TEST_FOR_DISCONNECT); 209 } 210 211 } 212