summaryrefslogtreecommitdiff
path: root/Source/Android/app/src/main/java/org
diff options
context:
space:
mode:
authorSimonx22 <simon@oatmealdome.me>2026-04-06 18:26:19 -0400
committerSimonx22 <simon@oatmealdome.me>2026-04-06 19:56:27 -0400
commite982aeacad320509a9402e08b0dbb19ae97236ed (patch)
tree1f4d852399a89b3ba10e3660ae7b257ed878947e /Source/Android/app/src/main/java/org
parent5abf0e35560b3d2e1be0ad0db825e343c3e2c8fe (diff)
Android: Convert GCAdapter to Kotlin
Diffstat (limited to 'Source/Android/app/src/main/java/org')
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.java259
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.kt235
2 files changed, 235 insertions, 259 deletions
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.java
deleted file mode 100644
index b9e67e654f..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.java
+++ /dev/null
@@ -1,259 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.utils;
-
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.hardware.usb.UsbConfiguration;
-import android.hardware.usb.UsbConstants;
-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 android.os.Build;
-import android.widget.Toast;
-
-import androidx.annotation.Keep;
-import androidx.annotation.Nullable;
-import androidx.core.content.ContextCompat;
-
-import org.dolphinemu.dolphinemu.BuildConfig;
-import org.dolphinemu.dolphinemu.DolphinApplication;
-import org.dolphinemu.dolphinemu.R;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class GCAdapter
-{
- public static UsbManager manager;
-
- @Keep
- static byte[] controllerPayload = new byte[37];
-
- static UsbDeviceConnection usbConnection;
- static UsbInterface usbInterface;
- static UsbEndpoint usbIn;
- static UsbEndpoint usbOut;
-
- private static final String ACTION_GC_ADAPTER_PERMISSION_GRANTED =
- BuildConfig.APPLICATION_ID + ".GC_ADAPTER_PERMISSION_GRANTED";
-
- private static final Object hotplugCallbackLock = new Object();
- private static boolean hotplugCallbackEnabled = false;
- private static UsbDevice adapterDevice = null;
- private static BroadcastReceiver hotplugBroadcastReceiver = new BroadcastReceiver()
- {
- @Override
- public void onReceive(Context context, Intent intent)
- {
- onUsbDevicesChanged();
- }
- };
-
- private static void requestPermission()
- {
- HashMap<String, UsbDevice> devices = manager.getDeviceList();
- for (Map.Entry<String, UsbDevice> pair : devices.entrySet())
- {
- UsbDevice dev = pair.getValue();
- if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e)
- {
- if (!manager.hasPermission(dev))
- {
- Context context = DolphinApplication.getAppContext();
-
- int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ?
- PendingIntent.FLAG_IMMUTABLE : 0;
- PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
- new Intent(ACTION_GC_ADAPTER_PERMISSION_GRANTED), flags);
-
- manager.requestPermission(dev, pendingIntent);
- }
- }
- }
- }
-
- public static void shutdown()
- {
- usbConnection.close();
- }
-
- @Keep
- public static int getFd()
- {
- return usbConnection.getFileDescriptor();
- }
-
- @Keep
- public static boolean isUsbDeviceAvailable()
- {
- synchronized (hotplugCallbackLock)
- {
- return adapterDevice != null;
- }
- }
-
- @Nullable
- private static UsbDevice queryAdapter()
- {
- HashMap<String, UsbDevice> devices = manager.getDeviceList();
- for (Map.Entry<String, UsbDevice> pair : devices.entrySet())
- {
- UsbDevice dev = pair.getValue();
- if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e)
- {
- if (manager.hasPermission(dev))
- return dev;
- else
- requestPermission();
- }
- }
- return null;
- }
-
- public static void initAdapter()
- {
- byte[] init = {0x13};
- usbConnection.bulkTransfer(usbOut, init, init.length, 0);
- }
-
- @Keep
- public static int input()
- {
- return usbConnection.bulkTransfer(usbIn, controllerPayload, controllerPayload.length, 16);
- }
-
- @Keep
- public static int output(byte[] rumble)
- {
- return usbConnection.bulkTransfer(usbOut, rumble, 5, 16);
- }
-
- @Keep
- public static boolean openAdapter()
- {
- UsbDevice dev;
- synchronized (hotplugCallbackLock)
- {
- dev = adapterDevice;
- }
- if (dev == null)
- {
- return false;
- }
-
- usbConnection = manager.openDevice(dev);
- if (usbConnection == null)
- {
- return false;
- }
-
- Log.info("GCAdapter: Number of configurations: " + dev.getConfigurationCount());
- Log.info("GCAdapter: Number of interfaces: " + dev.getInterfaceCount());
-
- if (dev.getConfigurationCount() > 0 && dev.getInterfaceCount() > 0)
- {
- UsbConfiguration conf = dev.getConfiguration(0);
- usbInterface = conf.getInterface(0);
- usbConnection.claimInterface(usbInterface, true);
-
- Log.info("GCAdapter: Number of endpoints: " + usbInterface.getEndpointCount());
-
- if (usbInterface.getEndpointCount() == 2)
- {
- for (int i = 0; i < usbInterface.getEndpointCount(); ++i)
- if (usbInterface.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_IN)
- usbIn = usbInterface.getEndpoint(i);
- else
- usbOut = usbInterface.getEndpoint(i);
-
- initAdapter();
- return true;
- }
- else
- {
- usbConnection.releaseInterface(usbInterface);
- }
- }
-
- Toast.makeText(DolphinApplication.getAppContext(), R.string.replug_gc_adapter,
- Toast.LENGTH_LONG).show();
- usbConnection.close();
- return false;
- }
-
- @Keep
- public static void enableHotplugCallback()
- {
- synchronized (hotplugCallbackLock)
- {
- if (hotplugCallbackEnabled)
- {
- throw new IllegalStateException("enableHotplugCallback was called when already enabled");
- }
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
- filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
- filter.addAction(ACTION_GC_ADAPTER_PERMISSION_GRANTED);
-
- ContextCompat.registerReceiver(DolphinApplication.getAppContext(), hotplugBroadcastReceiver,
- filter, ContextCompat.RECEIVER_EXPORTED);
-
- hotplugCallbackEnabled = true;
-
- onUsbDevicesChanged();
- }
- }
-
- @Keep
- public static void disableHotplugCallback()
- {
- synchronized (hotplugCallbackLock)
- {
- if (hotplugCallbackEnabled)
- {
- DolphinApplication.getAppContext().unregisterReceiver(hotplugBroadcastReceiver);
- hotplugCallbackEnabled = false;
- adapterDevice = null;
- }
- }
- }
-
- public static void onUsbDevicesChanged()
- {
- synchronized (hotplugCallbackLock)
- {
- if (adapterDevice != null)
- {
- boolean adapterStillConnected = manager.getDeviceList().entrySet().stream()
- .anyMatch(pair -> pair.getValue().getDeviceId() == adapterDevice.getDeviceId());
-
- if (!adapterStillConnected)
- {
- adapterDevice = null;
- onAdapterDisconnected();
- }
- }
-
- if (adapterDevice == null)
- {
- UsbDevice newAdapter = queryAdapter();
- if (newAdapter != null)
- {
- adapterDevice = newAdapter;
- onAdapterConnected();
- }
- }
- }
- }
-
- private static native void onAdapterConnected();
-
- private static native void onAdapterDisconnected();
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.kt
new file mode 100644
index 0000000000..a123810e2a
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.kt
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.utils
+
+import android.app.PendingIntent
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.hardware.usb.UsbConfiguration
+import android.hardware.usb.UsbConstants
+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 android.widget.Toast
+import androidx.annotation.Keep
+import androidx.core.content.ContextCompat
+import org.dolphinemu.dolphinemu.BuildConfig
+import org.dolphinemu.dolphinemu.DolphinApplication
+import org.dolphinemu.dolphinemu.R
+
+object GCAdapter {
+ lateinit var manager: UsbManager
+
+ @Keep
+ @JvmField
+ var controllerPayload = ByteArray(37)
+
+ private var usbConnection: UsbDeviceConnection? = null
+ private var usbInterface: UsbInterface? = null
+ private var usbIn: UsbEndpoint? = null
+ private var usbOut: UsbEndpoint? = null
+
+ private val ACTION_GC_ADAPTER_PERMISSION_GRANTED =
+ BuildConfig.APPLICATION_ID + ".GC_ADAPTER_PERMISSION_GRANTED"
+
+ private val hotplugCallbackLock = Any()
+ private var hotplugCallbackEnabled = false
+ private var adapterDevice: UsbDevice? = null
+ private val hotplugBroadcastReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ onUsbDevicesChanged()
+ }
+ }
+
+ private fun requestPermission() {
+ val devices = manager.deviceList
+ for (dev in devices.values) {
+ if (dev.productId == 0x0337 && dev.vendorId == 0x057e) {
+ if (!manager.hasPermission(dev)) {
+ val context = DolphinApplication.getAppContext()
+
+ val pendingIntent = PendingIntent.getBroadcast(
+ context,
+ 0,
+ Intent(ACTION_GC_ADAPTER_PERMISSION_GRANTED),
+ PendingIntent.FLAG_IMMUTABLE
+ )
+
+ manager.requestPermission(dev, pendingIntent)
+ }
+ }
+ }
+ }
+
+ @JvmStatic
+ fun shutdown() {
+ usbConnection!!.close()
+ }
+
+ @Keep
+ @JvmStatic
+ fun getFd(): Int {
+ return usbConnection!!.fileDescriptor
+ }
+
+ @Keep
+ @JvmStatic
+ fun isUsbDeviceAvailable(): Boolean {
+ synchronized(hotplugCallbackLock) {
+ return adapterDevice != null
+ }
+ }
+
+ private fun queryAdapter(): UsbDevice? {
+ val devices = manager.deviceList
+ for (dev in devices.values) {
+ if (dev.productId == 0x0337 && dev.vendorId == 0x057e) {
+ if (manager.hasPermission(dev)) {
+ return dev
+ } else {
+ requestPermission()
+ }
+ }
+ }
+ return null
+ }
+
+ @JvmStatic
+ fun initAdapter() {
+ val init = byteArrayOf(0x13)
+ usbConnection!!.bulkTransfer(usbOut, init, init.size, 0)
+ }
+
+ @Keep
+ @JvmStatic
+ fun input(): Int {
+ return usbConnection!!.bulkTransfer(usbIn, controllerPayload, controllerPayload.size, 16)
+ }
+
+ @Keep
+ @JvmStatic
+ fun output(rumble: ByteArray): Int {
+ return usbConnection!!.bulkTransfer(usbOut, rumble, 5, 16)
+ }
+
+ @Keep
+ @JvmStatic
+ fun openAdapter(): Boolean {
+ val dev: UsbDevice?
+ synchronized(hotplugCallbackLock) {
+ dev = adapterDevice
+ }
+ if (dev == null) {
+ return false
+ }
+
+ usbConnection = manager.openDevice(dev)
+ if (usbConnection == null) {
+ return false
+ }
+
+ Log.info("GCAdapter: Number of configurations: " + dev.configurationCount)
+ Log.info("GCAdapter: Number of interfaces: " + dev.interfaceCount)
+
+ if (dev.configurationCount > 0 && dev.interfaceCount > 0) {
+ val conf: UsbConfiguration = dev.getConfiguration(0)
+ usbInterface = conf.getInterface(0)
+ usbConnection!!.claimInterface(usbInterface, true)
+
+ Log.info("GCAdapter: Number of endpoints: " + usbInterface!!.endpointCount)
+
+ if (usbInterface!!.endpointCount == 2) {
+ for (i in 0 until usbInterface!!.endpointCount) {
+ if (usbInterface!!.getEndpoint(i).direction == UsbConstants.USB_DIR_IN) {
+ usbIn = usbInterface!!.getEndpoint(i)
+ } else {
+ usbOut = usbInterface!!.getEndpoint(i)
+ }
+ }
+
+ initAdapter()
+ return true
+ } else {
+ usbConnection!!.releaseInterface(usbInterface)
+ }
+ }
+
+ Toast.makeText(
+ DolphinApplication.getAppContext(), R.string.replug_gc_adapter, Toast.LENGTH_LONG
+ ).show()
+ usbConnection!!.close()
+ return false
+ }
+
+ @Keep
+ @JvmStatic
+ fun enableHotplugCallback() {
+ synchronized(hotplugCallbackLock) {
+ if (hotplugCallbackEnabled) {
+ throw IllegalStateException("enableHotplugCallback was called when already enabled")
+ }
+
+ val filter = IntentFilter()
+ filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED)
+ filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED)
+ filter.addAction(ACTION_GC_ADAPTER_PERMISSION_GRANTED)
+
+ ContextCompat.registerReceiver(
+ DolphinApplication.getAppContext(),
+ hotplugBroadcastReceiver,
+ filter,
+ ContextCompat.RECEIVER_EXPORTED
+ )
+
+ hotplugCallbackEnabled = true
+
+ onUsbDevicesChanged()
+ }
+ }
+
+ @Keep
+ @JvmStatic
+ fun disableHotplugCallback() {
+ synchronized(hotplugCallbackLock) {
+ if (hotplugCallbackEnabled) {
+ DolphinApplication.getAppContext().unregisterReceiver(hotplugBroadcastReceiver)
+ hotplugCallbackEnabled = false
+ adapterDevice = null
+ }
+ }
+ }
+
+ @JvmStatic
+ fun onUsbDevicesChanged() {
+ synchronized(hotplugCallbackLock) {
+ if (adapterDevice != null) {
+ val adapterStillConnected = manager.deviceList.values.any {
+ it.deviceId == adapterDevice!!.deviceId
+ }
+
+ if (!adapterStillConnected) {
+ adapterDevice = null
+ onAdapterDisconnected()
+ }
+ }
+
+ if (adapterDevice == null) {
+ val newAdapter = queryAdapter()
+ if (newAdapter != null) {
+ adapterDevice = newAdapter
+ onAdapterConnected()
+ }
+ }
+ }
+ }
+
+ @JvmStatic
+ private external fun onAdapterConnected()
+
+ @JvmStatic
+ private external fun onAdapterDisconnected()
+}