blob: a2aeafb9de2e4c1edc6d992089f9fbc0142ec4e1 [file] [log] [blame]
Jeff Brown9df6e7a2012-04-05 11:49:26 -07001/*
2 * Copyright (C) 2012 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
17package android.hardware.input;
18
Jeff Browncf39bdf2012-05-18 14:41:19 -070019import com.android.internal.util.ArrayUtils;
20
Jeff Brown9df6e7a2012-04-05 11:49:26 -070021import android.annotation.SdkConstant;
22import android.annotation.SdkConstant.SdkConstantType;
Jeff Brown9df6e7a2012-04-05 11:49:26 -070023import android.content.Context;
Jeff Browna47425a2012-04-13 04:09:27 -070024import android.os.Binder;
Jeff Brownaf9e8d32012-04-12 17:32:48 -070025import android.os.Handler;
Jeff Brownac143512012-04-05 18:57:33 -070026import android.os.IBinder;
Jeff Brownaf9e8d32012-04-12 17:32:48 -070027import android.os.Looper;
28import android.os.Message;
RoboErikfb290df2013-12-16 11:27:55 -080029import android.os.Parcel;
30import android.os.Parcelable;
Jeff Brownac143512012-04-05 18:57:33 -070031import android.os.RemoteException;
32import android.os.ServiceManager;
Jeff Browna47425a2012-04-13 04:09:27 -070033import android.os.Vibrator;
Jeff Brownac143512012-04-05 18:57:33 -070034import android.provider.Settings;
35import android.provider.Settings.SettingNotFoundException;
Jeff Brown9df6e7a2012-04-05 11:49:26 -070036import android.util.Log;
Jeff Brown9f25b7f2012-04-10 14:30:49 -070037import android.util.SparseArray;
Jeff Brownac143512012-04-05 18:57:33 -070038import android.view.InputDevice;
39import android.view.InputEvent;
Jeff Brown9df6e7a2012-04-05 11:49:26 -070040
Jeff Brownaf9e8d32012-04-12 17:32:48 -070041import java.util.ArrayList;
42
Jeff Brown9df6e7a2012-04-05 11:49:26 -070043/**
44 * Provides information about input devices and available key layouts.
45 * <p>
46 * Get an instance of this class by calling
47 * {@link android.content.Context#getSystemService(java.lang.String)
48 * Context.getSystemService()} with the argument
49 * {@link android.content.Context#INPUT_SERVICE}.
50 * </p>
51 */
52public final class InputManager {
53 private static final String TAG = "InputManager";
Jeff Brownaf9e8d32012-04-12 17:32:48 -070054 private static final boolean DEBUG = false;
55
56 private static final int MSG_DEVICE_ADDED = 1;
57 private static final int MSG_DEVICE_REMOVED = 2;
58 private static final int MSG_DEVICE_CHANGED = 3;
Jeff Brown9df6e7a2012-04-05 11:49:26 -070059
Jeff Brown9f25b7f2012-04-10 14:30:49 -070060 private static InputManager sInstance;
Jeff Brownac143512012-04-05 18:57:33 -070061
Jeff Brown9f25b7f2012-04-10 14:30:49 -070062 private final IInputManager mIm;
Jeff Brownaf9e8d32012-04-12 17:32:48 -070063
64 // Guarded by mInputDevicesLock
65 private final Object mInputDevicesLock = new Object();
66 private SparseArray<InputDevice> mInputDevices;
67 private InputDevicesChangedListener mInputDevicesChangedListener;
68 private final ArrayList<InputDeviceListenerDelegate> mInputDeviceListeners =
69 new ArrayList<InputDeviceListenerDelegate>();
Jeff Brown9df6e7a2012-04-05 11:49:26 -070070
71 /**
72 * Broadcast Action: Query available keyboard layouts.
73 * <p>
74 * The input manager service locates available keyboard layouts
75 * by querying broadcast receivers that are registered for this action.
76 * An application can offer additional keyboard layouts to the user
77 * by declaring a suitable broadcast receiver in its manifest.
78 * </p><p>
79 * Here is an example broadcast receiver declaration that an application
80 * might include in its AndroidManifest.xml to advertise keyboard layouts.
81 * The meta-data specifies a resource that contains a description of each keyboard
82 * layout that is provided by the application.
83 * <pre><code>
Jeff Brownd9fec5d2012-05-17 16:01:54 -070084 * &lt;receiver android:name=".InputDeviceReceiver"
85 * android:label="@string/keyboard_layouts_label">
Jeff Brown9df6e7a2012-04-05 11:49:26 -070086 * &lt;intent-filter>
87 * &lt;action android:name="android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS" />
88 * &lt;/intent-filter>
89 * &lt;meta-data android:name="android.hardware.input.metadata.KEYBOARD_LAYOUTS"
90 * android:resource="@xml/keyboard_layouts" />
91 * &lt;/receiver>
92 * </code></pre>
93 * </p><p>
94 * In the above example, the <code>@xml/keyboard_layouts</code> resource refers to
95 * an XML resource whose root element is <code>&lt;keyboard-layouts></code> that
96 * contains zero or more <code>&lt;keyboard-layout></code> elements.
97 * Each <code>&lt;keyboard-layout></code> element specifies the name, label, and location
Jeff Brownd9fec5d2012-05-17 16:01:54 -070098 * of a key character map for a particular keyboard layout. The label on the receiver
99 * is used to name the collection of keyboard layouts provided by this receiver in the
100 * keyboard layout settings.
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700101 * <pre></code>
102 * &lt;?xml version="1.0" encoding="utf-8"?>
103 * &lt;keyboard-layouts xmlns:android="http://schemas.android.com/apk/res/android">
104 * &lt;keyboard-layout android:name="keyboard_layout_english_us"
105 * android:label="@string/keyboard_layout_english_us_label"
Jeff Brown2f095762012-05-10 21:29:33 -0700106 * android:keyboardLayout="@raw/keyboard_layout_english_us" />
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700107 * &lt;/keyboard-layouts>
108 * </p><p>
109 * The <code>android:name</code> attribute specifies an identifier by which
110 * the keyboard layout will be known in the package.
111 * The <code>android:label</code> attributes specifies a human-readable descriptive
112 * label to describe the keyboard layout in the user interface, such as "English (US)".
Jeff Brown2f095762012-05-10 21:29:33 -0700113 * The <code>android:keyboardLayout</code> attribute refers to a
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700114 * <a href="http://source.android.com/tech/input/key-character-map-files.html">
115 * key character map</a> resource that defines the keyboard layout.
116 * </p>
117 */
118 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
119 public static final String ACTION_QUERY_KEYBOARD_LAYOUTS =
120 "android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS";
121
122 /**
123 * Metadata Key: Keyboard layout metadata associated with
124 * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS}.
125 * <p>
126 * Specifies the resource id of a XML resource that describes the keyboard
127 * layouts that are provided by the application.
128 * </p>
129 */
130 public static final String META_DATA_KEYBOARD_LAYOUTS =
131 "android.hardware.input.metadata.KEYBOARD_LAYOUTS";
132
Jeff Brownac143512012-04-05 18:57:33 -0700133 /**
134 * Pointer Speed: The minimum (slowest) pointer speed (-7).
135 * @hide
136 */
137 public static final int MIN_POINTER_SPEED = -7;
138
139 /**
140 * Pointer Speed: The maximum (fastest) pointer speed (7).
141 * @hide
142 */
143 public static final int MAX_POINTER_SPEED = 7;
144
145 /**
146 * Pointer Speed: The default pointer speed (0).
147 * @hide
148 */
149 public static final int DEFAULT_POINTER_SPEED = 0;
150
151 /**
152 * Input Event Injection Synchronization Mode: None.
153 * Never blocks. Injection is asynchronous and is assumed always to be successful.
154 * @hide
155 */
156 public static final int INJECT_INPUT_EVENT_MODE_ASYNC = 0; // see InputDispatcher.h
157
158 /**
159 * Input Event Injection Synchronization Mode: Wait for result.
160 * Waits for previous events to be dispatched so that the input dispatcher can
161 * determine whether input event injection will be permitted based on the current
162 * input focus. Does not wait for the input event to finish being handled
163 * by the application.
164 * @hide
165 */
166 public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT = 1; // see InputDispatcher.h
167
168 /**
169 * Input Event Injection Synchronization Mode: Wait for finish.
170 * Waits for the event to be delivered to the application and handled.
171 * @hide
172 */
173 public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2; // see InputDispatcher.h
174
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700175 private InputManager(IInputManager im) {
176 mIm = im;
Jeff Brownac143512012-04-05 18:57:33 -0700177 }
178
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700179 /**
180 * Gets an instance of the input manager.
181 *
182 * @return The input manager instance.
183 *
184 * @hide
185 */
186 public static InputManager getInstance() {
187 synchronized (InputManager.class) {
188 if (sInstance == null) {
189 IBinder b = ServiceManager.getService(Context.INPUT_SERVICE);
190 sInstance = new InputManager(IInputManager.Stub.asInterface(b));
191 }
192 return sInstance;
193 }
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700194 }
195
196 /**
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700197 * Gets information about the input device with the specified id.
198 * @param id The device id.
199 * @return The input device or null if not found.
200 */
201 public InputDevice getInputDevice(int id) {
202 synchronized (mInputDevicesLock) {
203 populateInputDevicesLocked();
204
205 int index = mInputDevices.indexOfKey(id);
206 if (index < 0) {
207 return null;
208 }
209
210 InputDevice inputDevice = mInputDevices.valueAt(index);
211 if (inputDevice == null) {
212 try {
213 inputDevice = mIm.getInputDevice(id);
214 } catch (RemoteException ex) {
215 throw new RuntimeException("Could not get input device information.", ex);
216 }
Jeff Brown8f1248422013-01-30 16:23:40 -0800217 if (inputDevice != null) {
218 mInputDevices.setValueAt(index, inputDevice);
219 }
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700220 }
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700221 return inputDevice;
222 }
223 }
224
225 /**
Jeff Browncf39bdf2012-05-18 14:41:19 -0700226 * Gets information about the input device with the specified descriptor.
227 * @param descriptor The input device descriptor.
228 * @return The input device or null if not found.
229 * @hide
230 */
231 public InputDevice getInputDeviceByDescriptor(String descriptor) {
232 if (descriptor == null) {
233 throw new IllegalArgumentException("descriptor must not be null.");
234 }
235
236 synchronized (mInputDevicesLock) {
237 populateInputDevicesLocked();
238
239 int numDevices = mInputDevices.size();
240 for (int i = 0; i < numDevices; i++) {
241 InputDevice inputDevice = mInputDevices.valueAt(i);
242 if (inputDevice == null) {
243 int id = mInputDevices.keyAt(i);
244 try {
245 inputDevice = mIm.getInputDevice(id);
246 } catch (RemoteException ex) {
247 // Ignore the problem for the purposes of this method.
Jeff Brown8f1248422013-01-30 16:23:40 -0800248 }
249 if (inputDevice == null) {
Jeff Browncf39bdf2012-05-18 14:41:19 -0700250 continue;
251 }
252 mInputDevices.setValueAt(i, inputDevice);
253 }
254 if (descriptor.equals(inputDevice.getDescriptor())) {
255 return inputDevice;
256 }
257 }
258 return null;
259 }
260 }
261
262 /**
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700263 * Gets the ids of all input devices in the system.
264 * @return The input device ids.
265 */
266 public int[] getInputDeviceIds() {
267 synchronized (mInputDevicesLock) {
268 populateInputDevicesLocked();
269
270 final int count = mInputDevices.size();
271 final int[] ids = new int[count];
272 for (int i = 0; i < count; i++) {
273 ids[i] = mInputDevices.keyAt(i);
274 }
275 return ids;
276 }
277 }
278
279 /**
280 * Registers an input device listener to receive notifications about when
281 * input devices are added, removed or changed.
282 *
283 * @param listener The listener to register.
284 * @param handler The handler on which the listener should be invoked, or null
285 * if the listener should be invoked on the calling thread's looper.
286 *
287 * @see #unregisterInputDeviceListener
288 */
289 public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) {
290 if (listener == null) {
291 throw new IllegalArgumentException("listener must not be null");
292 }
293
294 synchronized (mInputDevicesLock) {
295 int index = findInputDeviceListenerLocked(listener);
296 if (index < 0) {
297 mInputDeviceListeners.add(new InputDeviceListenerDelegate(listener, handler));
298 }
299 }
300 }
301
302 /**
303 * Unregisters an input device listener.
304 *
305 * @param listener The listener to unregister.
306 *
307 * @see #registerInputDeviceListener
308 */
309 public void unregisterInputDeviceListener(InputDeviceListener listener) {
310 if (listener == null) {
311 throw new IllegalArgumentException("listener must not be null");
312 }
313
314 synchronized (mInputDevicesLock) {
315 int index = findInputDeviceListenerLocked(listener);
316 if (index >= 0) {
Jeff Brown9e6d4b02012-04-20 13:08:27 -0700317 InputDeviceListenerDelegate d = mInputDeviceListeners.get(index);
318 d.removeCallbacksAndMessages(null);
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700319 mInputDeviceListeners.remove(index);
320 }
321 }
322 }
323
324 private int findInputDeviceListenerLocked(InputDeviceListener listener) {
325 final int numListeners = mInputDeviceListeners.size();
326 for (int i = 0; i < numListeners; i++) {
327 if (mInputDeviceListeners.get(i).mListener == listener) {
328 return i;
329 }
330 }
331 return -1;
332 }
333
334 /**
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700335 * Gets information about all supported keyboard layouts.
336 * <p>
337 * The input manager consults the built-in keyboard layouts as well
338 * as all keyboard layouts advertised by applications using a
339 * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver.
340 * </p>
341 *
342 * @return A list of all supported keyboard layouts.
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700343 *
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700344 * @hide
345 */
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700346 public KeyboardLayout[] getKeyboardLayouts() {
347 try {
348 return mIm.getKeyboardLayouts();
349 } catch (RemoteException ex) {
350 Log.w(TAG, "Could not get list of keyboard layout informations.", ex);
351 return new KeyboardLayout[0];
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700352 }
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700353 }
354
355 /**
356 * Gets the keyboard layout with the specified descriptor.
357 *
358 * @param keyboardLayoutDescriptor The keyboard layout descriptor, as returned by
359 * {@link KeyboardLayout#getDescriptor()}.
360 * @return The keyboard layout, or null if it could not be loaded.
361 *
362 * @hide
363 */
364 public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) {
365 if (keyboardLayoutDescriptor == null) {
366 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
367 }
368
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700369 try {
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700370 return mIm.getKeyboardLayout(keyboardLayoutDescriptor);
371 } catch (RemoteException ex) {
372 Log.w(TAG, "Could not get keyboard layout information.", ex);
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700373 return null;
374 }
375 }
376
377 /**
RoboErikfb290df2013-12-16 11:27:55 -0800378 * Gets the current keyboard layout descriptor for the specified input
379 * device.
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700380 *
RoboErikfb290df2013-12-16 11:27:55 -0800381 * @param identifier Identifier for the input device
382 * @return The keyboard layout descriptor, or null if no keyboard layout has
383 * been set.
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700384 * @hide
385 */
RoboErikfb290df2013-12-16 11:27:55 -0800386 public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) {
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700387 try {
RoboErikfb290df2013-12-16 11:27:55 -0800388 return mIm.getCurrentKeyboardLayoutForInputDevice(identifier);
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700389 } catch (RemoteException ex) {
Jeff Browncf39bdf2012-05-18 14:41:19 -0700390 Log.w(TAG, "Could not get current keyboard layout for input device.", ex);
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700391 return null;
392 }
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700393 }
394
395 /**
RoboErikfb290df2013-12-16 11:27:55 -0800396 * Sets the current keyboard layout descriptor for the specified input
397 * device.
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700398 * <p>
RoboErikfb290df2013-12-16 11:27:55 -0800399 * This method may have the side-effect of causing the input device in
400 * question to be reconfigured.
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700401 * </p>
402 *
RoboErikfb290df2013-12-16 11:27:55 -0800403 * @param identifier The identifier for the input device.
404 * @param keyboardLayoutDescriptor The keyboard layout descriptor to use,
405 * must not be null.
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700406 * @hide
407 */
RoboErikfb290df2013-12-16 11:27:55 -0800408 public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700409 String keyboardLayoutDescriptor) {
RoboErikfb290df2013-12-16 11:27:55 -0800410 if (identifier == null) {
411 throw new IllegalArgumentException("identifier must not be null");
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700412 }
Jeff Browncf39bdf2012-05-18 14:41:19 -0700413 if (keyboardLayoutDescriptor == null) {
414 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
415 }
416
417 try {
RoboErikfb290df2013-12-16 11:27:55 -0800418 mIm.setCurrentKeyboardLayoutForInputDevice(identifier,
Jeff Browncf39bdf2012-05-18 14:41:19 -0700419 keyboardLayoutDescriptor);
420 } catch (RemoteException ex) {
421 Log.w(TAG, "Could not set current keyboard layout for input device.", ex);
422 }
423 }
424
425 /**
RoboErikfb290df2013-12-16 11:27:55 -0800426 * Gets all keyboard layout descriptors that are enabled for the specified
427 * input device.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700428 *
RoboErikfb290df2013-12-16 11:27:55 -0800429 * @param identifier The identifier for the input device.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700430 * @return The keyboard layout descriptors.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700431 * @hide
432 */
RoboErikfb290df2013-12-16 11:27:55 -0800433 public String[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
434 if (identifier == null) {
Jeff Browncf39bdf2012-05-18 14:41:19 -0700435 throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
436 }
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700437
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700438 try {
RoboErikfb290df2013-12-16 11:27:55 -0800439 return mIm.getKeyboardLayoutsForInputDevice(identifier);
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700440 } catch (RemoteException ex) {
Jeff Browncf39bdf2012-05-18 14:41:19 -0700441 Log.w(TAG, "Could not get keyboard layouts for input device.", ex);
442 return ArrayUtils.emptyArray(String.class);
443 }
444 }
445
446 /**
447 * Adds the keyboard layout descriptor for the specified input device.
448 * <p>
RoboErikfb290df2013-12-16 11:27:55 -0800449 * This method may have the side-effect of causing the input device in
450 * question to be reconfigured.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700451 * </p>
452 *
RoboErikfb290df2013-12-16 11:27:55 -0800453 * @param identifier The identifier for the input device.
454 * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to
455 * add.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700456 * @hide
457 */
RoboErikfb290df2013-12-16 11:27:55 -0800458 public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
Jeff Browncf39bdf2012-05-18 14:41:19 -0700459 String keyboardLayoutDescriptor) {
RoboErikfb290df2013-12-16 11:27:55 -0800460 if (identifier == null) {
Jeff Browncf39bdf2012-05-18 14:41:19 -0700461 throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
462 }
463 if (keyboardLayoutDescriptor == null) {
464 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
465 }
466
467 try {
RoboErikfb290df2013-12-16 11:27:55 -0800468 mIm.addKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor);
Jeff Browncf39bdf2012-05-18 14:41:19 -0700469 } catch (RemoteException ex) {
470 Log.w(TAG, "Could not add keyboard layout for input device.", ex);
471 }
472 }
473
474 /**
475 * Removes the keyboard layout descriptor for the specified input device.
476 * <p>
RoboErikfb290df2013-12-16 11:27:55 -0800477 * This method may have the side-effect of causing the input device in
478 * question to be reconfigured.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700479 * </p>
480 *
RoboErikfb290df2013-12-16 11:27:55 -0800481 * @param identifier The identifier for the input device.
482 * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to
483 * remove.
Jeff Browncf39bdf2012-05-18 14:41:19 -0700484 * @hide
485 */
RoboErikfb290df2013-12-16 11:27:55 -0800486 public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
Jeff Browncf39bdf2012-05-18 14:41:19 -0700487 String keyboardLayoutDescriptor) {
RoboErikfb290df2013-12-16 11:27:55 -0800488 if (identifier == null) {
Jeff Browncf39bdf2012-05-18 14:41:19 -0700489 throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
490 }
491 if (keyboardLayoutDescriptor == null) {
492 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
493 }
494
495 try {
RoboErikfb290df2013-12-16 11:27:55 -0800496 mIm.removeKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor);
Jeff Browncf39bdf2012-05-18 14:41:19 -0700497 } catch (RemoteException ex) {
498 Log.w(TAG, "Could not remove keyboard layout for input device.", ex);
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700499 }
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700500 }
501
Jeff Brownac143512012-04-05 18:57:33 -0700502 /**
503 * Gets the mouse pointer speed.
504 * <p>
505 * Only returns the permanent mouse pointer speed. Ignores any temporary pointer
506 * speed set by {@link #tryPointerSpeed}.
507 * </p>
508 *
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700509 * @param context The application context.
Jeff Brownac143512012-04-05 18:57:33 -0700510 * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
511 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
512 *
513 * @hide
514 */
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700515 public int getPointerSpeed(Context context) {
Jeff Brownac143512012-04-05 18:57:33 -0700516 int speed = DEFAULT_POINTER_SPEED;
517 try {
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700518 speed = Settings.System.getInt(context.getContentResolver(),
Jeff Brownac143512012-04-05 18:57:33 -0700519 Settings.System.POINTER_SPEED);
520 } catch (SettingNotFoundException snfe) {
521 }
522 return speed;
523 }
524
525 /**
526 * Sets the mouse pointer speed.
527 * <p>
528 * Requires {@link android.Manifest.permissions.WRITE_SETTINGS}.
529 * </p>
530 *
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700531 * @param context The application context.
Jeff Brownac143512012-04-05 18:57:33 -0700532 * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
533 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
534 *
535 * @hide
536 */
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700537 public void setPointerSpeed(Context context, int speed) {
Jeff Brownac143512012-04-05 18:57:33 -0700538 if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
539 throw new IllegalArgumentException("speed out of range");
540 }
541
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700542 Settings.System.putInt(context.getContentResolver(),
Jeff Brownac143512012-04-05 18:57:33 -0700543 Settings.System.POINTER_SPEED, speed);
544 }
545
546 /**
547 * Changes the mouse pointer speed temporarily, but does not save the setting.
548 * <p>
549 * Requires {@link android.Manifest.permission.SET_POINTER_SPEED}.
550 * </p>
551 *
552 * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
553 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
554 *
555 * @hide
556 */
557 public void tryPointerSpeed(int speed) {
558 if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
559 throw new IllegalArgumentException("speed out of range");
560 }
561
562 try {
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700563 mIm.tryPointerSpeed(speed);
Jeff Brownac143512012-04-05 18:57:33 -0700564 } catch (RemoteException ex) {
565 Log.w(TAG, "Could not set temporary pointer speed.", ex);
566 }
567 }
568
569 /**
Jeff Brownac143512012-04-05 18:57:33 -0700570 * Queries the framework about whether any physical keys exist on the
571 * any keyboard attached to the device that are capable of producing the given
572 * array of key codes.
573 *
574 * @param keyCodes The array of key codes to query.
575 * @return A new array of the same size as the key codes array whose elements
576 * are set to true if at least one attached keyboard supports the corresponding key code
577 * at the same index in the key codes array.
578 *
579 * @hide
580 */
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700581 public boolean[] deviceHasKeys(int[] keyCodes) {
Michael Wrightb7b2d4b2013-08-19 15:55:38 -0700582 return deviceHasKeys(-1, keyCodes);
583 }
584
585 /**
586 * Queries the framework about whether any physical keys exist on the
587 * any keyboard attached to the device that are capable of producing the given
588 * array of key codes.
589 *
590 * @param id The id of the device to query.
591 * @param keyCodes The array of key codes to query.
592 * @return A new array of the same size as the key codes array whose elements are set to true
593 * if the given device could produce the corresponding key code at the same index in the key
594 * codes array.
595 *
596 * @hide
597 */
598 public boolean[] deviceHasKeys(int id, int[] keyCodes) {
Jeff Brownac143512012-04-05 18:57:33 -0700599 boolean[] ret = new boolean[keyCodes.length];
600 try {
Michael Wrightb7b2d4b2013-08-19 15:55:38 -0700601 mIm.hasKeys(id, InputDevice.SOURCE_ANY, keyCodes, ret);
Jeff Brownac143512012-04-05 18:57:33 -0700602 } catch (RemoteException e) {
603 // no fallback; just return the empty array
604 }
605 return ret;
606 }
607
Michael Wrightb7b2d4b2013-08-19 15:55:38 -0700608
Jeff Brownac143512012-04-05 18:57:33 -0700609 /**
610 * Injects an input event into the event system on behalf of an application.
611 * The synchronization mode determines whether the method blocks while waiting for
612 * input injection to proceed.
613 * <p>
614 * Requires {@link android.Manifest.permission.INJECT_EVENTS} to inject into
615 * windows that are owned by other applications.
616 * </p><p>
617 * Make sure you correctly set the event time and input source of the event
618 * before calling this method.
619 * </p>
620 *
621 * @param event The event to inject.
622 * @param mode The synchronization mode. One of:
623 * {@link #INJECT_INPUT_EVENT_MODE_ASYNC},
624 * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT}, or
625 * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH}.
626 * @return True if input event injection succeeded.
627 *
628 * @hide
629 */
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700630 public boolean injectInputEvent(InputEvent event, int mode) {
Jeff Brownac143512012-04-05 18:57:33 -0700631 if (event == null) {
632 throw new IllegalArgumentException("event must not be null");
633 }
634 if (mode != INJECT_INPUT_EVENT_MODE_ASYNC
635 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
636 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {
637 throw new IllegalArgumentException("mode is invalid");
638 }
639
640 try {
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700641 return mIm.injectInputEvent(event, mode);
Jeff Brownac143512012-04-05 18:57:33 -0700642 } catch (RemoteException ex) {
643 return false;
644 }
645 }
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700646
647 private void populateInputDevicesLocked() {
648 if (mInputDevicesChangedListener == null) {
649 final InputDevicesChangedListener listener = new InputDevicesChangedListener();
650 try {
651 mIm.registerInputDevicesChangedListener(listener);
652 } catch (RemoteException ex) {
653 throw new RuntimeException(
654 "Could not get register input device changed listener", ex);
655 }
656 mInputDevicesChangedListener = listener;
657 }
658
659 if (mInputDevices == null) {
660 final int[] ids;
661 try {
662 ids = mIm.getInputDeviceIds();
663 } catch (RemoteException ex) {
664 throw new RuntimeException("Could not get input device ids.", ex);
665 }
666
667 mInputDevices = new SparseArray<InputDevice>();
668 for (int i = 0; i < ids.length; i++) {
669 mInputDevices.put(ids[i], null);
670 }
671 }
672 }
673
674 private void onInputDevicesChanged(int[] deviceIdAndGeneration) {
675 if (DEBUG) {
676 Log.d(TAG, "Received input devices changed.");
677 }
678
679 synchronized (mInputDevicesLock) {
680 for (int i = mInputDevices.size(); --i > 0; ) {
681 final int deviceId = mInputDevices.keyAt(i);
682 if (!containsDeviceId(deviceIdAndGeneration, deviceId)) {
683 if (DEBUG) {
684 Log.d(TAG, "Device removed: " + deviceId);
685 }
686 mInputDevices.removeAt(i);
687 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_REMOVED, deviceId);
688 }
689 }
690
691 for (int i = 0; i < deviceIdAndGeneration.length; i += 2) {
692 final int deviceId = deviceIdAndGeneration[i];
693 int index = mInputDevices.indexOfKey(deviceId);
694 if (index >= 0) {
695 final InputDevice device = mInputDevices.valueAt(index);
696 if (device != null) {
697 final int generation = deviceIdAndGeneration[i + 1];
698 if (device.getGeneration() != generation) {
699 if (DEBUG) {
700 Log.d(TAG, "Device changed: " + deviceId);
701 }
702 mInputDevices.setValueAt(index, null);
703 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_CHANGED, deviceId);
704 }
705 }
706 } else {
707 if (DEBUG) {
708 Log.d(TAG, "Device added: " + deviceId);
709 }
710 mInputDevices.put(deviceId, null);
711 sendMessageToInputDeviceListenersLocked(MSG_DEVICE_ADDED, deviceId);
712 }
713 }
714 }
715 }
716
717 private void sendMessageToInputDeviceListenersLocked(int what, int deviceId) {
718 final int numListeners = mInputDeviceListeners.size();
719 for (int i = 0; i < numListeners; i++) {
720 InputDeviceListenerDelegate listener = mInputDeviceListeners.get(i);
721 listener.sendMessage(listener.obtainMessage(what, deviceId, 0));
722 }
723 }
724
725 private static boolean containsDeviceId(int[] deviceIdAndGeneration, int deviceId) {
726 for (int i = 0; i < deviceIdAndGeneration.length; i += 2) {
727 if (deviceIdAndGeneration[i] == deviceId) {
728 return true;
729 }
730 }
731 return false;
732 }
733
734 /**
Jeff Browna47425a2012-04-13 04:09:27 -0700735 * Gets a vibrator service associated with an input device, assuming it has one.
736 * @return The vibrator, never null.
737 * @hide
738 */
739 public Vibrator getInputDeviceVibrator(int deviceId) {
740 return new InputDeviceVibrator(deviceId);
741 }
742
743 /**
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700744 * Listens for changes in input devices.
745 */
746 public interface InputDeviceListener {
747 /**
748 * Called whenever an input device has been added to the system.
749 * Use {@link InputManager#getInputDevice} to get more information about the device.
750 *
751 * @param deviceId The id of the input device that was added.
752 */
753 void onInputDeviceAdded(int deviceId);
754
755 /**
756 * Called whenever an input device has been removed from the system.
757 *
758 * @param deviceId The id of the input device that was removed.
759 */
760 void onInputDeviceRemoved(int deviceId);
761
762 /**
763 * Called whenever the properties of an input device have changed since they
764 * were last queried. Use {@link InputManager#getInputDevice} to get
765 * a fresh {@link InputDevice} object with the new properties.
766 *
767 * @param deviceId The id of the input device that changed.
768 */
769 void onInputDeviceChanged(int deviceId);
770 }
771
772 private final class InputDevicesChangedListener extends IInputDevicesChangedListener.Stub {
773 @Override
774 public void onInputDevicesChanged(int[] deviceIdAndGeneration) throws RemoteException {
775 InputManager.this.onInputDevicesChanged(deviceIdAndGeneration);
776 }
777 }
778
779 private static final class InputDeviceListenerDelegate extends Handler {
780 public final InputDeviceListener mListener;
781
782 public InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler) {
783 super(handler != null ? handler.getLooper() : Looper.myLooper());
784 mListener = listener;
785 }
786
787 @Override
788 public void handleMessage(Message msg) {
789 switch (msg.what) {
790 case MSG_DEVICE_ADDED:
791 mListener.onInputDeviceAdded(msg.arg1);
792 break;
793 case MSG_DEVICE_REMOVED:
794 mListener.onInputDeviceRemoved(msg.arg1);
795 break;
796 case MSG_DEVICE_CHANGED:
797 mListener.onInputDeviceChanged(msg.arg1);
798 break;
799 }
800 }
801 }
Jeff Browna47425a2012-04-13 04:09:27 -0700802
803 private final class InputDeviceVibrator extends Vibrator {
804 private final int mDeviceId;
805 private final Binder mToken;
806
807 public InputDeviceVibrator(int deviceId) {
808 mDeviceId = deviceId;
809 mToken = new Binder();
810 }
811
812 @Override
813 public boolean hasVibrator() {
814 return true;
815 }
816
817 @Override
818 public void vibrate(long milliseconds) {
819 vibrate(new long[] { 0, milliseconds}, -1);
820 }
821
822 @Override
823 public void vibrate(long[] pattern, int repeat) {
824 if (repeat >= pattern.length) {
825 throw new ArrayIndexOutOfBoundsException();
826 }
827 try {
828 mIm.vibrate(mDeviceId, pattern, repeat, mToken);
829 } catch (RemoteException ex) {
830 Log.w(TAG, "Failed to vibrate.", ex);
831 }
832 }
833
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800834 /**
835 * @hide
836 */
837 @Override
838 public void vibrate(int owningUid, String owningPackage, long milliseconds) {
839 vibrate(milliseconds);
840 }
841
842 /**
843 * @hide
844 */
845 @Override
846 public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat) {
847 vibrate(pattern, repeat);
848 }
849
Jeff Browna47425a2012-04-13 04:09:27 -0700850 @Override
851 public void cancel() {
852 try {
853 mIm.cancelVibrate(mDeviceId, mToken);
854 } catch (RemoteException ex) {
855 Log.w(TAG, "Failed to cancel vibration.", ex);
856 }
857 }
858 }
Jeff Brown9df6e7a2012-04-05 11:49:26 -0700859}