Closed Bug 790775 Opened 7 years ago Closed 6 years ago

Sony Ericsson Xperia Play's "circle" and "cross" D-pad buttons generate unexpected keycodes

Categories

(Firefox for Android :: Keyboards and IME, defect, P4)

ARM
Android
defect

Tracking

()

RESOLVED FIXED
Firefox 24
Tracking Status
firefox18 --- affected
firefox23 --- wontfix
firefox24 --- fixed

People

(Reporter: cpeterson, Assigned: stully)

References

Details

(Whiteboard: [mentor=cpeterson][lang=java])

Attachments

(1 file, 2 obsolete files)

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)
Summary: Son 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 unexpected keycodes
Whiteboard: [mentor=cpeterson][lang=java]
Version: unspecified → Trunk
Chris, you may be interested in this bug. :)
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.
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
Depends on: 604039
Priority: P3 → P4
This is a good first bug.
Assignee: nobody → stully
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 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)`.
Attachment #752466 - Flags: feedback+
Revised patch with changes requested by :cpeterson
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.
Attachment #752466 - Attachment is obsolete: true
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.
Attachment #752849 - Flags: feedback+
Revised patch with changes requested by :cpeterson and :kats
Attachment #752890 - Flags: review?(cpeterson)
Attachment #752849 - Attachment is obsolete: true
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.
Attachment #752890 - Flags: review?(cpeterson) → review+
https://hg.mozilla.org/mozilla-central/rev/cd0d9ac28abb
Status: NEW → RESOLVED
Closed: 6 years ago
Resolution: --- → FIXED
Target Milestone: --- → Firefox 24
You need to log in before you can comment on or make changes to this bug.