michael@0: /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.util; michael@0: michael@0: import android.os.Build; michael@0: import android.view.InputDevice; michael@0: import android.view.KeyCharacterMap; michael@0: import android.view.KeyEvent; michael@0: import android.view.MotionEvent; michael@0: import android.view.View; michael@0: import android.widget.AdapterView; michael@0: import android.widget.ListView; michael@0: michael@0: public final class GamepadUtils { michael@0: private static final int SONY_XPERIA_GAMEPAD_DEVICE_ID = 196611; michael@0: michael@0: private static View.OnKeyListener sClickDispatcher; michael@0: private static float sDeadZoneThresholdOverride = 1e-2f; michael@0: michael@0: private GamepadUtils() { michael@0: } michael@0: michael@0: private static boolean isGamepadKey(KeyEvent event) { michael@0: if (Build.VERSION.SDK_INT >= 9) { michael@0: return (event.getSource() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: public static boolean isActionKey(KeyEvent event) { michael@0: return (isGamepadKey(event) && (event.getKeyCode() == KeyEvent.KEYCODE_BUTTON_A)); michael@0: } michael@0: michael@0: public static boolean isActionKeyDown(KeyEvent event) { michael@0: return isActionKey(event) && event.getAction() == KeyEvent.ACTION_DOWN; michael@0: } michael@0: michael@0: public static boolean isBackKey(KeyEvent event) { michael@0: return (isGamepadKey(event) && (event.getKeyCode() == KeyEvent.KEYCODE_BUTTON_B)); michael@0: } michael@0: michael@0: public static void overrideDeadZoneThreshold(float threshold) { michael@0: sDeadZoneThresholdOverride = threshold; michael@0: } michael@0: michael@0: public static boolean isValueInDeadZone(MotionEvent event, int axis) { michael@0: if (Build.VERSION.SDK_INT < 9) { michael@0: return false; michael@0: } michael@0: michael@0: float threshold; michael@0: if (sDeadZoneThresholdOverride >= 0) { michael@0: threshold = sDeadZoneThresholdOverride; michael@0: } else { michael@0: InputDevice.MotionRange range = event.getDevice().getMotionRange(axis); michael@0: threshold = range.getFlat() + range.getFuzz(); michael@0: } michael@0: float value = event.getAxisValue(axis); michael@0: return (Math.abs(value) < threshold); michael@0: } michael@0: michael@0: public static boolean isPanningControl(MotionEvent event) { michael@0: if (Build.VERSION.SDK_INT < 12) { michael@0: return false; michael@0: } michael@0: if ((event.getSource() & InputDevice.SOURCE_CLASS_MASK) != InputDevice.SOURCE_CLASS_JOYSTICK) { michael@0: return false; michael@0: } michael@0: if (isValueInDeadZone(event, MotionEvent.AXIS_X) michael@0: && isValueInDeadZone(event, MotionEvent.AXIS_Y) michael@0: && isValueInDeadZone(event, MotionEvent.AXIS_Z) michael@0: && isValueInDeadZone(event, MotionEvent.AXIS_RZ)) { michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: public static View.OnKeyListener getClickDispatcher() { michael@0: if (sClickDispatcher == null) { michael@0: sClickDispatcher = new View.OnKeyListener() { michael@0: @Override michael@0: public boolean onKey(View v, int keyCode, KeyEvent event) { michael@0: if (isActionKeyDown(event)) { michael@0: return v.performClick(); michael@0: } michael@0: return false; michael@0: } michael@0: }; michael@0: } michael@0: return sClickDispatcher; michael@0: } michael@0: michael@0: public static KeyEvent translateSonyXperiaGamepadKeys(int keyCode, KeyEvent event) { michael@0: // The cross and circle button mappings may be swapped in the different regions so michael@0: // determine if they are swapped so the proper key codes can be mapped to the keys michael@0: boolean areKeysSwapped = areSonyXperiaGamepadKeysSwapped(); michael@0: michael@0: // If a Sony Xperia, remap the cross and circle buttons to buttons michael@0: // A and B for the gamepad API michael@0: switch (keyCode) { michael@0: case KeyEvent.KEYCODE_BACK: michael@0: keyCode = (areKeysSwapped ? KeyEvent.KEYCODE_BUTTON_A : KeyEvent.KEYCODE_BUTTON_B); michael@0: break; michael@0: michael@0: case KeyEvent.KEYCODE_DPAD_CENTER: michael@0: keyCode = (areKeysSwapped ? KeyEvent.KEYCODE_BUTTON_B : KeyEvent.KEYCODE_BUTTON_A); michael@0: break; michael@0: michael@0: default: michael@0: return event; michael@0: } michael@0: michael@0: return new KeyEvent(event.getAction(), keyCode); michael@0: } michael@0: michael@0: public static boolean isSonyXperiaGamepadKeyEvent(KeyEvent event) { michael@0: return (event.getDeviceId() == SONY_XPERIA_GAMEPAD_DEVICE_ID && michael@0: "Sony Ericsson".equals(Build.MANUFACTURER) && michael@0: ("R800".equals(Build.MODEL) || "R800i".equals(Build.MODEL))); michael@0: } michael@0: michael@0: private static boolean areSonyXperiaGamepadKeysSwapped() { michael@0: // The cross and circle buttons on Sony Xperia phones are swapped michael@0: // in different regions michael@0: // http://developer.sonymobile.com/2011/02/13/xperia-play-game-keys/ michael@0: final char DEFAULT_O_BUTTON_LABEL = 0x25CB; michael@0: michael@0: boolean swapped = false; michael@0: int[] deviceIds = InputDevice.getDeviceIds(); michael@0: michael@0: for (int i= 0; deviceIds != null && i < deviceIds.length; i++) { michael@0: KeyCharacterMap keyCharacterMap = KeyCharacterMap.load(deviceIds[i]); michael@0: if (keyCharacterMap != null && DEFAULT_O_BUTTON_LABEL == michael@0: keyCharacterMap.getDisplayLabel(KeyEvent.KEYCODE_DPAD_CENTER)) { michael@0: swapped = true; michael@0: break; michael@0: } michael@0: } michael@0: return swapped; michael@0: } michael@0: }