summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GamepadUtils.java
blob: e22be8fd8c78f151a0dacf5145d0936ca6575cfd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.gecko.util;

import android.annotation.TargetApi;
import android.os.Build;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;

public final class GamepadUtils {
    private static final int SONY_XPERIA_GAMEPAD_DEVICE_ID = 196611;

    private static View.OnKeyListener sClickDispatcher;
    private static float sDeadZoneThresholdOverride = 1e-2f;

    private GamepadUtils() {
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
    private static boolean isGamepadKey(KeyEvent event) {
        if (Build.VERSION.SDK_INT < 12) {
            return false;
        }
        return (event.getSource() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD;
    }

    public static boolean isActionKey(KeyEvent event) {
        return (isGamepadKey(event) && (event.getKeyCode() == KeyEvent.KEYCODE_BUTTON_A));
    }

    public static boolean isActionKeyDown(KeyEvent event) {
        return isActionKey(event) && event.getAction() == KeyEvent.ACTION_DOWN;
    }

    public static boolean isBackKey(KeyEvent event) {
        return (isGamepadKey(event) && (event.getKeyCode() == KeyEvent.KEYCODE_BUTTON_B));
    }

    public static void overrideDeadZoneThreshold(float threshold) {
        sDeadZoneThresholdOverride = threshold;
    }

    public static boolean isValueInDeadZone(MotionEvent event, int axis) {
        float threshold;
        if (sDeadZoneThresholdOverride >= 0) {
            threshold = sDeadZoneThresholdOverride;
        } else {
            InputDevice.MotionRange range = event.getDevice().getMotionRange(axis);
            threshold = range.getFlat() + range.getFuzz();
        }
        float value = event.getAxisValue(axis);
        return (Math.abs(value) < threshold);
    }

    public static boolean isPanningControl(MotionEvent event) {
        if (Build.VERSION.SDK_INT < 12) {
            return false;
        }
        if ((event.getSource() & InputDevice.SOURCE_CLASS_MASK) != InputDevice.SOURCE_CLASS_JOYSTICK) {
            return false;
        }
        if (isValueInDeadZone(event, MotionEvent.AXIS_X)
                && isValueInDeadZone(event, MotionEvent.AXIS_Y)
                && isValueInDeadZone(event, MotionEvent.AXIS_Z)
                && isValueInDeadZone(event, MotionEvent.AXIS_RZ)) {
            return false;
        }
        return true;
    }

    public static View.OnKeyListener getClickDispatcher() {
        if (sClickDispatcher == null) {
            sClickDispatcher = new View.OnKeyListener() {
                @Override
                public boolean onKey(View v, int keyCode, KeyEvent event) {
                    if (isActionKeyDown(event)) {
                        return v.performClick();
                    }
                    return false;
                }
            };
        }
        return sClickDispatcher;
    }

    public static KeyEvent translateSonyXperiaGamepadKeys(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
        boolean areKeysSwapped = areSonyXperiaGamepadKeysSwapped();

        // If a Sony Xperia, remap the cross and circle buttons to buttons
        // A and B for the gamepad API
        switch (keyCode) {
            case KeyEvent.KEYCODE_BACK:
                keyCode = (areKeysSwapped ? KeyEvent.KEYCODE_BUTTON_A : KeyEvent.KEYCODE_BUTTON_B);
                break;

            case KeyEvent.KEYCODE_DPAD_CENTER:
                keyCode = (areKeysSwapped ? KeyEvent.KEYCODE_BUTTON_B : KeyEvent.KEYCODE_BUTTON_A);
                break;

            default:
                return event;
        }

        return new KeyEvent(event.getAction(), keyCode);
    }

    public static boolean isSonyXperiaGamepadKeyEvent(KeyEvent event) {
        return (event.getDeviceId() == SONY_XPERIA_GAMEPAD_DEVICE_ID &&
                "Sony Ericsson".equals(Build.MANUFACTURER) &&
                ("R800".equals(Build.MODEL) || "R800i".equals(Build.MODEL)));
    }

    private static boolean areSonyXperiaGamepadKeysSwapped() {
        // The cross and circle buttons on Sony Xperia phones are swapped
        // in different regions
        // http://developer.sonymobile.com/2011/02/13/xperia-play-game-keys/
        final char DEFAULT_O_BUTTON_LABEL = 0x25CB;

        boolean swapped = false;
        int[] deviceIds = InputDevice.getDeviceIds();

        for (int i = 0; deviceIds != null && i < deviceIds.length; i++) {
            KeyCharacterMap keyCharacterMap = KeyCharacterMap.load(deviceIds[i]);
            if (keyCharacterMap != null && DEFAULT_O_BUTTON_LABEL ==
                keyCharacterMap.getDisplayLabel(KeyEvent.KEYCODE_DPAD_CENTER)) {
                swapped = true;
                break;
            }
        }
        return swapped;
    }
}