Bug 1289208 - Add native methods to AndroidGamepadManager; r=snorp
authorJim Chen <nchen@mozilla.com>
Thu, 04 Aug 2016 09:16:05 -0400
changeset 308203 c016f9d74449dbafc76c8bcd207c37794f72cb56
parent 308202 10231cdd4c76ed742f12b021547768817bf4e748
child 308204 86b4155ce94b3ac028dc4c62a62452f439e1a20b
push id31092
push usercbook@mozilla.com
push dateFri, 05 Aug 2016 10:16:59 +0000
treeherderautoland@b97dd7dd3cb9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1289208
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1289208 - Add native methods to AndroidGamepadManager; r=snorp Add native method calls to AndroidGamepadManager to replace the gamepad events in GeckoEvent. Implement those calls in AndroidGamepad.cpp. The jni/Refs.h change is necessary to fix a compile error when using jni::BooleanArray.
dom/gamepad/android/AndroidGamepad.cpp
mobile/android/geckoview/src/main/java/org/mozilla/gecko/AndroidGamepadManager.java
widget/android/jni/Refs.h
--- a/dom/gamepad/android/AndroidGamepad.cpp
+++ b/dom/gamepad/android/AndroidGamepad.cpp
@@ -1,21 +1,89 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #include "GeneratedJNIWrappers.h"
+#include "GeneratedJNINatives.h"
+#include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace dom {
 
+class AndroidGamepadManager final
+  : public java::AndroidGamepadManager::Natives<AndroidGamepadManager>
+  , public jni::UsesNativeCallProxy
+{
+  AndroidGamepadManager() = delete;
+
+public:
+  template<class Functor>
+  static void OnNativeCall(Functor&& aCall)
+  {
+    NS_DispatchToMainThread(NS_NewRunnableFunction(Move(aCall)));
+  }
+
+  static void
+  OnGamepadChange(int32_t aID, bool aAdded)
+  {
+    RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+    if (!service) {
+      return;
+    }
+
+    if (aAdded) {
+      const int svc_id = service->AddGamepad(
+          "android", GamepadMappingType::Standard,
+          kStandardGamepadButtons, kStandardGamepadAxes);
+      java::AndroidGamepadManager::OnGamepadAdded(aID, svc_id);
+
+    } else {
+      service->RemoveGamepad(aID);
+    }
+  }
+
+  static void
+  OnButtonChange(int32_t aID, int32_t aButton, bool aPressed, float aValue)
+  {
+    RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+    if (!service) {
+      return;
+    }
+
+    service->NewButtonEvent(aID, aButton, aPressed, aValue);
+  }
+
+  static void
+  OnAxisChange(int32_t aID, jni::BooleanArray::Param aValid,
+               jni::FloatArray::Param aValues)
+  {
+    RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+    if (!service) {
+      return;
+    }
+
+    const auto& valid = aValid->GetElements();
+    const auto& values = aValues->GetElements();
+    MOZ_ASSERT(valid.Length() == values.Length());
+
+    for (size_t i = 0; i < values.Length(); i++) {
+      service->NewAxisMoveEvent(aID, i, values[i]);
+    }
+  }
+};
+
 void StartGamepadMonitoring()
 {
+  AndroidGamepadManager::Init();
   java::AndroidGamepadManager::Start();
 }
 
 void StopGamepadMonitoring()
 {
   java::AndroidGamepadManager::Stop();
 }
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/AndroidGamepadManager.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/AndroidGamepadManager.java
@@ -6,16 +6,17 @@
 package org.mozilla.gecko;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Timer;
 import java.util.TimerTask;
 
 import org.mozilla.gecko.AppConstants.Versions;
+import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.util.GamepadUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.content.Context;
 import android.hardware.input.InputManager;
 import android.util.SparseArray;
 import android.view.InputDevice;
 import android.view.KeyEvent;
@@ -123,16 +124,23 @@ public class AndroidGamepadManager {
                                             MotionEvent.AXIS_GAS};
                 } else {
                     triggerAxes = null;
                 }
             }
         }
     }
 
+    @WrapForJNI
+    private static native void onGamepadChange(int id, boolean added);
+    @WrapForJNI
+    private static native void onButtonChange(int id, int button, boolean pressed, float value);
+    @WrapForJNI
+    private static native void onAxisChange(int id, boolean[] valid, float[] values);
+
     private static boolean sStarted;
     private static final SparseArray<Gamepad> sGamepads = new SparseArray<>();
     private static final SparseArray<List<KeyEvent>> sPendingGamepads = new SparseArray<>();
     private static InputManager.InputDeviceListener sListener;
     private static Timer sPollTimer;
 
     private AndroidGamepadManager() {
     }
