|
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 package org.mozilla.gecko.util; |
|
7 |
|
8 import android.os.Build; |
|
9 import android.view.InputDevice; |
|
10 import android.view.KeyCharacterMap; |
|
11 import android.view.KeyEvent; |
|
12 import android.view.MotionEvent; |
|
13 import android.view.View; |
|
14 import android.widget.AdapterView; |
|
15 import android.widget.ListView; |
|
16 |
|
17 public final class GamepadUtils { |
|
18 private static final int SONY_XPERIA_GAMEPAD_DEVICE_ID = 196611; |
|
19 |
|
20 private static View.OnKeyListener sClickDispatcher; |
|
21 private static float sDeadZoneThresholdOverride = 1e-2f; |
|
22 |
|
23 private GamepadUtils() { |
|
24 } |
|
25 |
|
26 private static boolean isGamepadKey(KeyEvent event) { |
|
27 if (Build.VERSION.SDK_INT >= 9) { |
|
28 return (event.getSource() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD; |
|
29 } |
|
30 return false; |
|
31 } |
|
32 |
|
33 public static boolean isActionKey(KeyEvent event) { |
|
34 return (isGamepadKey(event) && (event.getKeyCode() == KeyEvent.KEYCODE_BUTTON_A)); |
|
35 } |
|
36 |
|
37 public static boolean isActionKeyDown(KeyEvent event) { |
|
38 return isActionKey(event) && event.getAction() == KeyEvent.ACTION_DOWN; |
|
39 } |
|
40 |
|
41 public static boolean isBackKey(KeyEvent event) { |
|
42 return (isGamepadKey(event) && (event.getKeyCode() == KeyEvent.KEYCODE_BUTTON_B)); |
|
43 } |
|
44 |
|
45 public static void overrideDeadZoneThreshold(float threshold) { |
|
46 sDeadZoneThresholdOverride = threshold; |
|
47 } |
|
48 |
|
49 public static boolean isValueInDeadZone(MotionEvent event, int axis) { |
|
50 if (Build.VERSION.SDK_INT < 9) { |
|
51 return false; |
|
52 } |
|
53 |
|
54 float threshold; |
|
55 if (sDeadZoneThresholdOverride >= 0) { |
|
56 threshold = sDeadZoneThresholdOverride; |
|
57 } else { |
|
58 InputDevice.MotionRange range = event.getDevice().getMotionRange(axis); |
|
59 threshold = range.getFlat() + range.getFuzz(); |
|
60 } |
|
61 float value = event.getAxisValue(axis); |
|
62 return (Math.abs(value) < threshold); |
|
63 } |
|
64 |
|
65 public static boolean isPanningControl(MotionEvent event) { |
|
66 if (Build.VERSION.SDK_INT < 12) { |
|
67 return false; |
|
68 } |
|
69 if ((event.getSource() & InputDevice.SOURCE_CLASS_MASK) != InputDevice.SOURCE_CLASS_JOYSTICK) { |
|
70 return false; |
|
71 } |
|
72 if (isValueInDeadZone(event, MotionEvent.AXIS_X) |
|
73 && isValueInDeadZone(event, MotionEvent.AXIS_Y) |
|
74 && isValueInDeadZone(event, MotionEvent.AXIS_Z) |
|
75 && isValueInDeadZone(event, MotionEvent.AXIS_RZ)) { |
|
76 return false; |
|
77 } |
|
78 return true; |
|
79 } |
|
80 |
|
81 public static View.OnKeyListener getClickDispatcher() { |
|
82 if (sClickDispatcher == null) { |
|
83 sClickDispatcher = new View.OnKeyListener() { |
|
84 @Override |
|
85 public boolean onKey(View v, int keyCode, KeyEvent event) { |
|
86 if (isActionKeyDown(event)) { |
|
87 return v.performClick(); |
|
88 } |
|
89 return false; |
|
90 } |
|
91 }; |
|
92 } |
|
93 return sClickDispatcher; |
|
94 } |
|
95 |
|
96 public static KeyEvent translateSonyXperiaGamepadKeys(int keyCode, KeyEvent event) { |
|
97 // The cross and circle button mappings may be swapped in the different regions so |
|
98 // determine if they are swapped so the proper key codes can be mapped to the keys |
|
99 boolean areKeysSwapped = areSonyXperiaGamepadKeysSwapped(); |
|
100 |
|
101 // If a Sony Xperia, remap the cross and circle buttons to buttons |
|
102 // A and B for the gamepad API |
|
103 switch (keyCode) { |
|
104 case KeyEvent.KEYCODE_BACK: |
|
105 keyCode = (areKeysSwapped ? KeyEvent.KEYCODE_BUTTON_A : KeyEvent.KEYCODE_BUTTON_B); |
|
106 break; |
|
107 |
|
108 case KeyEvent.KEYCODE_DPAD_CENTER: |
|
109 keyCode = (areKeysSwapped ? KeyEvent.KEYCODE_BUTTON_B : KeyEvent.KEYCODE_BUTTON_A); |
|
110 break; |
|
111 |
|
112 default: |
|
113 return event; |
|
114 } |
|
115 |
|
116 return new KeyEvent(event.getAction(), keyCode); |
|
117 } |
|
118 |
|
119 public static boolean isSonyXperiaGamepadKeyEvent(KeyEvent event) { |
|
120 return (event.getDeviceId() == SONY_XPERIA_GAMEPAD_DEVICE_ID && |
|
121 "Sony Ericsson".equals(Build.MANUFACTURER) && |
|
122 ("R800".equals(Build.MODEL) || "R800i".equals(Build.MODEL))); |
|
123 } |
|
124 |
|
125 private static boolean areSonyXperiaGamepadKeysSwapped() { |
|
126 // The cross and circle buttons on Sony Xperia phones are swapped |
|
127 // in different regions |
|
128 // http://developer.sonymobile.com/2011/02/13/xperia-play-game-keys/ |
|
129 final char DEFAULT_O_BUTTON_LABEL = 0x25CB; |
|
130 |
|
131 boolean swapped = false; |
|
132 int[] deviceIds = InputDevice.getDeviceIds(); |
|
133 |
|
134 for (int i= 0; deviceIds != null && i < deviceIds.length; i++) { |
|
135 KeyCharacterMap keyCharacterMap = KeyCharacterMap.load(deviceIds[i]); |
|
136 if (keyCharacterMap != null && DEFAULT_O_BUTTON_LABEL == |
|
137 keyCharacterMap.getDisplayLabel(KeyEvent.KEYCODE_DPAD_CENTER)) { |
|
138 swapped = true; |
|
139 break; |
|
140 } |
|
141 } |
|
142 return swapped; |
|
143 } |
|
144 } |