Home | History | Annotate | Download | only in inputmanagercompat
      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 
     49         PollingMessageHandler(InputManagerV9 im) {
     50             mInputManager = new WeakReference<InputManagerV9>(im);
     51         }
     52 
     53         @Override
     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 
     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
     98     public InputDevice getInputDevice(int id) {
     99         return InputDevice.getDevice(id);
    100     }
    101 
    102     @Override
    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
    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
    128     public void unregisterInputDeviceListener(InputDeviceListener listener) {
    129         mListeners.remove(listener);
    130     }
    131 
    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 
    151         private DeviceEvent() {
    152         }
    153 
    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
    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
    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
    202     public void onPause() {
    203         mDefaultHandler.removeMessages(MESSAGE_TEST_FOR_DISCONNECT);
    204     }
    205 
    206     @Override
    207     public void onResume() {
    208         mDefaultHandler.sendEmptyMessage(MESSAGE_TEST_FOR_DISCONNECT);
    209     }
    210 
    211 }
    212