diff --git a/changelog.d/8080-android-picker-callback-and-json-extract-pointer.md b/changelog.d/8080-android-picker-callback-and-json-extract-pointer.md new file mode 100644 index 0000000000..51eae83bc3 --- /dev/null +++ b/changelog.d/8080-android-picker-callback-and-json-extract-pointer.md @@ -0,0 +1,19 @@ +Fixed two Android defects that made `perry-ui-android` unbuildable and stopped +`Picker` from reporting selection changes. + +`crates/perry-ui-android/src/json.rs` still called the `is_raw_pointer` helper +that #7448 deleted, so the crate failed to compile for any Android target +(`error[E0425]: cannot find function is_raw_pointer`). #7448 converted the +object path to `extract_pointer` but not the array-element path. Routing it +through the same helper also extends the #7447 fix to array elements: the old +bit test is the IEEE-754 positive-subnormal predicate, so every positive +denormal element in a stringified array was classified as a pointer and +dereferenced. + +`PerryBridge.setSpinnerCallback` did not exist. `widgets/picker.rs` has always +called it over JNI and discarded the resulting error with `let _ =`, so +`Picker`'s `onChange` could never fire on Android — the widget rendered and +selected, and the app was simply never told. + +Neither showed up in CI because the Android jobs in `feature-matrix.yml` are +`continue-on-error: true` and push-only. diff --git a/crates/perry-ui-android/src/json.rs b/crates/perry-ui-android/src/json.rs index e251b2fa43..b69183ed83 100644 --- a/crates/perry-ui-android/src/json.rs +++ b/crates/perry-ui-android/src/json.rs @@ -552,12 +552,14 @@ unsafe fn stringify_array(ptr: *const u8, buf: &mut String) { buf.push_str("true"); } else if elem_bits == TAG_FALSE { buf.push_str("false"); - } else if elem_tag == POINTER_TAG || is_raw_pointer(elem_bits) { - let elem_ptr = if elem_tag == POINTER_TAG { - (elem_bits & POINTER_MASK) as *const u8 - } else { - elem_bits as *const u8 - }; + // #7448 converted the object path to `extract_pointer` but left this + // array-element path calling the `is_raw_pointer` it deleted, so + // perry-ui-android stopped compiling for any Android target. Routing + // it through the same helper also gives array elements the fix the + // object path already had: the old bit test was the IEEE-754 + // positive-subnormal predicate, so every positive denormal element was + // classified as a pointer and dereferenced (#7447). + } else if let Some(elem_ptr) = extract_pointer(elem_bits) { if is_object_pointer(elem_ptr) { stringify_object(elem_ptr, buf); } else { diff --git a/crates/perry-ui-android/template/app/src/main/java/com/perry/app/PerryBridge.kt b/crates/perry-ui-android/template/app/src/main/java/com/perry/app/PerryBridge.kt index 2fe2fbfad5..9076814ad9 100644 --- a/crates/perry-ui-android/template/app/src/main/java/com/perry/app/PerryBridge.kt +++ b/crates/perry-ui-android/template/app/src/main/java/com/perry/app/PerryBridge.kt @@ -426,6 +426,38 @@ object PerryBridge { seekBar.progress = progress } + // --- Spinner (Picker) callback --- + + // `perry-ui-android`'s picker.rs has always called this, but it was never + // defined here and the Rust side discarded the resulting JNI error with + // `let _ =` — so `Picker`'s onChange could not fire on Android at all. + @JvmStatic + fun setSpinnerCallback(spinner: Spinner, callbackKey: Long) { + // Spinner calls the listener once when an adapter installs the initial + // selection, and again for every later `setAdapter` — `pickerAddItem` + // rebuilds the adapter per item. None of those is a user choice, so + // report only actual changes, with the first one taken as the baseline. + var reported = -1 + spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + if (reported == -1) { + reported = position + return + } + if (position == reported) return + reported = position + nativeInvokeCallback1(callbackKey, position.toDouble()) + } + + override fun onNothingSelected(parent: AdapterView<*>?) {} + } + } + // --- Context menu --- @JvmStatic