also looking at this
feat(android): downloadFile and saveFile in Zig — ratchet 69 → 67
#176
6 files
+477
-3
| @@ -53,6 +53,7 @@ const contacts = ; | ||
| 53 | 53 | const widgets = ; |
| 54 | 54 | const shortcuts = ; |
| 55 | 55 | const screen = ; |
| 56 | const files = ; | |
| 56 | 57 | const main_thread = ; |
| 57 | 58 | |
| 58 | 59 | const Jni = jni.Jni; |
| @@ -347,6 +348,16 @@ const natives = [_]jni.JNINativeMethod{ | ||
| 347 | 348 | .signature = "(Landroid/app/Activity;Z)Z", |
| 348 | 349 | .fnPtr = @ptrCast(&nativeSetKeepAwake), |
| 349 | 350 | }, |
| 351 | .{ | |
| 352 | .name = "nativeDownloadFile", | |
| 353 | .signature = "(Landroid/app/Activity;Ljava/lang/String;Ljava/lang/String;)Z", | |
| 354 | .fnPtr = @ptrCast(&nativeDownloadFile), | |
| 355 | }, | |
| 356 | .{ | |
| 357 | .name = "nativeSaveFile", | |
| 358 | .signature = "(Landroid/app/Activity;Ljava/lang/String;Ljava/lang/String;)Z", | |
| 359 | .fnPtr = @ptrCast(&nativeSaveFile), | |
| 360 | }, | |
| 350 | 361 | }; |
| 351 | 362 | |
| 352 | 363 | /// How binding went. A value rather than a log line, so every path is |
| @@ -1401,6 +1412,66 @@ fn nativeSetKeepAwake( | ||
| 1401 | 1412 | return jni.JNI_TRUE; |
| 1402 | 1413 | } |
| 1403 | 1414 | |
| 1415 | /// `nativeDownloadFile(activity, url, filename)`. | |
| 1416 | /// | |
| 1417 | /// Resolves with the download id as a JSON *string*, because the shim | |
| 1418 | /// interpolated its `Long` into a quoted JavaScript string and the page has | |
| 1419 | /// always received text. | |
| 1420 | fn nativeDownloadFile( | |
| 1421 | env: jni.JNIEnv, | |
| 1422 | _: jni.jobject, | |
| 1423 | activity: jni.jobject, | |
| 1424 | url: jni.jstring, | |
| 1425 | filename: jni.jstring, | |
| 1426 | ) callconv(.c) jni.jboolean { | |
| 1427 | const j = Jni.init(env); | |
| 1428 | var arena = std.heap.ArenaAllocator.init(backing); | |
| 1429 | defer arena.deinit(); | |
| 1430 | const allocator = arena.allocator(); | |
| 1431 | ||
| 1432 | const url_text = j.stringToUtf8(allocator, url) catch return jni.JNI_FALSE; | |
| 1433 | const name_text = j.stringToUtf8(allocator, filename) catch return jni.JNI_FALSE; | |
| 1434 | ||
| 1435 | const id = files.download(j, allocator, activity, url_text, name_text) catch |err| { | |
| 1436 | calendar.rejectOn(allocator, files.download_reject_global, @errorName(err)) catch {}; | |
| 1437 | return jni.JNI_TRUE; | |
| 1438 | }; | |
| 1439 | ||
| 1440 | var buf: [24]u8 = undefined; | |
| 1441 | const digits = std.fmt.bufPrint(&buf, "{d}", .{id}) catch return jni.JNI_FALSE; | |
| 1442 | const payload = files.jsonString(allocator, digits) catch return jni.JNI_FALSE; | |
| 1443 | events.settle(allocator, files.download_resolve_global, payload) catch return jni.JNI_FALSE; | |
| 1444 | return jni.JNI_TRUE; | |
| 1445 | } | |
| 1446 | ||
| 1447 | /// `nativeSaveFile(activity, data, filename)`. | |
| 1448 | fn nativeSaveFile( | |
| 1449 | env: jni.JNIEnv, | |
| 1450 | _: jni.jobject, | |
| 1451 | activity: jni.jobject, | |
| 1452 | data: jni.jstring, | |
| 1453 | filename: jni.jstring, | |
| 1454 | ) callconv(.c) jni.jboolean { | |
| 1455 | const j = Jni.init(env); | |
| 1456 | var arena = std.heap.ArenaAllocator.init(backing); | |
| 1457 | defer arena.deinit(); | |
| 1458 | const allocator = arena.allocator(); | |
| 1459 | ||
| 1460 | const data_text = j.stringToUtf8(allocator, data) catch return jni.JNI_FALSE; | |
| 1461 | const name_text = j.stringToUtf8(allocator, filename) catch return jni.JNI_FALSE; | |
| 1462 | ||
| 1463 | // The plan can be `nothing`, and the shim still resolves with the path. | |
| 1464 | // Reproduced rather than corrected — see #175. | |
| 1465 | const path = files.save(j, allocator, activity, name_text, files.planFor(data_text)) catch |err| { | |
| 1466 | calendar.rejectOn(allocator, files.save_reject_global, @errorName(err)) catch {}; | |
| 1467 | return jni.JNI_TRUE; | |
| 1468 | }; | |
| 1469 | ||
| 1470 | const payload = files.jsonString(allocator, path) catch return jni.JNI_FALSE; | |
| 1471 | events.settle(allocator, files.save_resolve_global, payload) catch return jni.JNI_FALSE; | |
| 1472 | return jni.JNI_TRUE; | |
| 1473 | } | |
| 1474 | ||
| 1404 | 1475 | // ============================================================================= |
| 1405 | 1476 | // Tests |
| 1406 | 1477 | // ============================================================================= |
| @@ -1512,7 +1583,7 @@ test "the registered natives name methods the Kotlin actually declares" { | ||
| 1512 | 1583 | // A descriptor is checked by the JVM at registration, so a wrong one fails |
| 1513 | 1584 | // at load rather than at call — but only if the *name* matches something. |
| 1514 | 1585 | // These two strings are the contract with CraftBridge.kt. |
| 1515 | try testing.expectEqual(@as(usize, 35), natives.len); | |
| 1586 | try testing.expectEqual(@as(usize, 37), natives.len); | |
| 1516 | 1587 | try testing.expectEqualStrings("nativeGetDeviceInfo", std.mem.span(natives[0].name)); |
| 1517 | 1588 | |
| 1518 | 1589 | // And the class they bind to is the fixed one, not the templated bridge. |