also looking at this
feat(android): the shortcut pair in Zig — ratchet 75 → 73
#171
6 files
+602
-3
| @@ -51,6 +51,7 @@ const db = ; | ||
| 51 | 51 | const shareditem = ; |
| 52 | 52 | const contacts = ; |
| 53 | 53 | const widgets = ; |
| 54 | const shortcuts = ; | |
| 54 | 55 | |
| 55 | 56 | const Jni = jni.Jni; |
| 56 | 57 | |
| @@ -307,6 +308,16 @@ const natives = [_]jni.JNINativeMethod{ | ||
| 307 | 308 | .signature = "(Landroid/app/Activity;Ljava/lang/String;)Z", |
| 308 | 309 | .fnPtr = @ptrCast(&nativeReloadWidgets), |
| 309 | 310 | }, |
| 311 | .{ | |
| 312 | .name = "nativeSetShortcuts", | |
| 313 | .signature = "(Landroid/app/Activity;Ljava/lang/String;)Z", | |
| 314 | .fnPtr = @ptrCast(&nativeSetShortcuts), | |
| 315 | }, | |
| 316 | .{ | |
| 317 | .name = "nativeClearShortcuts", | |
| 318 | .signature = "(Landroid/app/Activity;)Z", | |
| 319 | .fnPtr = @ptrCast(&nativeClearShortcuts), | |
| 320 | }, | |
| 310 | 321 | }; |
| 311 | 322 | |
| 312 | 323 | /// How binding went. A value rather than a log line, so every path is |
| @@ -1152,6 +1163,89 @@ fn nativeReloadWidgets( | ||
| 1152 | 1163 | return jni.JNI_TRUE; |
| 1153 | 1164 | } |
| 1154 | 1165 | |
| 1166 | /// `nativeSetShortcuts(activity, shortcutsJson)`. | |
| 1167 | fn nativeSetShortcuts( | |
| 1168 | env: jni.JNIEnv, | |
| 1169 | _: jni.jobject, | |
| 1170 | activity: jni.jobject, | |
| 1171 | shortcuts_json: jni.jstring, | |
| 1172 | ) callconv(.c) jni.jboolean { | |
| 1173 | const j = Jni.init(env); | |
| 1174 | var arena = std.heap.ArenaAllocator.init(backing); | |
| 1175 | defer arena.deinit(); | |
| 1176 | const allocator = arena.allocator(); | |
| 1177 | ||
| 1178 | const sdk = shortcuts.sdkInt(j) catch |err| { | |
| 1179 | std.log.warn("craft: setShortcuts fell through to the shim ({s})", .{@errorName(err)}); | |
| 1180 | return jni.JNI_FALSE; | |
| 1181 | }; | |
| 1182 | ||
| 1183 | if (sdk < shortcuts.n_mr1) { | |
| 1184 | const payload = shortcuts.jsonString(allocator, shortcuts.unsupported_message) catch | |
| 1185 | return jni.JNI_FALSE; | |
| 1186 | events.settle(allocator, shortcuts.reject_global, payload) catch return jni.JNI_FALSE; | |
| 1187 | return jni.JNI_TRUE; | |
| 1188 | } | |
| 1189 | ||
| 1190 | const text = j.stringToUtf8(allocator, shortcuts_json) catch return jni.JNI_FALSE; | |
| 1191 | var parsed = std.json.parseFromSlice(std.json.Value, allocator, text, .{}) catch | |
| 1192 | return jni.JNI_FALSE; | |
| 1193 | defer parsed.deinit(); | |
| 1194 | ||
| 1195 | // `getString` raises where `optString` defaults, so a payload missing a | |
| 1196 | // title is a rejection rather than a shape to hand back — and the shim | |
| 1197 | // sets nothing at all, including the entries that parsed. | |
| 1198 | const list = shortcuts.parseShortcuts(allocator, parsed.value) catch |err| { | |
| 1199 | const payload = shortcuts.jsonString(allocator, @errorName(err)) catch | |
| 1200 | return jni.JNI_FALSE; | |
| 1201 | events.settle(allocator, shortcuts.reject_global, payload) catch return jni.JNI_FALSE; | |
| 1202 | return jni.JNI_TRUE; | |
| 1203 | } orelse return jni.JNI_FALSE; | |
| 1204 | ||
| 1205 | shortcuts.set(j, allocator, activity, list) catch |err| { | |
| 1206 | const payload = shortcuts.jsonString(allocator, @errorName(err)) catch | |
| 1207 | return jni.JNI_FALSE; | |
| 1208 | events.settle(allocator, shortcuts.reject_global, payload) catch return jni.JNI_FALSE; | |
| 1209 | return jni.JNI_TRUE; | |
| 1210 | }; | |
| 1211 | ||
| 1212 | const payload = shortcuts.countPayload(allocator, list.len) catch return jni.JNI_FALSE; | |
| 1213 | events.settle(allocator, shortcuts.resolve_global, payload) catch return jni.JNI_FALSE; | |
| 1214 | return jni.JNI_TRUE; | |
| 1215 | } | |
| 1216 | ||
| 1217 | /// `nativeClearShortcuts(activity)`. | |
| 1218 | /// | |
| 1219 | /// Resolves `true` below API 25 without touching anything, because that is | |
| 1220 | /// what the shim does: nothing to clear is the same as having cleared it. | |
| 1221 | fn nativeClearShortcuts( | |
| 1222 | env: jni.JNIEnv, | |
| 1223 | _: jni.jobject, | |
| 1224 | activity: jni.jobject, | |
| 1225 | ) callconv(.c) jni.jboolean { | |
| 1226 | const j = Jni.init(env); | |
| 1227 | var arena = std.heap.ArenaAllocator.init(backing); | |
| 1228 | defer arena.deinit(); | |
| 1229 | const allocator = arena.allocator(); | |
| 1230 | ||
| 1231 | const sdk = shortcuts.sdkInt(j) catch |err| { | |
| 1232 | std.log.warn("craft: clearShortcuts fell through to the shim ({s})", .{@errorName(err)}); | |
| 1233 | return jni.JNI_FALSE; | |
| 1234 | }; | |
| 1235 | ||
| 1236 | if (sdk >= shortcuts.n_mr1) { | |
| 1237 | shortcuts.clear(j, activity) catch |err| { | |
| 1238 | const payload = shortcuts.jsonString(allocator, @errorName(err)) catch | |
| 1239 | return jni.JNI_FALSE; | |
| 1240 | events.settle(allocator, shortcuts.reject_global, payload) catch return jni.JNI_FALSE; | |
| 1241 | return jni.JNI_TRUE; | |
| 1242 | }; | |
| 1243 | } | |
| 1244 | ||
| 1245 | events.settle(allocator, shortcuts.resolve_global, "true") catch return jni.JNI_FALSE; | |
| 1246 | return jni.JNI_TRUE; | |
| 1247 | } | |
| 1248 | ||
| 1155 | 1249 | // ============================================================================= |
| 1156 | 1250 | // Tests |
| 1157 | 1251 | // ============================================================================= |
| @@ -1263,7 +1357,7 @@ test "the registered natives name methods the Kotlin actually declares" { | ||
| 1263 | 1357 | // A descriptor is checked by the JVM at registration, so a wrong one fails |
| 1264 | 1358 | // at load rather than at call — but only if the *name* matches something. |
| 1265 | 1359 | // These two strings are the contract with CraftBridge.kt. |
| 1266 | try testing.expectEqual(@as(usize, 28), natives.len); | |
| 1360 | try testing.expectEqual(@as(usize, 30), natives.len); | |
| 1267 | 1361 | try testing.expectEqualStrings("nativeGetDeviceInfo", std.mem.span(natives[0].name)); |
| 1268 | 1362 | |
| 1269 | 1363 | // And the class they bind to is the fixed one, not the templated bridge. |