From 2de1693cd306fd5592947be5ccb4cf0e52c35b4b Mon Sep 17 00:00:00 2001 From: Antonio Vargas Garcia Date: Tue, 29 Dec 2020 14:18:06 -0800 Subject: [PATCH 1/4] Auto-focus and open soft-keyboard on Android - On iOS the keyboard is displayed automatically when the dialog is shown - This change makes it work the same way on Android --- src/android/PinDialog.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/android/PinDialog.java b/src/android/PinDialog.java index 6be1d6e..cbd27a7 100644 --- a/src/android/PinDialog.java +++ b/src/android/PinDialog.java @@ -9,17 +9,15 @@ import org.json.JSONObject; import android.app.AlertDialog; -import android.app.ProgressDialog; import android.content.DialogInterface; import android.text.InputType; import android.text.method.PasswordTransformationMethod; +import android.view.WindowManager; import android.widget.EditText; public class PinDialog extends CordovaPlugin { - public ProgressDialog spinnerDialog = null; - public PinDialog() { } @@ -108,9 +106,11 @@ public void onCancel(DialogInterface dialog){ } }); - dlg.create(); - dlg.show(); - + AlertDialog instance = dlg.create(); + instance.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); + instance.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); + instance.show(); + promptInput.requestFocus(); }; }; this.cordova.getActivity().runOnUiThread(runnable); From 3ad37abb37c1d75523b9ac3c1ed38ced923ff56a Mon Sep 17 00:00:00 2001 From: Antonio Vargas Garcia Date: Tue, 29 Dec 2020 14:35:35 -0800 Subject: [PATCH 2/4] Wait for dialog dismissal before sending result - Do not callback into the Cordova application until dialog is dismissed - Otherwise, UI elements shown after the callback get dismissed along with the dialog --- src/ios/CDVPinDialog.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ios/CDVPinDialog.m b/src/ios/CDVPinDialog.m index bd33a20..73c0ce5 100644 --- a/src/ios/CDVPinDialog.m +++ b/src/ios/CDVPinDialog.m @@ -43,7 +43,7 @@ - (void)prompt:(CDVInvokedUrlCommand*)command } -- (void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex +- (void)alertView:(UIAlertView*)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { CDVPluginResult* result; From f144a87ac4354b1ec3010c763f7983ed7806765a Mon Sep 17 00:00:00 2001 From: Antonio Vargas Date: Tue, 23 Jun 2026 15:22:21 -0700 Subject: [PATCH 3/4] fix(#752): migrate iOS PIN dialog from UIAlertView to UIAlertController UIAlertView is deprecated and, on the cordova-ios 8 UIScene lifecycle, instantiating it throws NSObjectNotAvailableException -> SIGABRT. Rewrite prompt: with UIAlertController, preserving the exact JS contract (1-based buttonIndex by position, input1, success-only result). Drop the UIAlertViewDelegate conformance and delete the old delegate callback. Co-Authored-By: Claude Opus 4.8 --- src/ios/CDVPinDialog.h | 2 +- src/ios/CDVPinDialog.m | 64 +++++++++++++++++------------------------- 2 files changed, 26 insertions(+), 40 deletions(-) diff --git a/src/ios/CDVPinDialog.h b/src/ios/CDVPinDialog.h index 415195d..8ca077d 100644 --- a/src/ios/CDVPinDialog.h +++ b/src/ios/CDVPinDialog.h @@ -10,7 +10,7 @@ #import -@interface CDVPinDialog : CDVPlugin {} +@interface CDVPinDialog : CDVPlugin {} @property (nonatomic, copy) NSString* callbackId; - (void)prompt:(CDVInvokedUrlCommand*)command; diff --git a/src/ios/CDVPinDialog.m b/src/ios/CDVPinDialog.m index 73c0ce5..adcf4f1 100644 --- a/src/ios/CDVPinDialog.m +++ b/src/ios/CDVPinDialog.m @@ -15,47 +15,33 @@ - (void)prompt:(CDVInvokedUrlCommand*)command NSString* message = [command argumentAtIndex:0]; NSString* title = [command argumentAtIndex:1]; NSArray* buttons = [command argumentAtIndex:2]; - - UIAlertView* alertView = [[UIAlertView alloc] - initWithTitle:title - message:message - delegate:self - cancelButtonTitle:nil - otherButtonTitles:nil]; - - //alertView.callbackId = callbackId; - - int count = [buttons count]; - - for (int n = 0; n < count; n++) { - [alertView addButtonWithTitle:[buttons objectAtIndex:n]]; - } - - alertView.alertViewStyle = UIAlertViewStyleSecureTextInput; - UITextField* textField = [alertView textFieldAtIndex:0]; - - [alertView show]; - [textField resignFirstResponder]; - [textField setKeyboardType:UIKeyboardTypeNumberPad]; - [textField becomeFirstResponder]; - + dispatch_async(dispatch_get_main_queue(), ^{ + UIAlertController* alert = [UIAlertController alertControllerWithTitle:title + message:message preferredStyle:UIAlertControllerStyleAlert]; + [alert addTextFieldWithConfigurationHandler:^(UITextField* tf) { + tf.secureTextEntry = YES; + tf.keyboardType = UIKeyboardTypeNumberPad; + }]; + for (NSUInteger n = 0; n < buttons.count; n++) { + NSInteger oneBased = (NSInteger)n + 1; + UIAlertActionStyle style = (n == buttons.count - 1) + ? UIAlertActionStyleCancel : UIAlertActionStyleDefault; + [alert addAction:[UIAlertAction actionWithTitle:buttons[n] + style:style handler:^(UIAlertAction* a) { + NSString* value0 = alert.textFields.firstObject.text; + NSDictionary* info = @{ @"buttonIndex": @(oneBased), + @"input1": (value0 ?: [NSNull null]) }; + CDVPluginResult* r = [CDVPluginResult + resultWithStatus:CDVCommandStatus_OK messageAsDictionary:info]; + [self.commandDelegate sendPluginResult:r callbackId:self.callbackId]; + }]]; + } + UIViewController* top = self.viewController; + while (top.presentedViewController) top = top.presentedViewController; + [top presentViewController:alert animated:YES completion:nil]; + }); } - - -- (void)alertView:(UIAlertView*)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex -{ - CDVPluginResult* result; - NSString* value0 = [[alertView textFieldAtIndex:0] text]; - NSDictionary* info = @{ - @"buttonIndex":@(buttonIndex + 1), - @"input1":(value0 ? value0 : [NSNull null]) - }; - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:info]; - - [self.commandDelegate sendPluginResult:result callbackId:self.callbackId]; -} - @end From 862c8eb2353db36fd19121baf6c19a4714ae21ad Mon Sep 17 00:00:00 2001 From: Antonio Vargas Date: Tue, 23 Jun 2026 20:28:14 -0700 Subject: [PATCH 4/4] fix: avoid UIAlertController retain cycle in PIN dialog handlers The action handlers captured the alert strongly to read its text field, forming an alert->action->handler->alert cycle that leaked one controller per prompt. Capture the alert weakly instead. Co-Authored-By: Claude Opus 4.8 --- src/ios/CDVPinDialog.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ios/CDVPinDialog.m b/src/ios/CDVPinDialog.m index adcf4f1..f817fac 100644 --- a/src/ios/CDVPinDialog.m +++ b/src/ios/CDVPinDialog.m @@ -23,13 +23,14 @@ - (void)prompt:(CDVInvokedUrlCommand*)command tf.secureTextEntry = YES; tf.keyboardType = UIKeyboardTypeNumberPad; }]; + __weak UIAlertController* weakAlert = alert; // avoid action-handler retain cycle for (NSUInteger n = 0; n < buttons.count; n++) { NSInteger oneBased = (NSInteger)n + 1; UIAlertActionStyle style = (n == buttons.count - 1) ? UIAlertActionStyleCancel : UIAlertActionStyleDefault; [alert addAction:[UIAlertAction actionWithTitle:buttons[n] style:style handler:^(UIAlertAction* a) { - NSString* value0 = alert.textFields.firstObject.text; + NSString* value0 = weakAlert.textFields.firstObject.text; NSDictionary* info = @{ @"buttonIndex": @(oneBased), @"input1": (value0 ?: [NSNull null]) }; CDVPluginResult* r = [CDVPluginResult