/* * 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 com.example.inputmanagercompat; 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 mDevices; private final Map mListeners; private final Handler mDefaultHandler; private static class PollingMessageHandler extends Handler { private final WeakReference mInputManager; PollingMessageHandler(InputManagerV9 im) { mInputManager = new WeakReference(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(); mListeners = new HashMap(); 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 sEventQueue = new ArrayDeque(); 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); } }