Last Comment Bug 790775 - Sony Ericsson Xperia Play's "circle" and "cross" D-pad buttons generate unexpected keycodes
: Sony Ericsson Xperia Play's "circle" and "cross" D-pad buttons generate unexp...
Status: RESOLVED FIXED
[mentor=cpeterson][lang=java]
:
Product: Firefox for Android
Classification: Client Software
Component: Keyboards and IME (show other bugs)
: Trunk
: ARM Android
: P4 normal (vote)
: Firefox 24
Assigned To: Shane Tully (:stully:
:
:
Mentors:
Depends on: 604039
Blocks:
  Show dependency treegraph
 
Reported: 2012-09-12 15:20 PDT by Chris Peterson [:cpeterson]
Modified: 2013-05-23 23:29 PDT (History)
4 users (show)
See Also:
Crash Signature:
(edit)
QA Whiteboard:
Iteration: ---
Points: ---
Has Regression Range: ---
Has STR: ---
affected
wontfix
fixed


Attachments
Remap the circle and cross buttons to KEYCODE_BUTTON_A or KEYCODE_BUTTON_B depending on region (4.74 KB, patch)
2013-05-21 18:14 PDT, Shane Tully (:stully:
cpeterson: feedback+
Details | Diff | Splinter Review
02: Remap the circle and cross buttons to KEYCODE_BUTTON_A or KEYCODE_BUTTON_B depending on region (5.11 KB, patch)
2013-05-22 10:59 PDT, Shane Tully (:stully:
cpeterson: feedback+
Details | Diff | Splinter Review
03: Remap the circle and cross buttons to KEYCODE_BUTTON_A or KEYCODE_BUTTON_B depending on region (4.95 KB, patch)
2013-05-22 12:29 PDT, Shane Tully (:stully:
cpeterson: review+
Details | Diff | Splinter Review

Description Chris Peterson [:cpeterson] 2012-09-12 15:20:56 PDT
The Sony Ericsson Xperia Play's D-pad button layout (unsurprisingly) matches the PlayStation game controller.

AFAIK, the expected mapping from PlayStation to Xbox controller buttons is:

  PS Cross    -> Xbox A
  PS Circle   -> Xbox B
  PS Square   -> Xbox X
  PS Triangle -> Xbox Y

The actual keycodes generated by the Xperia Play buttons is:

  Cross    -> Expected KEYCODE_BUTTON_A; got KEYCODE_DPAD_CENTER (keycode 23)
  Circle   -> Expected KEYCODE_BUTTON_B; got META_ALT_ON + KEYCODE_BACK (keycode 4)
  Square   -> Expected and got KEYCODE_BUTTON_X (keycode 99)
  Triangle -> Expected and got KEYCODE_BUTTON_Y (keycode 100)
Comment 1 Chris Peterson [:cpeterson] 2013-03-06 05:15:06 PST
Chris, you may be interested in this bug. :)
Comment 2 Chris Lord [:cwiiis] 2013-03-06 05:20:38 PST
If we think of X as 'select' and O as 'back' (which it is in Western games - interestingly, O is 'select' and I think triangle or X is back in Japan...), then the mappings make some sense... Still a bit annoying, but we should take it into account as many bluetooth gamepads emulate the Xperia Play key codes.
Comment 3 Chris Peterson [:cpeterson] 2013-03-06 05:53:02 PST
If the Gamepad API defines gamepad button events, we should ensure that we map these Java keycodes to the correct gamepad codes.

https://wiki.mozilla.org/GamepadAPI#Gamepad_Button_Events
Comment 4 Chris Peterson [:cpeterson] 2013-05-21 13:52:42 PDT
This is a good first bug.
Comment 5 Shane Tully (:stully: 2013-05-21 18:14:52 PDT
Created attachment 752466 [details] [diff] [review]
Remap the circle and cross buttons to KEYCODE_BUTTON_A or KEYCODE_BUTTON_B depending on region

The attached patch remaps the cross key to KEYCODE_BUTTON_A and the circle key to KEYCODE_BUTTON_B or vice verse if the keys are swapped (for devices in certain regions)
Comment 6 Chris Peterson [:cpeterson] 2013-05-21 18:36:36 PDT
Comment on attachment 752466 [details] [diff] [review]
Remap the circle and cross buttons to KEYCODE_BUTTON_A or KEYCODE_BUTTON_B depending on region

Review of attachment 752466 [details] [diff] [review]:
-----------------------------------------------------------------

Looking good. Here are some suggestions:

::: mobile/android/base/GeckoInputConnection.java
@@ +24,5 @@
>  import android.util.Log;
>  import android.view.KeyEvent;
>  import android.view.View;
> +import android.view.InputDevice;
> +import android.view.KeyCharacterMap;

Please alphabetize these new android.view.* imports.

@@ +759,5 @@
>          }
>          return false;
>      }
>  
> +    private KeyEvent translateSonyXperiaGamepad(int keyCode, KeyEvent event) {

I would rename this method to something like `translateSonyXperiaGamepadKey()`.

@@ +762,5 @@
>  
> +    private KeyEvent translateSonyXperiaGamepad(int keyCode, KeyEvent event) {
> +        // The cross and circle button mappings may be swapped in the different
> +        // regions so determine if they are swapped so the proper key codes
> +        // can be mapped to the keys

Mozilla's C++ coding style requires 80 column lines, but we allow Java files to use 100 columns (because Java code is so verbose). So you can use wider comments, if you like.

@@ +764,5 @@
> +        // The cross and circle button mappings may be swapped in the different
> +        // regions so determine if they are swapped so the proper key codes
> +        // can be mapped to the keys
> +        boolean areKeysSwapped = areSonyXperiaGamepadKeysSwapped();
> +        

Please remove trailing whitespace.

@@ +768,5 @@
> +        
> +        // If a Sony Xperia, remap the cross and circle buttons to buttons
> +        // A and B for the gamepad API
> +        int newKeyCode = keyCode;
> +        switch(keyCode) {

Please insert a space between before the first paren.

@@ +778,5 @@
> +                newKeyCode = (areKeysSwapped ? KeyEvent.KEYCODE_BUTTON_B : KeyEvent.KEYCODE_BUTTON_A);
> +                break;
> +        }
> +
> +        return new KeyEvent(event.getAction(), newKeyCode);

Instead of allocating a new KeyEvent every time, let's add a default case to the switch statement that just returns the original `event` parameter. You will probably be able to skip the initialization of `newKeyCode = keyCode` (because the compiler should be able tell that newKeyCode will always be initialized in every code path).

@@ +790,5 @@
> +
> +        boolean swapped = false;
> +        int[] ids = InputDevice.getDeviceIds();
> +
> +        for (int i= 0; ids != null && i<ids.length; i++) {

I know this code was written by someone else, but please add spaces before and after the '<' operator.

@@ +815,5 @@
>      }
>  
>      private boolean processKey(int keyCode, KeyEvent event, boolean down) {
> +        // If the key event came from a Sony Xperia keypad, remap the keys
> +        // if necessary

We can probably remove this comment because the translateSonyXperiaGamePad() method name is pretty self-explanatory. :)

@@ +816,5 @@
>  
>      private boolean processKey(int keyCode, KeyEvent event, boolean down) {
> +        // If the key event came from a Sony Xperia keypad, remap the keys
> +        // if necessary
> +        if (event.getDeviceId() == SONY_XPERIA_GAMEPAD_DEVICE_ID) {

I'm worried that some other (Sony) device might return the same device ID. I think we should double-check the android.os.Build.MANUFACTURER and MODEL, too. Check device ID first because it is cheap integer comparison.

Now that the Xperia device check is getting more complicated, I think we should extract that logic into a static helper method. Something like `private static boolean isSonyXperiaKeyEvent(KeyEvent event)`.
Comment 7 Shane Tully (:stully: 2013-05-22 10:59:29 PDT
Created attachment 752849 [details] [diff] [review]
02: Remap the circle and cross buttons to KEYCODE_BUTTON_A or KEYCODE_BUTTON_B depending on region

Revised patch with changes requested by :cpeterson
Comment 8 Kartikaya Gupta (email:kats@mozilla.com) 2013-05-22 11:30:46 PDT
Drive-by comment: If some of these new functions are meant to be reusable it might be better to put them in the util/GamepadUtils.java file. I'm not sure if that's more appropriate or not.
Comment 9 Chris Peterson [:cpeterson] 2013-05-22 11:52:06 PDT
Comment on attachment 752849 [details] [diff] [review]
02: Remap the circle and cross buttons to KEYCODE_BUTTON_A or KEYCODE_BUTTON_B depending on region

Review of attachment 752849 [details] [diff] [review]:
-----------------------------------------------------------------

kats makes a good point. Even though these new methods are specific to the Sony Xperia devices, I think we should move them to util/GamepadUtils.java. We can just expose isSonyXperiaGamepadKeyEvent() and translateSonyXperiaGamepadKeys() from GamepadUtils.java and encapsulate all the implementation details in private methods.

Plus, GeckoInputConnection.java is already a huge file, so moving code to other files is a good idea in general. :)

::: mobile/android/base/GeckoInputConnection.java
@@ +769,5 @@
> +        // A and B for the gamepad API
> +        switch (keyCode) {
> +            case KeyEvent.KEYCODE_BACK:
> +                keyCode = (areKeysSwapped ? KeyEvent.KEYCODE_BUTTON_A : 
> +                                            KeyEvent.KEYCODE_BUTTON_B);

Please remove the trailing whitespace at the end of the line and move the colon to the next line, indented to line up with the question mark. Mozilla coding style is to put && and || at the end of a continued line, but put other operators like + or - at the beginning of the continuing line.

@@ +774,5 @@
> +                break;
> +
> +            case KeyEvent.KEYCODE_DPAD_CENTER:
> +                keyCode = (areKeysSwapped ? KeyEvent.KEYCODE_BUTTON_B : 
> +                                            KeyEvent.KEYCODE_BUTTON_A);

Same as above.

@@ +805,5 @@
> +        return swapped;
> +    }
> +
> +    private static boolean isSonyXperiaGamepadKeyEvent(KeyEvent event) {
> +        return (event.getDeviceId() == SONY_XPERIA_GAMEPAD_DEVICE_ID && 

Please remove trailing whitespace.

@@ +807,5 @@
> +
> +    private static boolean isSonyXperiaGamepadKeyEvent(KeyEvent event) {
> +        return (event.getDeviceId() == SONY_XPERIA_GAMEPAD_DEVICE_ID && 
> +                android.os.Build.MANUFACTURER.equals("Sony Ericsson") &&
> +                (android.os.Build.MODEL.equals("R800") || android.os.Build.MODEL.equals("R800i")));

* You can just specify `Build.MODEL` instead of `android.os.Build.MODEL` because this file already imported android.os.Build above.

* Our Java coding style for string comparisons is to put the string constant first (like `"Sony Ericsson".equals(Build.MANUFACTURER)`. This style is a little ugly but it protects against NullPointerExceptions (because the equal() method checks for null) and may, in theory, allow Java to make some micro-optimization for the method call because the object, the string constant, is known statically at compile-time.
Comment 10 Shane Tully (:stully: 2013-05-22 12:29:43 PDT
Created attachment 752890 [details] [diff] [review]
03: Remap the circle and cross buttons to KEYCODE_BUTTON_A or KEYCODE_BUTTON_B depending on region


Revised patch with changes requested by :cpeterson and :kats
Comment 11 Chris Peterson [:cpeterson] 2013-05-22 12:35:38 PDT
Comment on attachment 752890 [details] [diff] [review]
03: Remap the circle and cross buttons to KEYCODE_BUTTON_A or KEYCODE_BUTTON_B depending on region

Review of attachment 752890 [details] [diff] [review]:
-----------------------------------------------------------------

Looks good to me! I can land your patch on mozilla-inbound today.
Comment 12 Chris Peterson [:cpeterson] 2013-05-22 14:09:08 PDT
I landed stully's patch on mozilla-inbound:
https://hg.mozilla.org/integration/mozilla-inbound/rev/cd0d9ac28abb
Comment 13 Ed Morley [:emorley] 2013-05-23 04:55:33 PDT
https://hg.mozilla.org/mozilla-central/rev/cd0d9ac28abb

Note You need to log in before you can comment on or make changes to this bug.