Skip to content

Commit 58cda8b

Browse files
authored
Merge pull request #30 from PureSwift/feature/AndroidNativeActivity
Add `AndroidNativeActivity` target
2 parents 1e38b70 + c358f89 commit 58cda8b

9 files changed

Lines changed: 2722 additions & 3 deletions

File tree

Package.swift

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ var package = Package(
8181
),
8282
.library(
8383
name: "AndroidFileManager", targets: ["AndroidFileManager"]
84+
),
85+
.library(
86+
name: "AndroidNativeActivity", targets: ["AndroidNativeActivity"]
87+
),
88+
.library(
89+
name: "AndroidInput", targets: ["AndroidInput"]
8490
)
8591
],
8692
dependencies: [
@@ -153,7 +159,9 @@ var package = Package(
153159
"AndroidLogging",
154160
"AndroidLooper",
155161
"AndroidHardware",
156-
"AndroidFileManager"
162+
"AndroidFileManager",
163+
"AndroidNativeActivity",
164+
"AndroidInput"
157165
],
158166
swiftSettings: [
159167
.swiftLanguageMode(.v5),
@@ -489,6 +497,42 @@ var package = Package(
489497
linkerSettings: [
490498
.linkedLibrary("android", .when(platforms: [.android]))
491499
]
500+
),
501+
.target(
502+
name: "AndroidNativeActivity",
503+
dependencies: [
504+
"AndroidNDK",
505+
"AndroidLooper",
506+
"AndroidFileManager",
507+
"AndroidInput",
508+
.product(
509+
name: "SystemPackage",
510+
package: "swift-system"
511+
)
512+
],
513+
swiftSettings: [
514+
.swiftLanguageMode(.v6),
515+
ndkVersionDefine,
516+
sdkVersionDefine
517+
],
518+
linkerSettings: [
519+
.linkedLibrary("android", .when(platforms: [.android]))
520+
]
521+
),
522+
.target(
523+
name: "AndroidInput",
524+
dependencies: [
525+
"AndroidNDK",
526+
"AndroidLooper"
527+
],
528+
swiftSettings: [
529+
.swiftLanguageMode(.v6),
530+
ndkVersionDefine,
531+
sdkVersionDefine
532+
],
533+
linkerSettings: [
534+
.linkedLibrary("android", .when(platforms: [.android]))
535+
]
492536
)
493537
],
494538
swiftLanguageModes: [.v5, .v6]

Sources/AndroidHardware/Syscalls.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ func stub() -> Never {
1717
fatalError("Not running on Android")
1818
}
1919

20-
typealias ALooper_callbackFunc = @convention(c) (Int32, Int32, UnsafeMutableRawPointer?) -> Int32
20+
public typealias ALooper_callbackFunc = @convention(c) (Int32, Int32, UnsafeMutableRawPointer?) -> Int32
2121

2222
// MARK: - ASensorEvent
2323

@@ -29,7 +29,7 @@ typealias ALooper_callbackFunc = @convention(c) (Int32, Int32, UnsafeMutableRawP
2929
* version(4) + sensor(4) + type(4) + reserved0(4) +
3030
* timestamp(8) + data_union(64) + flags(4) + reserved1(12)
3131
*/
32-
public struct ASensorEvent {
32+
public struct ASensorEvent: Sendable {
3333
public var version: Int32 // sizeof(struct ASensorEvent)
3434
public var sensor: Int32 // sensor identifier
3535
public var type: Int32 // sensor type
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
//
2+
// GameController.swift
3+
// SwiftAndroid
4+
//
5+
// Created by Alsey Coleman Miller on 2/27/26.
6+
//
7+
8+
#if AGDK
9+
#if os(Android)
10+
import Android
11+
import AndroidNDK
12+
#endif
13+
14+
/// Swift wrapper for the Android Game Controller (Paddleboat) C API.
15+
@MainActor
16+
public struct GameController: ~Copyable {
17+
18+
let environment: JNIEnvironment
19+
20+
public init(context: jobject, environment: JNIEnvironment) throws {
21+
let result = Paddleboat_init(environment, context)
22+
guard result == 0 else {
23+
throw GameController.Error(rawValue: result) ?? GameController.Error.notInitialized
24+
}
25+
guard Paddleboat_isInitialized() else {
26+
throw GameController.Error.notInitialized
27+
}
28+
self.environment = environment
29+
}
30+
31+
deinit {
32+
Paddleboat_destroy(environment)
33+
}
34+
35+
public func update() {
36+
Paddleboat_update(environment)
37+
}
38+
39+
// MARK: - Back button
40+
public static func setBackButtonConsumed(_ consume: Bool) {
41+
Paddleboat_setBackButtonConsumed(consume)
42+
}
43+
44+
public static func isBackButtonConsumed() -> Bool {
45+
Paddleboat_getBackButtonConsumed()
46+
}
47+
48+
// MARK: - Controller Info / Data
49+
public static func getControllerStatus(index: Int32) -> ControllerStatus {
50+
ControllerStatus(rawValue: Paddleboat_getControllerStatus(index)) ?? .inactive
51+
}
52+
53+
public static func getControllerName(index: Int32, bufferSize: Int = 128) -> (ErrorCode, String) {
54+
var buffer = [CChar](repeating: 0, count: bufferSize)
55+
let err = Paddleboat_getControllerName(index, Int32(buffer.count), &buffer)
56+
let code = ErrorCode(rawValue: err) ?? .noError
57+
let name = buffer.withUnsafeBufferPointer { String(cString: $0.baseAddress!) }
58+
return (code, name)
59+
}
60+
61+
// MARK: - Lights / Vibration
62+
@discardableResult
63+
public func setControllerLight(index: Int32, type: LightType, data: UInt32) -> ErrorCode {
64+
let err = Paddleboat_setControllerLight(index, type.rawValue, data, environment)
65+
return ErrorCode(rawValue: err) ?? .noError
66+
}
67+
68+
@discardableResult
69+
public func setControllerVibration(index: Int32, vibration: VibrationData) -> ErrorCode {
70+
var cData = Paddleboat_Vibration_Data(duration_ms: vibration.durationMs,
71+
left_motor_intensity: vibration.intensityLeft,
72+
right_motor_intensity: vibration.intensityRight)
73+
let err = Paddleboat_setControllerVibrationData(index, &cData, environment)
74+
return ErrorCode(rawValue: err) ?? .noError
75+
}
76+
77+
// MARK: - Motion
78+
public static func getIntegratedMotionSensorFlags() -> IntegratedMotionSensorFlags {
79+
IntegratedMotionSensorFlags(rawValue: Paddleboat_getIntegratedMotionSensorFlags())
80+
}
81+
}
82+
83+
// MARK: - Supporting Types
84+
85+
public extension GameController {
86+
87+
// MARK: - Version / Constants
88+
public static var maxControllers: Int32 { 8 }
89+
90+
// MARK: - Error
91+
public enum Error: Int32, Swift.Error {
92+
case noError = 0
93+
case alreadyInitialized = -2000
94+
case notInitialized = -2001
95+
case invalidParameter = -2002
96+
case invalidControllerIndex = -2003
97+
case noController = -2004
98+
case featureNotSupported = -2005
99+
case fileIO = -2006
100+
case incompatibleMappingData = -2007
101+
case invalidMappingData = -2008
102+
case noMouse = -2009
103+
case initGCMFailure = -2010
104+
}
105+
106+
typealias ErrorCode = Error
107+
108+
public enum ControllerStatus: Int32 {
109+
case inactive = 0
110+
case active = 1
111+
case justConnected = 2
112+
case justDisconnected = 3
113+
}
114+
115+
public struct Buttons: OptionSet, Sendable {
116+
public let rawValue: UInt32
117+
public init(rawValue: UInt32) { self.rawValue = rawValue }
118+
public static let a = Buttons(rawValue: 1 << 0)
119+
public static let b = Buttons(rawValue: 1 << 1)
120+
public static let x = Buttons(rawValue: 1 << 2)
121+
public static let y = Buttons(rawValue: 1 << 3)
122+
public static let l1 = Buttons(rawValue: 1 << 4)
123+
public static let r1 = Buttons(rawValue: 1 << 5)
124+
public static let l2 = Buttons(rawValue: 1 << 6)
125+
public static let r2 = Buttons(rawValue: 1 << 7)
126+
public static let l3 = Buttons(rawValue: 1 << 8)
127+
public static let r3 = Buttons(rawValue: 1 << 9)
128+
public static let dpadUp = Buttons(rawValue: 1 << 10)
129+
public static let dpadDown = Buttons(rawValue: 1 << 11)
130+
public static let dpadLeft = Buttons(rawValue: 1 << 12)
131+
public static let dpadRight = Buttons(rawValue: 1 << 13)
132+
public static let start = Buttons(rawValue: 1 << 14)
133+
public static let select = Buttons(rawValue: 1 << 15)
134+
public static let system = Buttons(rawValue: 1 << 16)
135+
public static let touchpad = Buttons(rawValue: 1 << 17)
136+
public static let aux1 = Buttons(rawValue: 1 << 18)
137+
public static let aux2 = Buttons(rawValue: 1 << 19)
138+
public static let aux3 = Buttons(rawValue: 1 << 20)
139+
public static let aux4 = Buttons(rawValue: 1 << 21)
140+
}
141+
142+
public enum LightType: Int32 {
143+
case playerNumber = 0
144+
case rgb = 1
145+
}
146+
147+
public struct IntegratedMotionSensorFlags: OptionSet, Sendable {
148+
public let rawValue: UInt32
149+
public init(rawValue: UInt32) { self.rawValue = rawValue }
150+
public static let none = IntegratedMotionSensorFlags([])
151+
public static let accelerometer = IntegratedMotionSensorFlags(rawValue: 0x00000001)
152+
public static let gyroscope = IntegratedMotionSensorFlags(rawValue: 0x00000002)
153+
public static let indexFlag = IntegratedMotionSensorFlags(rawValue: 0x40000000)
154+
}
155+
156+
public struct VibrationData {
157+
public var durationMs: Int32
158+
public var intensityLeft: Float
159+
public var intensityRight: Float
160+
public init(durationMs: Int32, intensityLeft: Float, intensityRight: Float) {
161+
self.durationMs = durationMs
162+
self.intensityLeft = intensityLeft
163+
self.intensityRight = intensityRight
164+
}
165+
}
166+
}
167+
#endif

0 commit comments

Comments
 (0)