blob: 3b3c237405132e3f8650f158a5b6ffa22f525e9c [file] [log] [blame]
/*
* Copyright (C) 2012 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 android.hardware.input;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Log;
import android.util.SparseArray;
import android.view.InputDevice;
import android.view.InputEvent;
/**
* Provides information about input devices and available key layouts.
* <p>
* Get an instance of this class by calling
* {@link android.content.Context#getSystemService(java.lang.String)
* Context.getSystemService()} with the argument
* {@link android.content.Context#INPUT_SERVICE}.
* </p>
*/
public final class InputManager {
private static final String TAG = "InputManager";
private static InputManager sInstance;
private final IInputManager mIm;
private final SparseArray<InputDevice> mInputDevices = new SparseArray<InputDevice>();
/**
* Broadcast Action: Query available keyboard layouts.
* <p>
* The input manager service locates available keyboard layouts
* by querying broadcast receivers that are registered for this action.
* An application can offer additional keyboard layouts to the user
* by declaring a suitable broadcast receiver in its manifest.
* </p><p>
* Here is an example broadcast receiver declaration that an application
* might include in its AndroidManifest.xml to advertise keyboard layouts.
* The meta-data specifies a resource that contains a description of each keyboard
* layout that is provided by the application.
* <pre><code>
* &lt;receiver android:name=".InputDeviceReceiver">
* &lt;intent-filter>
* &lt;action android:name="android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS" />
* &lt;/intent-filter>
* &lt;meta-data android:name="android.hardware.input.metadata.KEYBOARD_LAYOUTS"
* android:resource="@xml/keyboard_layouts" />
* &lt;/receiver>
* </code></pre>
* </p><p>
* In the above example, the <code>@xml/keyboard_layouts</code> resource refers to
* an XML resource whose root element is <code>&lt;keyboard-layouts></code> that
* contains zero or more <code>&lt;keyboard-layout></code> elements.
* Each <code>&lt;keyboard-layout></code> element specifies the name, label, and location
* of a key character map for a particular keyboard layout.
* <pre></code>
* &lt;?xml version="1.0" encoding="utf-8"?>
* &lt;keyboard-layouts xmlns:android="http://schemas.android.com/apk/res/android">
* &lt;keyboard-layout android:name="keyboard_layout_english_us"
* android:label="@string/keyboard_layout_english_us_label"
* android:kcm="@raw/keyboard_layout_english_us" />
* &lt;/keyboard-layouts>
* </p><p>
* The <code>android:name</code> attribute specifies an identifier by which
* the keyboard layout will be known in the package.
* The <code>android:label</code> attributes specifies a human-readable descriptive
* label to describe the keyboard layout in the user interface, such as "English (US)".
* The <code>android:kcm</code> attribute refers to a
* <a href="http://source.android.com/tech/input/key-character-map-files.html">
* key character map</a> resource that defines the keyboard layout.
* </p>
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_QUERY_KEYBOARD_LAYOUTS =
"android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS";
/**
* Metadata Key: Keyboard layout metadata associated with
* {@link #ACTION_QUERY_KEYBOARD_LAYOUTS}.
* <p>
* Specifies the resource id of a XML resource that describes the keyboard
* layouts that are provided by the application.
* </p>
*/
public static final String META_DATA_KEYBOARD_LAYOUTS =
"android.hardware.input.metadata.KEYBOARD_LAYOUTS";
/**
* Pointer Speed: The minimum (slowest) pointer speed (-7).
* @hide
*/
public static final int MIN_POINTER_SPEED = -7;
/**
* Pointer Speed: The maximum (fastest) pointer speed (7).
* @hide
*/
public static final int MAX_POINTER_SPEED = 7;
/**
* Pointer Speed: The default pointer speed (0).
* @hide
*/
public static final int DEFAULT_POINTER_SPEED = 0;
/**
* Input Event Injection Synchronization Mode: None.
* Never blocks. Injection is asynchronous and is assumed always to be successful.
* @hide
*/
public static final int INJECT_INPUT_EVENT_MODE_ASYNC = 0; // see InputDispatcher.h
/**
* Input Event Injection Synchronization Mode: Wait for result.
* Waits for previous events to be dispatched so that the input dispatcher can
* determine whether input event injection will be permitted based on the current
* input focus. Does not wait for the input event to finish being handled
* by the application.
* @hide
*/
public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT = 1; // see InputDispatcher.h
/**
* Input Event Injection Synchronization Mode: Wait for finish.
* Waits for the event to be delivered to the application and handled.
* @hide
*/
public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2; // see InputDispatcher.h
private InputManager(IInputManager im) {
mIm = im;
}
/**
* Gets an instance of the input manager.
*
* @return The input manager instance.
*
* @hide
*/
public static InputManager getInstance() {
synchronized (InputManager.class) {
if (sInstance == null) {
IBinder b = ServiceManager.getService(Context.INPUT_SERVICE);
sInstance = new InputManager(IInputManager.Stub.asInterface(b));
}
return sInstance;
}
}
/**
* Gets information about all supported keyboard layouts.
* <p>
* The input manager consults the built-in keyboard layouts as well
* as all keyboard layouts advertised by applications using a
* {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver.
* </p>
*
* @return A list of all supported keyboard layouts.
*
* @hide
*/
public KeyboardLayout[] getKeyboardLayouts() {
try {
return mIm.getKeyboardLayouts();
} catch (RemoteException ex) {
Log.w(TAG, "Could not get list of keyboard layout informations.", ex);
return new KeyboardLayout[0];
}
}
/**
* Gets the keyboard layout with the specified descriptor.
*
* @param keyboardLayoutDescriptor The keyboard layout descriptor, as returned by
* {@link KeyboardLayout#getDescriptor()}.
* @return The keyboard layout, or null if it could not be loaded.
*
* @hide
*/
public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) {
if (keyboardLayoutDescriptor == null) {
throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
}
try {
return mIm.getKeyboardLayout(keyboardLayoutDescriptor);
} catch (RemoteException ex) {
Log.w(TAG, "Could not get keyboard layout information.", ex);
return null;
}
}
/**
* Gets the keyboard layout descriptor for the specified input device.
*
* @param inputDeviceDescriptor The input device descriptor.
* @return The keyboard layout descriptor, or null if unknown or if the default
* keyboard layout will be used.
*
* @hide
*/
public String getKeyboardLayoutForInputDevice(String inputDeviceDescriptor) {
if (inputDeviceDescriptor == null) {
throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
}
try {
return mIm.getKeyboardLayoutForInputDevice(inputDeviceDescriptor);
} catch (RemoteException ex) {
Log.w(TAG, "Could not get keyboard layout for input device.", ex);
return null;
}
}
/**
* Sets the keyboard layout descriptor for the specified input device.
* <p>
* This method may have the side-effect of causing the input device in question
* to be reconfigured.
* </p>
*
* @param inputDeviceDescriptor The input device descriptor.
* @param keyboardLayoutDescriptor The keyboard layout descriptor, or null to remove
* the mapping so that the default keyboard layout will be used for the input device.
*
* @hide
*/
public void setKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
String keyboardLayoutDescriptor) {
if (inputDeviceDescriptor == null) {
throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
}
try {
mIm.setKeyboardLayoutForInputDevice(inputDeviceDescriptor, keyboardLayoutDescriptor);
} catch (RemoteException ex) {
Log.w(TAG, "Could not set keyboard layout for input device.", ex);
}
}
/**
* Gets the mouse pointer speed.
* <p>
* Only returns the permanent mouse pointer speed. Ignores any temporary pointer
* speed set by {@link #tryPointerSpeed}.
* </p>
*
* @param context The application context.
* @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
* {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
*
* @hide
*/
public int getPointerSpeed(Context context) {
int speed = DEFAULT_POINTER_SPEED;
try {
speed = Settings.System.getInt(context.getContentResolver(),
Settings.System.POINTER_SPEED);
} catch (SettingNotFoundException snfe) {
}
return speed;
}
/**
* Sets the mouse pointer speed.
* <p>
* Requires {@link android.Manifest.permissions.WRITE_SETTINGS}.
* </p>
*
* @param context The application context.
* @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
* {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
*
* @hide
*/
public void setPointerSpeed(Context context, int speed) {
if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
throw new IllegalArgumentException("speed out of range");
}
Settings.System.putInt(context.getContentResolver(),
Settings.System.POINTER_SPEED, speed);
}
/**
* Changes the mouse pointer speed temporarily, but does not save the setting.
* <p>
* Requires {@link android.Manifest.permission.SET_POINTER_SPEED}.
* </p>
*
* @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
* {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
*
* @hide
*/
public void tryPointerSpeed(int speed) {
if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
throw new IllegalArgumentException("speed out of range");
}
try {
mIm.tryPointerSpeed(speed);
} catch (RemoteException ex) {
Log.w(TAG, "Could not set temporary pointer speed.", ex);
}
}
/**
* Gets information about the input device with the specified id.
* @param id The device id.
* @return The input device or null if not found.
*
* @hide
*/
public InputDevice getInputDevice(int id) {
synchronized (mInputDevices) {
InputDevice inputDevice = mInputDevices.get(id);
if (inputDevice != null) {
return inputDevice;
}
}
final InputDevice newInputDevice;
try {
newInputDevice = mIm.getInputDevice(id);
} catch (RemoteException ex) {
throw new RuntimeException("Could not get input device information.", ex);
}
synchronized (mInputDevices) {
InputDevice inputDevice = mInputDevices.get(id);
if (inputDevice != null) {
return inputDevice;
}
mInputDevices.put(id, newInputDevice);
return newInputDevice;
}
}
/**
* Gets the ids of all input devices in the system.
* @return The input device ids.
*
* @hide
*/
public int[] getInputDeviceIds() {
try {
return mIm.getInputDeviceIds();
} catch (RemoteException ex) {
throw new RuntimeException("Could not get input device ids.", ex);
}
}
/**
* Queries the framework about whether any physical keys exist on the
* any keyboard attached to the device that are capable of producing the given
* array of key codes.
*
* @param keyCodes The array of key codes to query.
* @return A new array of the same size as the key codes array whose elements
* are set to true if at least one attached keyboard supports the corresponding key code
* at the same index in the key codes array.
*
* @hide
*/
public boolean[] deviceHasKeys(int[] keyCodes) {
boolean[] ret = new boolean[keyCodes.length];
try {
mIm.hasKeys(-1, InputDevice.SOURCE_ANY, keyCodes, ret);
} catch (RemoteException e) {
// no fallback; just return the empty array
}
return ret;
}
/**
* Injects an input event into the event system on behalf of an application.
* The synchronization mode determines whether the method blocks while waiting for
* input injection to proceed.
* <p>
* Requires {@link android.Manifest.permission.INJECT_EVENTS} to inject into
* windows that are owned by other applications.
* </p><p>
* Make sure you correctly set the event time and input source of the event
* before calling this method.
* </p>
*
* @param event The event to inject.
* @param mode The synchronization mode. One of:
* {@link #INJECT_INPUT_EVENT_MODE_ASYNC},
* {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT}, or
* {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH}.
* @return True if input event injection succeeded.
*
* @hide
*/
public boolean injectInputEvent(InputEvent event, int mode) {
if (event == null) {
throw new IllegalArgumentException("event must not be null");
}
if (mode != INJECT_INPUT_EVENT_MODE_ASYNC
&& mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
&& mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {
throw new IllegalArgumentException("mode is invalid");
}
try {
return mIm.injectInputEvent(event, mode);
} catch (RemoteException ex) {
return false;
}
}
}