Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ let package = Package(
dependencies: [
.package(url: "https://source.skip.tools/skip.git", from: "1.2.34"),
.package(url: "https://source.skip.tools/skip-foundation.git", from: "1.3.1"),
.package(url: "https://source.skip.tools/swift-jni.git", "0.0.0"..<"2.0.0"),
.package(url: "https://source.skip.tools/skip-bridge.git", "0.0.0"..<"2.0.0"),
.package(url: "https://source.skip.tools/swift-android-native.git", from: "1.4.1")
//.package(url: "https://source.skip.tools/swift-jni.git", "0.0.0"..<"2.0.0"),
.package(url: "https://source.skip.tools/swift-jni.git", branch: "swift-java-jni-cutover"), // ### REMOVEME
//.package(url: "https://source.skip.tools/skip-bridge.git", "0.0.0"..<"2.0.0"),
.package(url: "https://source.skip.tools/skip-bridge.git", branch: "swift-java-jni-cutover"),
.package(url: "https://github.com/swift-android-sdk/swift-android-native.git", from: "2.0.1")
],
targets: [
.target(name: "SkipAndroidBridge", dependencies: [
Expand All @@ -31,10 +33,30 @@ let package = Package(

.target(name: "SkipAndroidBridgeSamples", dependencies: [
"SkipAndroidBridge",
.product(name: "AndroidContext", package: "swift-android-native", condition: .when(platforms: [.android])),
], resources: [.process("Resources")], plugins: [.plugin(name: "skipstone", package: "skip")]),
.testTarget(name: "SkipAndroidBridgeSamplesTests", dependencies: [
"SkipAndroidBridgeSamples",
.product(name: "SkipTest", package: "skip"),
], resources: [.process("Resources")], plugins: [.plugin(name: "skipstone", package: "skip")]),
]
)

if let dependencyRoot = Context.environment["SKIP_DEPENDENCY_ROOT"] {
package.dependencies = package.dependencies.map { dep in
switch dep.kind {
case .sourceControl(_, let location, _):
guard let baseName = location.split(separator: "/").last?.split(separator: ".").first else {
return dep
}
// Remap skip* and swift-jni (the SWIFT_JAVA_JNI_CORE substrate; a direct dep here) to local;
// leave swift-android-native on its declared fork URL.
guard baseName.hasPrefix("skip") || baseName == "swift-jni" else {
return dep
}
return Package.Dependency.package(path: dependencyRoot + "/" + baseName)
default:
return dep
}
}
}
38 changes: 34 additions & 4 deletions Sources/SkipAndroidBridge/AndroidBridgeBootstrap.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ public class AndroidBridgeBootstrap {
try AssetURLProtocol.register()
logger.debug("initAndroidBridge: bootstrapTimezone")
try bootstrapTimezone()
logger.debug("initAndroidBridge: setupCACerts")
try AndroidBootstrap.setupCACerts()
logger.debug("initAndroidBridge: AndroidLooper.setupMainLooper")
let _ = AndroidLooper.setupMainLooper()
// CA-certificate bootstrap for URLSession HTTPS is now provided by the Swift SDK for Android,
// so the former AndroidBootstrap.setupCACerts() call is no longer needed.
logger.debug("initAndroidBridge: AndroidMainActor.setupMainLooper")
let _ = AndroidMainActor.setupMainLooper()
logger.debug("initAndroidBridge: done")
#endif
logger.debug("AndroidBridgeBootstrap.initAndroidBridge done in \(Date.now.timeIntervalSince(start)) applicationSupportDirectory=\(URL.applicationSupportDirectory.path)")
Expand Down Expand Up @@ -140,3 +140,33 @@ extension URL {
}

#endif

#if os(Android)
/// Minimal OSLog-`Logger`-compatible shim for Android.
///
/// The skiptools `AndroidLogging` used to vend an OSLog-style `Logger`; the swift-android-sdk
/// `AndroidLogging` vends `AndroidLogger` instead. This re-provides the small `Logger` surface this
/// module relies on, forwarding messages to logcat via `AndroidLogger` (`__android_log_write`).
/// `AndroidLogger`/`LogTag`/`LogPriority` are available via the module's `@_exported import AndroidLogging`.
public struct Logger: Sendable {
private let tag: LogTag

public init(subsystem: String, category: String) {
self.tag = LogTag(rawValue: category)
}

public func trace(_ message: String) { emit(message, .verbose) }
public func debug(_ message: String) { emit(message, .debug) }
public func info(_ message: String) { emit(message, .info) }
public func notice(_ message: String) { emit(message, .info) }
public func warning(_ message: String) { emit(message, .warning) }
public func error(_ message: String) { emit(message, .error) }
public func critical(_ message: String) { emit(message, .error) }
public func fault(_ message: String) { emit(message, .error) }
public func log(_ message: String) { emit(message, .info) }

private func emit(_ message: String, _ priority: LogPriority) {
try? AndroidLogger(tag: tag, priority: priority).log(message)
}
}
#endif
19 changes: 16 additions & 3 deletions Sources/SkipAndroidBridge/AssetURLProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#if os(Android)
import Foundation
import FoundationNetworking
import AndroidAssetManager
import AndroidFileManager
import AndroidLogging
@preconcurrency import SwiftJNI

Expand All @@ -15,7 +15,7 @@ public class AssetURLProtocol: URLProtocol {
public static let scheme = "asset"

nonisolated(unsafe) private static var registered = false
nonisolated(unsafe) private static var assetManager: AndroidAssetManager? = nil
nonisolated(unsafe) private static var assetManager: AssetManager? = nil

public static func register() throws {
if registered { return }
Expand All @@ -32,7 +32,7 @@ public class AssetURLProtocol: URLProtocol {
throw AndroidAssetError(errorDescription: "no value for ProcessInfo.processInfo.dynamicAndroidContext.toJavaObject")
}
let am = JNI.jni.withEnv { intf, env in
AndroidAssetManager(env: env, peer: jobj)
AssetManager.fromJava(jobj, environment: env)
}
Self.assetManager = am
Self.registered = true
Expand Down Expand Up @@ -91,4 +91,17 @@ public struct AndroidAssetError : LocalizedError {
}
}

private extension AssetManager {
/// Reads the entire contents of the named asset into `Data`, or returns nil if it cannot be
/// opened or read. Replaces the former `AndroidAssetManager.load(from:)` convenience that the
/// swift-android-sdk `AndroidFileManager.AssetManager` does not provide.
func load(from path: String) -> Data? {
guard let asset = try? open(path) else { return nil }
return try? asset.readAll { (buffer: UnsafeRawBufferPointer) -> Data in
guard let base = buffer.baseAddress else { return Data() }
return Data(bytes: base, count: buffer.count)
}
}
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import SwiftJNI
#if canImport(AndroidNative)
import AndroidNative
#endif
#if canImport(AndroidContext)
import AndroidContext
#endif

public let swiftStringConstant = "s"

Expand Down
Loading