| Index: content/public/android/java/src/org/chromium/content/browser/GamepadList.java
|
| diff --git a/content/public/android/java/src/org/chromium/content/browser/GamepadList.java b/content/public/android/java/src/org/chromium/content/browser/GamepadList.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..47a89f15ff1c0c5ffcdf694da9f8ab460d95edf8
|
| --- /dev/null
|
| +++ b/content/public/android/java/src/org/chromium/content/browser/GamepadList.java
|
| @@ -0,0 +1,319 @@
|
| +/*
|
| + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
|
| + *
|
| + * Redistribution and use in source and binary forms, with or without
|
| + * modification, are permitted provided that the following conditions
|
| + * are met:
|
| + * * Redistributions of source code must retain the above copyright
|
| + * notice, this list of conditions and the following disclaimer.
|
| + * * Redistributions in binary form must reproduce the above copyright
|
| + * notice, this list of conditions and the following disclaimer in the
|
| + * documentation and/or other materials provided with the distribution.
|
| + * * Neither the name of NVIDIA CORPORATION nor the names of its
|
| + * contributors may be used to endorse or promote products derived
|
| + * from this software without specific prior written permission.
|
| + *
|
| + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
| + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
| + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
| + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
| + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
| + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
| + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
| + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| +*/
|
| +
|
| +package org.chromium.content.browser;
|
| +
|
| +import android.content.Context;
|
| +import android.hardware.input.InputManager;
|
| +import android.hardware.input.InputManager.InputDeviceListener;
|
| +import android.view.InputDevice;
|
| +import android.view.InputEvent;
|
| +import android.view.KeyEvent;
|
| +import android.view.MotionEvent;
|
| +
|
| +import org.chromium.base.CalledByNative;
|
| +
|
| +/*
|
| + * Class to manage connected gamepad devices list.
|
| + */
|
| +class GamepadList implements InputDeviceListener {
|
| + private static final int MAX_GAMEPADS = 4;
|
| + private GamepadDevice[] mGamepadDevices = new GamepadDevice[MAX_GAMEPADS];
|
| + protected boolean mHaveDevicesBeenInteractedWith = false;
|
| + private boolean IS_GAMEPAD_ACCESSED = false;
|
| + private InputManager inputManager;
|
| +
|
| + private GamepadList(Context context) {
|
| +
|
| + inputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
|
| + // Get list of all the attached input devices.
|
| + int[] deviceIds = inputManager.getInputDeviceIds();
|
| +
|
| + for (int i = 0; i < deviceIds.length; i++) {
|
| + InputDevice inputDevice = InputDevice.getDevice(deviceIds[i]);
|
| + // Check for gamepad device
|
| + if (isGamepadDevice(inputDevice)) {
|
| + // Register a new gamepad device.
|
| + registerGamepad(inputDevice);
|
| + }
|
| + }
|
| + // Register an input device listener.
|
| + inputManager.registerInputDeviceListener(this, null);
|
| + }
|
| +
|
| + private static GamepadList gamepadList = null;
|
| +
|
| + public boolean haveDevicesBeenInteractedWith() { return mHaveDevicesBeenInteractedWith; }
|
| +
|
| + // Override InputDeviceListener methods
|
| + @Override
|
| + public void onInputDeviceChanged(int deviceId) {
|
| + }
|
| +
|
| + @Override
|
| + public void onInputDeviceRemoved(int deviceId) {
|
| + unregisterGamepad(deviceId);
|
| + }
|
| +
|
| + @Override
|
| + public void onInputDeviceAdded(int deviceId) {
|
| + InputDevice inputDevice = InputDevice.getDevice(deviceId);
|
| + if (isGamepadDevice(inputDevice))
|
| + registerGamepad(inputDevice);
|
| + }
|
| +
|
| + public static GamepadList createInstance(Context context) {
|
| + if (gamepadList != null) {
|
| + return gamepadList;
|
| + } else {
|
| + gamepadList = new GamepadList(context);
|
| + return gamepadList;
|
| + }
|
| + }
|
| +
|
| + @CalledByNative
|
| + public static GamepadList getInstance() {
|
| + if (gamepadList != null) {
|
| + return gamepadList;
|
| + } else {
|
| + return null;
|
| + }
|
| + }
|
| +
|
| + @CalledByNative
|
| + private void notifyForGamepadsAccess(boolean isaccesspaused) {
|
| + IS_GAMEPAD_ACCESSED = !isaccesspaused;
|
| + }
|
| +
|
| + @CalledByNative
|
| + private GamepadList getGamepadList() {
|
| + // Gamepads must only appear in the list if they are currently connected to the user agent,
|
| + // and at least one device has been interacted with by the user. If no devices have been
|
| + // interacted with, devices must not appear in the list to avoid a malicious page from
|
| + // fingerprinting the user.
|
| + if (haveDevicesBeenInteractedWith()) {
|
| + return gamepadList;
|
| + }
|
| + else {
|
| + return null;
|
| + }
|
| + }
|
| +
|
| + @CalledByNative
|
| + private int getDeviceCount() {
|
| + int count = 0;
|
| + for (int i = 0; i < getCount(); ++i) {
|
| + if (getDevice(i) != null) {
|
| + count++;
|
| + }
|
| + }
|
| + return count;
|
| + }
|
| +
|
| + @CalledByNative
|
| + private String getDeviceName(int index) {
|
| + return getDevice(index).getName();
|
| + }
|
| +
|
| + @CalledByNative
|
| + private long getDeviceTimestamp(int index) {
|
| + return getDevice(index).getTimestamp();
|
| + }
|
| +
|
| + @CalledByNative
|
| + private float[] getDeviceAxes(int index) {
|
| + return getDevice(index).getAxes();
|
| + }
|
| +
|
| + @CalledByNative
|
| + private float[] getDeviceButtons(int index) {
|
| + return getDevice(index).getButtons();
|
| + }
|
| +
|
| + @CalledByNative
|
| + private boolean isDeviceConnected(int index) {
|
| + if (index < MAX_GAMEPADS && getDevice(index) != null) {
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + @CalledByNative
|
| + private boolean isKnownGamepadLayout(int index) {
|
| + return getDevice(index).isKnownGamepadLayout();
|
| + }
|
| +
|
| + public GamepadDevice getDeviceById(int deviceId) {
|
| + for (int i = 0; i < getCount(); ++i) {
|
| + GamepadDevice gamepad = mGamepadDevices[i];
|
| + if (gamepad != null && gamepad.getId() == deviceId) {
|
| + return gamepad;
|
| + }
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + public GamepadDevice getDevice(int index) {
|
| + // Maximum 4 Gamepads can be connected at a time starting at index zero.
|
| + assert index >= 0 && index <= getCount() - 1;
|
| + return mGamepadDevices[index];
|
| + }
|
| +
|
| + public int getCount() {
|
| + return MAX_GAMEPADS;
|
| + }
|
| +
|
| + public boolean updateForEvent(KeyEvent event) {
|
| + GamepadDevice gamepad = getGamepadForEvent(event);
|
| + if (gamepad != null) {
|
| + mHaveDevicesBeenInteractedWith = true;
|
| + return gamepad.updateForEvent(event);
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + public boolean updateForEvent(MotionEvent event) {
|
| + GamepadDevice gamepad = getGamepadForEvent(event);
|
| + if (gamepad != null) {
|
| + mHaveDevicesBeenInteractedWith = true;
|
| + return gamepad.updateForEvent(event);
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + public void onPause() {
|
| + inputManager.unregisterInputDeviceListener(this);
|
| + }
|
| +
|
| + public void onResume() {
|
| + // Re-initialize gamepad list.
|
| + // Check if any registered gamepad is disconnected when webview was in pause state.
|
| + for (int i = 0; i < getCount(); ++i) {
|
| + if (mGamepadDevices[i] != null) {
|
| + InputDevice device = InputDevice.getDevice(mGamepadDevices[i].getId());
|
| + if (device == null) {
|
| + // Remove disconnected gamepad.
|
| + mGamepadDevices[i] = null;
|
| + }
|
| + }
|
| + }
|
| + // Check if any new gamepad is connected when webview was in pause state.
|
| + int[] deviceIds = inputManager.getInputDeviceIds();
|
| + for (int i = 0; i < deviceIds.length; ++i) {
|
| + InputDevice device = InputDevice.getDevice(deviceIds[i]);
|
| + if (isGamepadDevice(device)) {
|
| + GamepadDevice gamepad = getDeviceById(deviceIds[i]);
|
| + if (gamepad == null) {
|
| + // Found unregistered gamepad, could be a new device connected when
|
| + // webview was in puase state or disconnected and re-connected same device.
|
| + registerGamepad(device);
|
| + }
|
| + }
|
| + }
|
| + inputManager.registerInputDeviceListener(this, null);
|
| + }
|
| +
|
| + protected int getNextAvailableIndex() {
|
| + // When multiple gamepads are connected to a user agent, indices must be assigned on a
|
| + // first-come first-serve basis, starting at zero. If a gamepad is disconnected, previously
|
| + // assigned indices must not be reassigned to gamepads that continue to be connected.
|
| + // However, if a gamepad is disconnected, and subsequently the same or a different
|
| + // gamepad is then connected, index entries must be reused.
|
| +
|
| + for (int i = 0; i < getCount(); ++i) {
|
| + if (getDevice(i) == null) {
|
| + return i;
|
| + }
|
| + }
|
| + // Reached maximum gamepads limit.
|
| + return -1;
|
| + }
|
| +
|
| + protected boolean registerGamepad(InputDevice inputDevice) {
|
| + int index = getNextAvailableIndex();
|
| + if (index == -1) {
|
| + return false; // invalid index
|
| + }
|
| +
|
| + GamepadDevice gamepad = new GamepadDevice(index, inputDevice);
|
| + mGamepadDevices[index] = gamepad;
|
| + return true;
|
| + }
|
| +
|
| + protected void unregisterGamepad(int deviceId) {
|
| + GamepadDevice gamepadDevice = getDeviceById(deviceId);
|
| + if (gamepadDevice == null) {
|
| + return; // Not a registered device.
|
| + }
|
| + int index = gamepadDevice.getIndex();
|
| + mGamepadDevices[index] = null;
|
| + }
|
| +
|
| + private boolean isGamepadDevice(InputDevice inputDevice) {
|
| + int sources = inputDevice.getSources();
|
| + if ((sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK ) {
|
| + //Found gamepad device
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + private GamepadDevice getGamepadForEvent(InputEvent event) {
|
| + int deviceId = event.getDeviceId();
|
| + GamepadDevice gamepad = getDeviceById(deviceId);
|
| + return gamepad;
|
| + }
|
| +
|
| + public boolean isGamepadAccessed() {
|
| + return IS_GAMEPAD_ACCESSED;
|
| + }
|
| +
|
| + public boolean isGamepadEvent(MotionEvent event) {
|
| + if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK) {
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + // Returns true if event's keycode corresponds to a gamepad key.
|
| + // Specific handling for dpad keys is required because
|
| + // KeyEvent.isGamepadButton doesn't consider dpad keys.
|
| + public boolean isGamepadEvent(KeyEvent event) {
|
| + int keyCode = event.getKeyCode();
|
| + switch (keyCode) {
|
| + case KeyEvent.KEYCODE_DPAD_UP:
|
| + case KeyEvent.KEYCODE_DPAD_DOWN:
|
| + case KeyEvent.KEYCODE_DPAD_LEFT:
|
| + case KeyEvent.KEYCODE_DPAD_RIGHT:
|
| + return true;
|
| + default:
|
| + return KeyEvent.isGamepadButton(keyCode);
|
| + }
|
| + }
|
| +
|
| +}
|
|
|