@@ -214,17 +222,17 @@ public class AndroidGamepadManager {
     }
 
     private static void mapDpadAxis(Gamepad gamepad,
                                     boolean pressed,
                                     float value,
                                     int which) {
         if (pressed != gamepad.dpad[which]) {
             gamepad.dpad[which] = pressed;
-            GeckoAppShell.sendEventToGecko(GeckoEvent.createGamepadButtonEvent(gamepad.id, FIRST_DPAD_BUTTON + which, pressed, Math.abs(value)));
+            onButtonChange(gamepad.id, FIRST_DPAD_BUTTON + which, pressed, Math.abs(value));
         }
     }
 
     public static boolean handleMotionEvent(MotionEvent ev) {
         ThreadUtils.assertOnUiThread();
         if (!sStarted) {
             return false;
         }
@@ -246,29 +254,29 @@ public class AndroidGamepadManager {
                 axes[i] = value;
                 gamepad.axes[i] = value;
                 valid[i] = true;
                 anyValidAxes = true;
             }
         }
         if (anyValidAxes) {
             // Send an axismove event.
-            GeckoAppShell.sendEventToGecko(GeckoEvent.createGamepadAxisEvent(gamepad.id, valid, axes));
+            onAxisChange(gamepad.id, valid, axes);
         }
 
         // Map triggers to buttons.
         if (gamepad.triggerAxes != null) {
             for (Trigger trigger : Trigger.values()) {
                 int i = trigger.ordinal();
                 int axis = gamepad.triggerAxes[i];
                 float value = deadZone(ev, axis);
                 if (value != gamepad.triggers[i]) {
                     gamepad.triggers[i] = value;
                     boolean pressed = value > TRIGGER_PRESSED_THRESHOLD;
-                    GeckoAppShell.sendEventToGecko(GeckoEvent.createGamepadButtonEvent(gamepad.id, trigger.button, pressed, value));
+                    onButtonChange(gamepad.id, trigger.button, pressed, value);
                 }
             }
         }
         // Map d-pad to buttons.
         for (DpadAxis dpadaxis : DpadAxis.values()) {
             float value = deadZone(ev, dpadaxis.axis);
             mapDpadAxis(gamepad, value < 0.0f, value, dpadaxis.negativeButton);
             mapDpadAxis(gamepad, value > 0.0f, value, dpadaxis.positiveButton);
@@ -317,17 +325,17 @@ public class AndroidGamepadManager {
         if (ev.getRepeatCount() > 0) {
             // We would handle this key, but we're not interested in
             // repeats. Eat it.
             return true;
         }
 
         Gamepad gamepad = sGamepads.get(deviceId);
         boolean pressed = ev.getAction() == KeyEvent.ACTION_DOWN;
-        GeckoAppShell.sendEventToGecko(GeckoEvent.createGamepadButtonEvent(gamepad.id, key, pressed, pressed ? 1.0f : 0.0f));
+        onButtonChange(gamepad.id, key, pressed, pressed ? 1.0f : 0.0f);
         return true;
     }
 
     private static void scanForGamepads() {
         int[] deviceIds = InputDevice.getDeviceIds();
         if (deviceIds == null) {
             return;
         }
@@ -339,28 +347,23 @@ public class AndroidGamepadManager {
             if ((device.getSources() & InputDevice.SOURCE_GAMEPAD) != InputDevice.SOURCE_GAMEPAD) {
                 continue;
             }
             addGamepad(device);
         }
     }
 
     private static void addGamepad(InputDevice device) {
-        //TODO: when we're using a newer SDK version, use these.
-        //if (Build.VERSION.SDK_INT >= 12) {
-        //int vid = device.getVendorId();
-        //int pid = device.getProductId();
-        //}
         sPendingGamepads.put(device.getId(), new ArrayList<KeyEvent>());
-        GeckoAppShell.sendEventToGecko(GeckoEvent.createGamepadAddRemoveEvent(device.getId(), true));
+        onGamepadChange(device.getId(), true);
     }
 
     private static void removeGamepad(int deviceId) {
         Gamepad gamepad = sGamepads.get(deviceId);
-        GeckoAppShell.sendEventToGecko(GeckoEvent.createGamepadAddRemoveEvent(gamepad.id, false));
+        onGamepadChange(gamepad.id, false);
         sGamepads.remove(deviceId);
     }
 
     private static void addDeviceListener() {
         if (Versions.preJB) {
             // Poll known gamepads to see if they've disappeared.
             sPollTimer = new Timer();
             sPollTimer.scheduleAtFixedRate(new TimerTask() {
--- a/widget/android/jni/Refs.h
+++ b/widget/android/jni/Refs.h
@@ -722,35 +722,41 @@ public:
     {
         const size_t ret = Base::Env()->GetArrayLength(Base::Instance());
         MOZ_CATCH_JNI_EXCEPTION(Base::Env());
         return ret;
     }
 
     ElementType GetElement(size_t index) const
     {
+        using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
+        static_assert(sizeof(ElementType) == sizeof(JNIElemType),
+                      "Size of native type must match size of JNI type");
+
         ElementType ret;
         (Base::Env()->*detail::TypeAdapter<ElementType>::GetArray)(
-                Base::Instance(), jsize(index), 1, &ret);
+                Base::Instance(), jsize(index), 1,
+                reinterpret_cast<JNIElemType*>(&ret));
         MOZ_CATCH_JNI_EXCEPTION(Base::Env());
         return ret;
     }
 
     nsTArray<ElementType> GetElements() const
     {
-        static_assert(sizeof(ElementType) ==
-                sizeof(typename detail::TypeAdapter<ElementType>::JNIType),
-                "Size of native type must match size of JNI type");
+        using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
+        static_assert(sizeof(ElementType) == sizeof(JNIElemType),
+                      "Size of native type must match size of JNI type");
 
         const jsize len = size_t(Base::Env()->GetArrayLength(Base::Instance()));
 
         nsTArray<ElementType> array((size_t(len)));
         array.SetLength(size_t(len));
         (Base::Env()->*detail::TypeAdapter<ElementType>::GetArray)(
-                Base::Instance(), 0, len, array.Elements());
+                Base::Instance(), 0, len,
+                reinterpret_cast<JNIElemType*>(array.Elements()));
         return array;
     }
 
     ElementType operator[](size_t index) const
     {
         return GetElement(index);
     }