summaryrefslogtreecommitdiff
path: root/Source/Android/app/src/main/java
diff options
context:
space:
mode:
authorRyan Houdek <Sonicadvance1@gmail.com>2016-02-04 18:31:36 -0600
committerRyan Houdek <Sonicadvance1@gmail.com>2016-02-07 05:31:48 -0600
commitfe53461611d86384478064be6b4297a7c7e437e1 (patch)
tree67ac77af74e6ca92a5159ea90b1e2025f4a95427 /Source/Android/app/src/main/java
parent2282651fdb7bee30694f03cac74c379335655fad (diff)
[Android] Implement support for real Wiimotes with the DolphinBar
This is the only way to get Wiimotes working under Android now. This, just like the Wii U Gamecube Controller Adapter, completely goes around Android's limitations and talks with the device directly through USBManager. Couple notes. Continuous scanning must be enabled otherwise the Wiimotes won't be seen. The UI doesn't expose support for this yet. One must change the Wiimote source and continuous scanning settings manually. Testing up to two wiimotes in Taiko No Tatsujin, no reason to believe all four won't work.
Diffstat (limited to 'Source/Android/app/src/main/java')
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java5
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java6
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter.java156
3 files changed, 167 insertions, 0 deletions
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java
index 38ea670f5e..992b670b1f 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java
@@ -380,6 +380,11 @@ public final class NativeLibrary
public static native void eglBindAPI(int api);
/**
+ * Provides a way to refresh the connections on Wiimotes
+ */
+ public static native void RefreshWiimotes();
+
+ /**
* The methods C++ uses to find references to Java classes and methods
* are really expensive. Rather than calling them every time we want to
* run them, do it once when we load the native library.
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java
index 2f38df3373..f90956dd9a 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java
@@ -33,6 +33,7 @@ import org.dolphinemu.dolphinemu.fragments.SaveStateFragment;
import org.dolphinemu.dolphinemu.ui.main.MainPresenter;
import org.dolphinemu.dolphinemu.utils.Animations;
import org.dolphinemu.dolphinemu.utils.Java_GCAdapter;
+import org.dolphinemu.dolphinemu.utils.Java_WiimoteAdapter;
import org.dolphinemu.dolphinemu.utils.Log;
import java.util.List;
@@ -120,6 +121,7 @@ public final class EmulationActivity extends AppCompatActivity
super.onCreate(savedInstanceState);
Java_GCAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE);
+ Java_WiimoteAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE);
// Picasso will take a while to load these big-ass screenshots. So don't run
// the animation until we say so.
@@ -392,6 +394,10 @@ public final class EmulationActivity extends AppCompatActivity
return;
}
+ case R.id.menu_refresh_wiimotes:
+ NativeLibrary.RefreshWiimotes();
+ return;
+
// Screenshot capturing
case R.id.menu_emulation_screenshot:
NativeLibrary.SaveScreenShot();
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter.java
new file mode 100644
index 0000000000..049f930cba
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter.java
@@ -0,0 +1,156 @@
+package org.dolphinemu.dolphinemu.utils;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.hardware.usb.UsbConfiguration;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+
+import org.dolphinemu.dolphinemu.NativeLibrary;
+import org.dolphinemu.dolphinemu.services.USBPermService;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+public class Java_WiimoteAdapter
+{
+ final static int MAX_PAYLOAD = 23;
+ final static int MAX_WIIMOTES = 4;
+ final static int TIMEOUT = 200;
+ final static short NINTENDO_VENDOR_ID = 0x057e;
+ final static short NINTENDO_WIIMOTE_PRODUCT_ID = 0x0306;
+ public static UsbManager manager;
+
+ static UsbDeviceConnection usb_con;
+ static UsbInterface[] usb_intf = new UsbInterface[MAX_WIIMOTES];
+ static UsbEndpoint[] usb_in = new UsbEndpoint[MAX_WIIMOTES];
+
+ public static byte[][] wiimote_payload = new byte[MAX_WIIMOTES][MAX_PAYLOAD];
+
+ private static void RequestPermission()
+ {
+ HashMap<String, UsbDevice> devices = manager.getDeviceList();
+ for (Map.Entry<String, UsbDevice> pair : devices.entrySet())
+ {
+ UsbDevice dev = (UsbDevice) pair.getValue();
+ if (dev.getProductId() == NINTENDO_WIIMOTE_PRODUCT_ID && dev.getVendorId() == NINTENDO_VENDOR_ID)
+ {
+ if (!manager.hasPermission(dev))
+ {
+ Log.warning("Requesting permission for Wiimote adapter");
+ Intent intent = new Intent();
+ PendingIntent pend_intent;
+ intent.setClass(NativeLibrary.sEmulationActivity, USBPermService.class);
+ pend_intent = PendingIntent.getService(NativeLibrary.sEmulationActivity, 0, intent, 0);
+ manager.requestPermission(dev, pend_intent);
+ }
+ }
+ }
+ }
+
+ public static boolean QueryAdapter()
+ {
+ HashMap<String, UsbDevice> devices = manager.getDeviceList();
+ for (Map.Entry<String, UsbDevice> pair : devices.entrySet())
+ {
+ UsbDevice dev = (UsbDevice) pair.getValue();
+ if (dev.getProductId() == NINTENDO_WIIMOTE_PRODUCT_ID && dev.getVendorId() == NINTENDO_VENDOR_ID)
+ {
+ if (manager.hasPermission(dev))
+ return true;
+ else
+ RequestPermission();
+ }
+ }
+ return false;
+ }
+
+ public static int Input(int index)
+ {
+ return usb_con.bulkTransfer(usb_in[index], wiimote_payload[index], MAX_PAYLOAD, TIMEOUT);
+ }
+
+ public static int Output(int index, byte[] buf, int size)
+ {
+ byte report_number = buf[0];
+
+ // Remove the report number from the buffer
+ buf = Arrays.copyOfRange(buf, 1, buf.length);
+ size--;
+
+ final int LIBUSB_REQUEST_TYPE_CLASS = (1 << 5);
+ final int LIBUSB_RECIPIENT_INTERFACE = 0x1;
+ final int LIBUSB_ENDPOINT_OUT = 0;
+
+ final int HID_SET_REPORT = 0x9;
+ final int HID_OUTPUT = (2 << 8);
+
+ int write = usb_con.controlTransfer(
+ LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT,
+ HID_SET_REPORT,
+ HID_OUTPUT | report_number,
+ index,
+ buf, size,
+ 1000);
+
+ if (write < 0)
+ return -1;
+
+ return write + 1;
+ }
+
+ public static boolean OpenAdapter()
+ {
+ // If the adapter is already open. Don't attempt to do it again
+ if (usb_con != null && usb_con.getFileDescriptor() != -1)
+ return true;
+
+ HashMap<String, UsbDevice> devices = manager.getDeviceList();
+ for (Map.Entry<String, UsbDevice> pair : devices.entrySet())
+ {
+ UsbDevice dev = (UsbDevice) pair.getValue();
+ if (dev.getProductId() == NINTENDO_WIIMOTE_PRODUCT_ID && dev.getVendorId() == NINTENDO_VENDOR_ID)
+ {
+ if (manager.hasPermission(dev))
+ {
+ usb_con = manager.openDevice(dev);
+ UsbConfiguration conf = dev.getConfiguration(0);
+
+ Log.info("Number of configurations: " + dev.getConfigurationCount());
+ Log.info("Number of Interfaces: " + dev.getInterfaceCount());
+ Log.info("Number of Interfaces from conf: " + conf.getInterfaceCount());
+
+ // Sometimes the interface count is returned as zero.
+ // Means the device needs to be unplugged and plugged back in again
+ if (dev.getInterfaceCount() > 0)
+ {
+ for (int i = 0; i < MAX_WIIMOTES; ++i)
+ {
+ // One interface per Wiimote
+ usb_intf[i] = dev.getInterface(i);
+ usb_con.claimInterface(usb_intf[i], true);
+
+ // One endpoint per Wiimote. Input only
+ // Output reports go through the control channel.
+ usb_in[i] = usb_intf[i].getEndpoint(0);
+ Log.info("Interface " + i + " endpoint count:" + usb_intf[i].getEndpointCount());
+ }
+ return true;
+ }
+ else
+ {
+ // XXX: Message that the device was found, but it needs to be unplugged and plugged back in?
+ usb_con.close();
+ }
+ }
+ }
+ }
+ return false;
+ }
+}