Skip to content

Commit cf4cd83

Browse files
committed
my attempt 2
1 parent 2797e52 commit cf4cd83

File tree

4 files changed

+355
-0
lines changed

4 files changed

+355
-0
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,17 @@
2222
<uses-permission android:name="android.permission.RECORD_AUDIO" />
2323
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
2424
<uses-permission android:name="android.permission.VIBRATE" />
25+
<uses-permission android:name="android.permission.LIGHTS" />
2526

2627
<uses-feature
2728
android:name="android.hardware.wifi"
2829
android:required="false" />
2930
<uses-feature
3031
android:glEsVersion="0x00020000"
3132
android:required="true" />
33+
<uses-feature
34+
android:name="android.hardware.gamepad"
35+
android:required="false" />
3236

3337
<application
3438
android:allowBackup="true"

app/src/main/java/com/geode/launcher/GeometryDashActivity.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,10 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
592592
mGLSurfaceView?.sendInternalTimestampEvents = true
593593
true
594594
}
595+
GeodeUtils.CAPABILITY_CONTROLLER_DATA -> {
596+
mGLSurfaceView?.enableControllerEvents()
597+
true
598+
}
595599
else -> false
596600
}
597601
}

app/src/main/java/com/geode/launcher/utils/GeodeUtils.kt

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import android.content.ClipboardManager
77
import android.content.Context
88
import android.content.Intent
99
import android.content.pm.PackageManager
10+
import android.hardware.lights.Light
11+
import android.hardware.lights.LightState
12+
import android.hardware.lights.LightsRequest
1013
import android.net.Uri
1114
import android.os.Build
1215
import android.os.Environment
@@ -16,6 +19,7 @@ import android.os.VibratorManager
1619
import android.provider.DocumentsContract
1720
import android.provider.Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
1821
import android.util.Log
22+
import android.view.InputDevice
1923
import android.widget.Toast
2024
import androidx.activity.result.ActivityResultLauncher
2125
import androidx.activity.result.contract.ActivityResultContracts
@@ -547,6 +551,7 @@ object GeodeUtils {
547551
const val CAPABILITY_EXTENDED_INPUT = "extended_input"
548552
const val CAPABILITY_TIMESTAMP_INPUT = "timestamp_inputs"
549553
const val CAPABILITY_INTERNAL_CALLBACKS = "internal_callbacks_v1"
554+
const val CAPABILITY_CONTROLLER_DATA = "controller_data"
550555

551556
private var capabilityListener: WeakReference<CapabilityListener?> = WeakReference(null)
552557

@@ -576,6 +581,188 @@ object GeodeUtils {
576581
.launchUrl(activity, url.toUri())
577582
}
578583

584+
/**
585+
* Determines if any gamepad or joystick input devices are connected.
586+
* This is useful during start, but tracking inputDeviceAdded events is better during runtime.
587+
*/
588+
@JvmStatic
589+
fun controllersConnected(): Int {
590+
val deviceIds = InputDevice.getDeviceIds()
591+
return deviceIds.count {
592+
InputDevice.getDevice(it)?.run {
593+
sources and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD || sources and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK
594+
} ?: false
595+
}
596+
}
597+
598+
@JvmStatic
599+
fun getConnectedDevices(): IntArray = InputDevice.getDeviceIds()
600+
601+
@JvmStatic
602+
fun getDevice(deviceId: Int): InputDevice? = InputDevice.getDevice(deviceId)
603+
604+
@JvmStatic
605+
fun getDeviceBatteryCapacity(deviceId: Int): Float {
606+
val device = InputDevice.getDevice(deviceId) ?: return 0.0f
607+
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
608+
device.batteryState.capacity
609+
} else {
610+
0.0f
611+
}
612+
}
613+
614+
@JvmStatic
615+
fun deviceHasBattery(deviceId: Int): Boolean {
616+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
617+
return false
618+
}
619+
620+
val device = InputDevice.getDevice(deviceId) ?: return false
621+
return device.batteryState.isPresent
622+
}
623+
624+
@JvmStatic
625+
fun getDeviceBatteryStatus(deviceId: Int): Int {
626+
val device = InputDevice.getDevice(deviceId) ?: return 0
627+
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
628+
device.batteryState.status
629+
} else {
630+
0
631+
}
632+
}
633+
634+
@JvmStatic
635+
fun getDeviceLightsCount(deviceId: Int): Int {
636+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
637+
return 0
638+
}
639+
640+
val device = InputDevice.getDevice(deviceId) ?: return 0
641+
val lightsManager = device.lightsManager
642+
643+
return lightsManager.lights.size
644+
}
645+
646+
// this is based on paddleboat's api
647+
@JvmStatic
648+
fun getLightType(deviceId: Int): Int {
649+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
650+
return 0
651+
}
652+
653+
val device = InputDevice.getDevice(deviceId) ?: return 0
654+
val lightsManager = device.lightsManager
655+
656+
var flags = 0
657+
lightsManager.lights.forEach { light ->
658+
if (light.type == Light.LIGHT_TYPE_PLAYER_ID) {
659+
flags = flags or 1
660+
} else if (light.hasRgbControl()) {
661+
flags = flags or 2
662+
}
663+
}
664+
665+
return flags
666+
}
667+
668+
/**
669+
* Sets the color for a device.
670+
* @return false if the given device does not exist or setting lights was otherwise unsuccessful
671+
*/
672+
@JvmStatic
673+
fun setDeviceLightColor(deviceId: Int, color: Int, type: Int): Boolean {
674+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
675+
return false
676+
}
677+
678+
val device = InputDevice.getDevice(deviceId) ?: return false
679+
val lightsManager = device.lightsManager
680+
681+
val lights = lightsManager.lights
682+
if (lights.isEmpty()) {
683+
return false
684+
}
685+
686+
val actedLights = lights.count { light ->
687+
val state = LightState.Builder()
688+
689+
if (light.type == Light.LIGHT_TYPE_PLAYER_ID && (type and 1 == 1)) {
690+
state.setPlayerId(color)
691+
} else if (light.hasRgbControl() && (type and 2 == 2)) {
692+
state.setColor(color)
693+
} else {
694+
return@count false
695+
}
696+
697+
val request = LightsRequest.Builder()
698+
.addLight(light, state.build())
699+
.build()
700+
701+
lightsManager.openSession()
702+
.requestLights(request)
703+
704+
true
705+
}
706+
707+
return actedLights > 0
708+
}
709+
710+
@JvmStatic
711+
fun getDeviceHapticsCount(deviceId: Int): Int {
712+
val device = InputDevice.getDevice(deviceId) ?: return 0
713+
714+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
715+
return if (device.vibrator.hasVibrator()) 1 else 0
716+
}
717+
718+
val vibratorManager = device.vibratorManager
719+
val vibratorIds = vibratorManager.vibratorIds
720+
return vibratorIds.size
721+
}
722+
723+
private fun performVibration(vibrator: Vibrator?, durationMs: Long, intensity: Int) {
724+
vibrator?.apply {
725+
if (intensity == 0) {
726+
cancel()
727+
} else {
728+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
729+
val vibrationEffect = VibrationEffect.createOneShot(durationMs, intensity)
730+
vibrate(vibrationEffect)
731+
} else {
732+
vibrate(durationMs)
733+
}
734+
}
735+
}
736+
}
737+
738+
/**
739+
* Vibrates a given device. Set motorIdx == -1 to vibrate all available motors on a device.
740+
*/
741+
@JvmStatic
742+
fun vibrateDevice(deviceId: Int, durationMs: Long, intensity: Int, motorIdx: Int): Boolean {
743+
val device = InputDevice.getDevice(deviceId) ?: return false
744+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
745+
val vibratorManager = device.vibratorManager
746+
val vibratorIds = vibratorManager.vibratorIds
747+
748+
if (motorIdx == -1) {
749+
vibratorIds.forEach {
750+
val vibrator = vibratorManager.getVibrator(it)
751+
performVibration(vibrator, durationMs, intensity)
752+
}
753+
} else {
754+
val deviceId = vibratorIds.getOrNull(motorIdx) ?: return false
755+
val vibrator = vibratorManager.getVibrator(deviceId)
756+
performVibration(vibrator, durationMs, intensity)
757+
}
758+
} else {
759+
val vibrator = device.vibrator
760+
performVibration(vibrator, durationMs, intensity)
761+
}
762+
763+
return true
764+
}
765+
579766
external fun nativeKeyUp(keyCode: Int, modifiers: Int)
580767
external fun nativeKeyDown(keyCode: Int, modifiers: Int, isRepeating: Boolean)
581768
external fun nativeActionScroll(scrollX: Float, scrollY: Float)
@@ -588,4 +775,15 @@ object GeodeUtils {
588775
*/
589776
external fun setNextInputTimestamp(timestamp: Long)
590777
external fun setNextInputTimestampInternal(timestamp: Long)
778+
779+
external fun setNextInputDevice(deviceId: Int, eventSource: Int)
780+
external fun inputDeviceAdded(deviceId: Int)
781+
external fun inputDeviceChanged(deviceId: Int)
782+
external fun inputDeviceRemoved(deviceId: Int)
783+
784+
/**
785+
* Sends all batched joystick events. The final position in each array represents the current position of that axis.
786+
* Have fun!
787+
*/
788+
external fun onJoystickEvent(leftX: FloatArray, leftY: FloatArray, rightX: FloatArray, rightY: FloatArray, hatX: FloatArray, hatY: FloatArray, leftTrigger: FloatArray, rightTrigger: FloatArray)
591789
}

0 commit comments

Comments
 (0)