also looking at this
feat(android): the clipboard pair — ratchet 100 -> 98
#144
6 files
+414
-3
| @@ -38,6 +38,7 @@ const builtin = ; | ||
| 38 | 38 | const jni = ; |
| 39 | 39 | const device = ; |
| 40 | 40 | const system = ; |
| 41 | const clipboard = ; | |
| 41 | 42 | |
| 42 | 43 | const Jni = jni.Jni; |
| 43 | 44 | |
| @@ -137,6 +138,16 @@ const natives = [_]jni.JNINativeMethod{ | ||
| 137 | 138 | .signature = "(Ljava/lang/String;)Z", |
| 138 | 139 | .fnPtr = @ptrCast(&nativeLog), |
| 139 | 140 | }, |
| 141 | .{ | |
| 142 | .name = "nativeClipboardRead", | |
| 143 | .signature = "(Landroid/app/Activity;)Ljava/lang/String;", | |
| 144 | .fnPtr = @ptrCast(&nativeClipboardRead), | |
| 145 | }, | |
| 146 | .{ | |
| 147 | .name = "nativeClipboardWrite", | |
| 148 | .signature = "(Landroid/app/Activity;Ljava/lang/String;)Z", | |
| 149 | .fnPtr = @ptrCast(&nativeClipboardWrite), | |
| 150 | }, | |
| 140 | 151 | }; |
| 141 | 152 | |
| 142 | 153 | /// How binding went. A value rather than a log line, so every path is |
| @@ -242,6 +253,52 @@ fn nativeLog(env: jni.JNIEnv, _: jni.jobject, message: jni.jstring) callconv(.c) | ||
| 242 | 253 | return jni.JNI_TRUE; |
| 243 | 254 | } |
| 244 | 255 | |
| 256 | /// `nativeClipboardRead(activity)`. | |
| 257 | /// | |
| 258 | /// Null here means "ask the shim", and an empty *string* means "the clipboard | |
| 259 | /// is empty" — two different answers that a nullable String is exactly able to | |
| 260 | /// carry, and that a plain String would collapse into one. | |
| 261 | fn nativeClipboardRead(env: jni.JNIEnv, _: jni.jobject, activity: jni.jobject) callconv(.c) jni.jstring { | |
| 262 | const j = Jni.init(env); | |
| 263 | ||
| 264 | var arena = std.heap.ArenaAllocator.init(backing); | |
| 265 | defer arena.deinit(); | |
| 266 | const allocator = arena.allocator(); | |
| 267 | ||
| 268 | const text = clipboard.read(allocator, j, activity) catch |err| { | |
| 269 | std.log.warn("craft: clipboardRead fell through to the shim ({s})", .{@errorName(err)}); | |
| 270 | return null; | |
| 271 | }; | |
| 272 | ||
| 273 | const owned = allocator.allocSentinel(u8, text.len, 0) catch return null; | |
| 274 | @memcpy(owned, text); | |
| 275 | return j.newStringUtf(owned.ptr) catch null; | |
| 276 | } | |
| 277 | ||
| 278 | /// `nativeClipboardWrite(activity, text)`. | |
| 279 | fn nativeClipboardWrite( | |
| 280 | env: jni.JNIEnv, | |
| 281 | _: jni.jobject, | |
| 282 | activity: jni.jobject, | |
| 283 | text: jni.jstring, | |
| 284 | ) callconv(.c) jni.jboolean { | |
| 285 | const j = Jni.init(env); | |
| 286 | ||
| 287 | var arena = std.heap.ArenaAllocator.init(backing); | |
| 288 | defer arena.deinit(); | |
| 289 | const allocator = arena.allocator(); | |
| 290 | ||
| 291 | const value = j.stringToUtf8(allocator, text) catch return jni.JNI_FALSE; | |
| 292 | const owned = allocator.allocSentinel(u8, value.len, 0) catch return jni.JNI_FALSE; | |
| 293 | @memcpy(owned, value); | |
| 294 | ||
| 295 | clipboard.write(j, activity, owned.ptr) catch |err| { | |
| 296 | std.log.warn("craft: clipboardWrite fell through to the shim ({s})", .{@errorName(err)}); | |
| 297 | return jni.JNI_FALSE; | |
| 298 | }; | |
| 299 | return jni.JNI_TRUE; | |
| 300 | } | |
| 301 | ||
| 245 | 302 | // ============================================================================= |
| 246 | 303 | // Tests |
| 247 | 304 | // ============================================================================= |
| @@ -353,7 +410,7 @@ test "the registered natives name methods the Kotlin actually declares" { | ||
| 353 | 410 | // A descriptor is checked by the JVM at registration, so a wrong one fails |
| 354 | 411 | // at load rather than at call — but only if the *name* matches something. |
| 355 | 412 | // These two strings are the contract with CraftBridge.kt. |
| 356 | try testing.expectEqual(@as(usize, 3), natives.len); | |
| 413 | try testing.expectEqual(@as(usize, 5), natives.len); | |
| 357 | 414 | try testing.expectEqualStrings("nativeGetDeviceInfo", std.mem.span(natives[0].name)); |
| 358 | 415 | |
| 359 | 416 | // And the class they bind to is the fixed one, not the templated bridge. |