From 132117a58687024ec4d596413637dc2306a776a8 Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Tue, 17 Dec 2024 11:31:45 +0000 Subject: [PATCH 01/15] feat: Add cancelable parameter on Android References: https://outsystemsrd.atlassian.net/browse/RMET-3576 --- .../capacitorjs/plugins/actionsheet/ActionSheet.java | 4 +++- .../plugins/actionsheet/ActionSheetPlugin.java | 8 +++++++- action-sheet/src/definitions.ts | 10 ++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheet.java b/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheet.java index 13a78f11a..75315532f 100644 --- a/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheet.java +++ b/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheet.java @@ -26,7 +26,9 @@ public interface OnCancelListener { @Override public void onCancel(DialogInterface dialog) { super.onCancel(dialog); - this.cancelListener.onCancel(); + if (this.cancelListener != null) { + this.cancelListener.onCancel(); + } } private String title; diff --git a/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheetPlugin.java b/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheetPlugin.java index 7ca88b8e4..189c527a0 100644 --- a/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheetPlugin.java +++ b/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheetPlugin.java @@ -19,6 +19,7 @@ public class ActionSheetPlugin extends Plugin { @PluginMethod public void showActions(final PluginCall call) { String title = call.getString("title"); + boolean cancelable = Boolean.TRUE.equals(call.getBoolean("cancelable", false)); JSArray options = call.getArray("options"); if (options == null) { call.reject("Must supply options"); @@ -39,7 +40,12 @@ public void showActions(final PluginCall call) { } implementation.setTitle(title); implementation.setOptions(actionOptions); - implementation.setCancelable(false); + implementation.setCancelable(cancelable); + if (cancelable) { + implementation.setOnCancelListener( + () -> call.reject("User canceled action sheet") + ); + } implementation.setOnSelectedListener( index -> { JSObject ret = new JSObject(); diff --git a/action-sheet/src/definitions.ts b/action-sheet/src/definitions.ts index 84dbfbaae..ade83a9af 100644 --- a/action-sheet/src/definitions.ts +++ b/action-sheet/src/definitions.ts @@ -15,6 +15,16 @@ export interface ShowActionsOptions { */ message?: string; + /** + * If true, sheet is canceled when clicked outside; If false, it is not. By default, false. + * + * On iOS, sheet is also cancelable if a button with ActionSheetButtonStyle.Cancel is provided and cancelable is false. + * + * @since TODO provide release number + */ + cancelable?: boolean; + + /** * Options the user can choose from. * From aa33a0716c0407940418c31cfe602182b1553046 Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Tue, 17 Dec 2024 16:46:01 +0000 Subject: [PATCH 02/15] feat: Add cancelable parameter on iOS Only is applied if no cancel option is provided. References: https://outsystemsrd.atlassian.net/browse/RMET-3576 --- .../ActionSheetPlugin/ActionSheetPlugin.swift | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift b/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift index b36c43e2f..74cfdad81 100644 --- a/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift +++ b/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift @@ -17,9 +17,11 @@ public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin { @objc func showActions(_ call: CAPPluginCall) { let title = call.options["title"] as? String let message = call.options["message"] as? String + let cancelable = call.options["cancelable"] as? Bool ?? false let options = call.getArray("options", JSObject.self) ?? [] var alertActions = [UIAlertAction]() + var forceCancelableOnClickOutside = cancelable for (index, option) in options.enumerated() { let style = option["style"] as? String ?? "DEFAULT" let title = option["title"] as? String ?? "" @@ -27,6 +29,8 @@ public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin { if style == "DESTRUCTIVE" { buttonStyle = .destructive } else if style == "CANCEL" { + // if there's a cancel action, then it will already be cancelable when clicked outside + forceCancelableOnClickOutside = false buttonStyle = .cancel } let action = UIAlertAction(title: title, style: buttonStyle, handler: { (_) -> Void in @@ -40,9 +44,31 @@ public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin { DispatchQueue.main.async { [weak self] in if let alertController = self?.implementation.buildActionSheet(title: title, message: message, actions: alertActions) { self?.setCenteredPopover(alertController) - self?.bridge?.viewController?.present(alertController, animated: true, completion: nil) + self?.bridge?.viewController?.present(alertController, animated: true) { + if (forceCancelableOnClickOutside) { + let gestureRecognizer = TapGestureRecognizerWithClosure { + alertController.dismiss(animated: true, completion: nil) + call.reject("User canceled action sheet") + } + let backroundView = alertController.view.superview?.subviews[0] + backroundView?.addGestureRecognizer(gestureRecognizer) + } + } } } } + + private final class TapGestureRecognizerWithClosure: UITapGestureRecognizer { + private let onTap: () -> Void + init(onTap: @escaping () -> Void) { + self.onTap = onTap + super.init(target: nil, action: nil) + self.addTarget(self, action: #selector(action)) + } + + @objc private func action() { + onTap() + } + } } From dd4bb5a26fbd6081eb5681f34584fc9c5d2b2d7e Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Tue, 17 Dec 2024 17:58:20 +0000 Subject: [PATCH 03/15] refactor: Return canceled boolean instead of error References: https://outsystemsrd.atlassian.net/browse/RMET-3576 --- .../plugins/actionsheet/ActionSheetPlugin.java | 17 ++++++++++------- .../ActionSheetPlugin/ActionSheetPlugin.swift | 8 ++++++-- action-sheet/src/definitions.ts | 12 ++++++++++-- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheetPlugin.java b/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheetPlugin.java index 189c527a0..613369b0a 100644 --- a/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheetPlugin.java +++ b/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheetPlugin.java @@ -43,16 +43,11 @@ public void showActions(final PluginCall call) { implementation.setCancelable(cancelable); if (cancelable) { implementation.setOnCancelListener( - () -> call.reject("User canceled action sheet") + () -> resolve(call, -1) ); } implementation.setOnSelectedListener( - index -> { - JSObject ret = new JSObject(); - ret.put("index", index); - call.resolve(ret); - implementation.dismiss(); - } + index -> resolve(call, index) ); implementation.show(getActivity().getSupportFragmentManager(), "capacitorModalsActionSheet"); } catch (JSONException ex) { @@ -60,4 +55,12 @@ public void showActions(final PluginCall call) { call.reject("JSON error processing an option for showActions", ex); } } + + private void resolve(final PluginCall call, int selectedIndex) { + JSObject ret = new JSObject(); + ret.put("index", selectedIndex); + ret.put("canceled", selectedIndex < 0); + call.resolve(ret); + implementation.dismiss(); + } } diff --git a/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift b/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift index 74cfdad81..1b995071a 100644 --- a/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift +++ b/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift @@ -35,7 +35,8 @@ public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin { } let action = UIAlertAction(title: title, style: buttonStyle, handler: { (_) -> Void in call.resolve([ - "index": index + "index": index, + "canceled": false ]) }) alertActions.append(action) @@ -48,7 +49,10 @@ public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin { if (forceCancelableOnClickOutside) { let gestureRecognizer = TapGestureRecognizerWithClosure { alertController.dismiss(animated: true, completion: nil) - call.reject("User canceled action sheet") + call.resolve([ + "index": -1, + "canceled": true + ]) } let backroundView = alertController.view.superview?.subviews[0] backroundView?.addGestureRecognizer(gestureRecognizer) diff --git a/action-sheet/src/definitions.ts b/action-sheet/src/definitions.ts index ade83a9af..376087064 100644 --- a/action-sheet/src/definitions.ts +++ b/action-sheet/src/definitions.ts @@ -20,7 +20,7 @@ export interface ShowActionsOptions { * * On iOS, sheet is also cancelable if a button with ActionSheetButtonStyle.Cancel is provided and cancelable is false. * - * @since TODO provide release number + * @since 6.1.0 */ cancelable?: boolean; @@ -86,11 +86,19 @@ export interface ActionSheetButton { export interface ShowActionsResult { /** - * The index of the clicked option (Zero-based) + * The index of the clicked option (Zero-based), or -1 if the sheet was canceled. + * + * On iOS, if there is a button with ActionSheetButtonStyle.Cancel, and user clicks outside the sheet, the index of the cancel option is returned * * @since 1.0.0 */ index: number; + /** + * True if sheet was canceled by user; False otherwise + * + * @since 6.1.0 + */ + canceled: boolean; } export interface ActionSheetPlugin { From 1c8180661f601147c55055c558d025dc7544b952 Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Tue, 17 Dec 2024 17:59:01 +0000 Subject: [PATCH 04/15] refactor: Add cancelable parameter on Web References: https://outsystemsrd.atlassian.net/browse/RMET-3576 --- action-sheet/src/web.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/action-sheet/src/web.ts b/action-sheet/src/web.ts index c63e0f576..178610c7b 100644 --- a/action-sheet/src/web.ts +++ b/action-sheet/src/web.ts @@ -15,14 +15,23 @@ export class ActionSheetWeb extends WebPlugin implements ActionSheetPlugin { document.body.appendChild(actionSheet); } actionSheet.header = options.title; - actionSheet.cancelable = false; + actionSheet.cancelable = options.cancelable; actionSheet.options = options.options; actionSheet.addEventListener('onSelection', async (e: any) => { const selection = e.detail; resolve({ index: selection, + canceled: false }); }); + if (options.cancelable) { + actionSheet.addEventListener('onCanceled', async () => { + resolve({ + index: -1, + canceled: true + }); + }) + } }); } } From 433e38d51527fb92f7eb09844bea7fa2b1149a25 Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Tue, 17 Dec 2024 18:12:26 +0000 Subject: [PATCH 05/15] docs: Update README for action-sheet --- action-sheet/README.md | 18 ++++++++++-------- action-sheet/src/definitions.ts | 17 ++++++++--------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/action-sheet/README.md b/action-sheet/README.md index b72498423..edf72ea0d 100644 --- a/action-sheet/README.md +++ b/action-sheet/README.md @@ -84,18 +84,20 @@ to select. #### ShowActionsResult -| Prop | Type | Description | Since | -| ----------- | ------------------- | -------------------------------------------- | ----- | -| **`index`** | number | The index of the clicked option (Zero-based) | 1.0.0 | +| Prop | Type | Description | Since | +| -------------- | -------------------- | -------------------------------------------- | ----- | +| **`index`** | number | The index of the clicked option (Zero-based), or -1 if the sheet was canceled. On iOS, if there is a button with ActionSheetButtonStyle.Cancel, and user clicks outside the sheet, the index of the cancel option is returned | 1.0.0 | +| **`canceled`** | boolean | True if sheet was canceled by user; False otherwise | 6.1.0 | #### ShowActionsOptions -| Prop | Type | Description | Since | -| ------------- | -------------------------------- | ------------------------------------------------------------------------ | ----- | -| **`title`** | string | The title of the Action Sheet. | 1.0.0 | -| **`message`** | string | A message to show under the title. This option is only supported on iOS. | 1.0.0 | -| **`options`** | ActionSheetButton[] | Options the user can choose from. | 1.0.0 | +| Prop | Type | Description | Since | +| ---------------- | -------------------------------- | ------------------------------------------------------------------------ | ----- | +| **`title`** | string | The title of the Action Sheet. | 1.0.0 | +| **`message`** | string | A message to show under the title. This option is only supported on iOS. | 1.0.0 | +| **`options`** | ActionSheetButton[] | Options the user can choose from. | 1.0.0 | +| **`cancelable`** | boolean | If true, sheet is canceled when clicked outside; If false, it is not. By default, false. On iOS, the sheet is also cancelable if a button with ActionSheetButtonStyle.Cancel is provided and cancelable is false. | 6.1.0 | #### ActionSheetButton diff --git a/action-sheet/src/definitions.ts b/action-sheet/src/definitions.ts index 376087064..145a788af 100644 --- a/action-sheet/src/definitions.ts +++ b/action-sheet/src/definitions.ts @@ -15,22 +15,21 @@ export interface ShowActionsOptions { */ message?: string; + /** + * Options the user can choose from. + * + * @since 1.0.0 + */ + options: ActionSheetButton[]; + /** * If true, sheet is canceled when clicked outside; If false, it is not. By default, false. * - * On iOS, sheet is also cancelable if a button with ActionSheetButtonStyle.Cancel is provided and cancelable is false. + * On iOS, the sheet is also cancelable if a button with ActionSheetButtonStyle.Cancel is provided and cancelable is false. * * @since 6.1.0 */ cancelable?: boolean; - - - /** - * Options the user can choose from. - * - * @since 1.0.0 - */ - options: ActionSheetButton[]; } export enum ActionSheetButtonStyle { From e247aa7390d4a71b970410670367670f2493321b Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Wed, 18 Dec 2024 09:25:59 +0000 Subject: [PATCH 06/15] chore: fix lint issues --- .../plugins/actionsheet/ActionSheetPlugin.java | 10 +++------- action-sheet/src/definitions.ts | 8 ++++---- action-sheet/src/web.ts | 6 +++--- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheetPlugin.java b/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheetPlugin.java index 613369b0a..ddddbac3d 100644 --- a/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheetPlugin.java +++ b/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheetPlugin.java @@ -42,20 +42,16 @@ public void showActions(final PluginCall call) { implementation.setOptions(actionOptions); implementation.setCancelable(cancelable); if (cancelable) { - implementation.setOnCancelListener( - () -> resolve(call, -1) - ); + implementation.setOnCancelListener(() -> resolve(call, -1)); } - implementation.setOnSelectedListener( - index -> resolve(call, index) - ); + implementation.setOnSelectedListener(index -> resolve(call, index)); implementation.show(getActivity().getSupportFragmentManager(), "capacitorModalsActionSheet"); } catch (JSONException ex) { Logger.error("JSON error processing an option for showActions", ex); call.reject("JSON error processing an option for showActions", ex); } } - + private void resolve(final PluginCall call, int selectedIndex) { JSObject ret = new JSObject(); ret.put("index", selectedIndex); diff --git a/action-sheet/src/definitions.ts b/action-sheet/src/definitions.ts index 145a788af..c236dbd50 100644 --- a/action-sheet/src/definitions.ts +++ b/action-sheet/src/definitions.ts @@ -24,9 +24,9 @@ export interface ShowActionsOptions { /** * If true, sheet is canceled when clicked outside; If false, it is not. By default, false. - * + * * On iOS, the sheet is also cancelable if a button with ActionSheetButtonStyle.Cancel is provided and cancelable is false. - * + * * @since 6.1.0 */ cancelable?: boolean; @@ -86,7 +86,7 @@ export interface ActionSheetButton { export interface ShowActionsResult { /** * The index of the clicked option (Zero-based), or -1 if the sheet was canceled. - * + * * On iOS, if there is a button with ActionSheetButtonStyle.Cancel, and user clicks outside the sheet, the index of the cancel option is returned * * @since 1.0.0 @@ -94,7 +94,7 @@ export interface ShowActionsResult { index: number; /** * True if sheet was canceled by user; False otherwise - * + * * @since 6.1.0 */ canceled: boolean; diff --git a/action-sheet/src/web.ts b/action-sheet/src/web.ts index 178610c7b..3a517c5fc 100644 --- a/action-sheet/src/web.ts +++ b/action-sheet/src/web.ts @@ -21,16 +21,16 @@ export class ActionSheetWeb extends WebPlugin implements ActionSheetPlugin { const selection = e.detail; resolve({ index: selection, - canceled: false + canceled: false, }); }); if (options.cancelable) { actionSheet.addEventListener('onCanceled', async () => { resolve({ index: -1, - canceled: true + canceled: true, }); - }) + }); } }); } From 99443aeefa17c7281542b810aac5837e512edb82 Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Mon, 13 Jan 2025 11:05:37 +0000 Subject: [PATCH 07/15] refactor(action-sheet): Explicitly get string and bool arguments --- .../ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift b/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift index 1b995071a..4c2586d54 100644 --- a/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift +++ b/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift @@ -15,9 +15,9 @@ public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin { private let implementation = ActionSheet() @objc func showActions(_ call: CAPPluginCall) { - let title = call.options["title"] as? String - let message = call.options["message"] as? String - let cancelable = call.options["cancelable"] as? Bool ?? false + let title = call.getString("title") + let message = call.getString("message") + let cancelable = call.getBool("cancelable", false) let options = call.getArray("options", JSObject.self) ?? [] var alertActions = [UIAlertAction]() From 89f87b25cc315b0144c67bcd4b69f5ae3cddf96d Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Mon, 13 Jan 2025 11:05:54 +0000 Subject: [PATCH 08/15] chore(action-sheet) update README.md --- action-sheet/README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/action-sheet/README.md b/action-sheet/README.md index edf72ea0d..8641a0aa3 100644 --- a/action-sheet/README.md +++ b/action-sheet/README.md @@ -84,20 +84,20 @@ to select. #### ShowActionsResult -| Prop | Type | Description | Since | -| -------------- | -------------------- | -------------------------------------------- | ----- | -| **`index`** | number | The index of the clicked option (Zero-based), or -1 if the sheet was canceled. On iOS, if there is a button with ActionSheetButtonStyle.Cancel, and user clicks outside the sheet, the index of the cancel option is returned | 1.0.0 | -| **`canceled`** | boolean | True if sheet was canceled by user; False otherwise | 6.1.0 | +| Prop | Type | Description | Since | +| -------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | +| **`index`** | number | The index of the clicked option (Zero-based), or -1 if the sheet was canceled. On iOS, if there is a button with ActionSheetButtonStyle.Cancel, and user clicks outside the sheet, the index of the cancel option is returned | 1.0.0 | +| **`canceled`** | boolean | True if sheet was canceled by user; False otherwise | 6.1.0 | #### ShowActionsOptions -| Prop | Type | Description | Since | -| ---------------- | -------------------------------- | ------------------------------------------------------------------------ | ----- | -| **`title`** | string | The title of the Action Sheet. | 1.0.0 | -| **`message`** | string | A message to show under the title. This option is only supported on iOS. | 1.0.0 | -| **`options`** | ActionSheetButton[] | Options the user can choose from. | 1.0.0 | -| **`cancelable`** | boolean | If true, sheet is canceled when clicked outside; If false, it is not. By default, false. On iOS, the sheet is also cancelable if a button with ActionSheetButtonStyle.Cancel is provided and cancelable is false. | 6.1.0 | +| Prop | Type | Description | Since | +| ---------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | +| **`title`** | string | The title of the Action Sheet. | 1.0.0 | +| **`message`** | string | A message to show under the title. This option is only supported on iOS. | 1.0.0 | +| **`options`** | ActionSheetButton[] | Options the user can choose from. | 1.0.0 | +| **`cancelable`** | boolean | If true, sheet is canceled when clicked outside; If false, it is not. By default, false. On iOS, the sheet is also cancelable if a button with ActionSheetButtonStyle.Cancel is provided and cancelable is false. | 6.1.0 | #### ActionSheetButton From a9431d306f63e0297112e483f5a44abce44c7a72 Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Thu, 19 Feb 2026 18:32:19 +0000 Subject: [PATCH 09/15] chore: run fmt --- .../ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift | 4 ++-- .../java/com/capacitorjs/plugins/camera/CameraPlugin.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift b/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift index 0332a54bd..39e312565 100644 --- a/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift +++ b/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift @@ -46,7 +46,7 @@ public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin { if let alertController = self?.implementation.buildActionSheet(title: title, message: message, actions: alertActions) { self?.setCenteredPopover(alertController) self?.bridge?.viewController?.present(alertController, animated: true) { - if (forceCancelableOnClickOutside) { + if forceCancelableOnClickOutside { let gestureRecognizer = TapGestureRecognizerWithClosure { alertController.dismiss(animated: true, completion: nil) call.resolve([ @@ -61,7 +61,7 @@ public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin { } } } - + private final class TapGestureRecognizerWithClosure: UITapGestureRecognizer { private let onTap: () -> Void diff --git a/camera/android/src/main/java/com/capacitorjs/plugins/camera/CameraPlugin.java b/camera/android/src/main/java/com/capacitorjs/plugins/camera/CameraPlugin.java index c952d0e57..a5598e39d 100644 --- a/camera/android/src/main/java/com/capacitorjs/plugins/camera/CameraPlugin.java +++ b/camera/android/src/main/java/com/capacitorjs/plugins/camera/CameraPlugin.java @@ -707,7 +707,8 @@ private Uri getTempImage(Uri u, ByteArrayOutputStream bitmapOutputStream) { try { bis = new ByteArrayInputStream(bitmapOutputStream.toByteArray()); newUri = saveImage(u, bis); - } catch (IOException ex) {} finally { + } catch (IOException ex) { + } finally { if (bis != null) { try { bis.close(); From e9531a98a3d68dbc202fd61fbfbb448ded935df3 Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Thu, 19 Feb 2026 18:36:22 +0000 Subject: [PATCH 10/15] docs: Update new parameters documentation --- action-sheet/README.md | 14 +++++++------- action-sheet/src/definitions.ts | 10 +++++++--- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/action-sheet/README.md b/action-sheet/README.md index e8a2c41aa..b34e26e04 100644 --- a/action-sheet/README.md +++ b/action-sheet/README.md @@ -87,17 +87,17 @@ to select. | Prop | Type | Description | Since | | -------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`index`** | number | The index of the clicked option (Zero-based), or -1 if the sheet was canceled. On iOS, if there is a button with ActionSheetButtonStyle.Cancel, and user clicks outside the sheet, the index of the cancel option is returned | 1.0.0 | -| **`canceled`** | boolean | True if sheet was canceled by user; False otherwise | 6.1.0 | +| **`canceled`** | boolean | True if sheet was canceled by user; False otherwise On Web, requires having @ionic/pwa-elements version 3.4.0 or higher. | 8.1.0 | #### ShowActionsOptions -| Prop | Type | Description | Since | -| ---------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | -| **`title`** | string | The title of the Action Sheet. | 1.0.0 | -| **`message`** | string | A message to show under the title. This option is only supported on iOS. | 1.0.0 | -| **`options`** | ActionSheetButton[] | Options the user can choose from. | 1.0.0 | -| **`cancelable`** | boolean | If true, sheet is canceled when clicked outside; If false, it is not. By default, false. On iOS, the sheet is also cancelable if a button with ActionSheetButtonStyle.Cancel is provided and cancelable is false. | 6.1.0 | +| Prop | Type | Description | Since | +| ---------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | +| **`title`** | string | The title of the Action Sheet. | 1.0.0 | +| **`message`** | string | A message to show under the title. This option is only supported on iOS. | 1.0.0 | +| **`options`** | ActionSheetButton[] | Options the user can choose from. | 1.0.0 | +| **`cancelable`** | boolean | If true, sheet is canceled when clicked outside; If false, it is not. By default, false. On iOS, the sheet is also cancelable if a button with ActionSheetButtonStyle.Cancel is provided and cancelable is false. On Web, requires having @ionic/pwa-elements version 3.4.0 or higher. | 8.1.0 | #### ActionSheetButton diff --git a/action-sheet/src/definitions.ts b/action-sheet/src/definitions.ts index c236dbd50..1af080092 100644 --- a/action-sheet/src/definitions.ts +++ b/action-sheet/src/definitions.ts @@ -26,8 +26,10 @@ export interface ShowActionsOptions { * If true, sheet is canceled when clicked outside; If false, it is not. By default, false. * * On iOS, the sheet is also cancelable if a button with ActionSheetButtonStyle.Cancel is provided and cancelable is false. + * + * On Web, requires having @ionic/pwa-elements version 3.4.0 or higher. * - * @since 6.1.0 + * @since 8.1.0 */ cancelable?: boolean; } @@ -94,8 +96,10 @@ export interface ShowActionsResult { index: number; /** * True if sheet was canceled by user; False otherwise - * - * @since 6.1.0 + * + * On Web, requires having @ionic/pwa-elements version 3.4.0 or higher. + * + * @since 8.1.0 */ canceled: boolean; } From 2dc1aaa9bb2c16ab328240472d6fb9ee909e61b9 Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Fri, 20 Feb 2026 13:50:51 +0000 Subject: [PATCH 11/15] fix: Remove cancelable logic from iOS Cannot consitently set action sheet / UIAlertController as non-cancelable across iOS versions, particularly iOS 26. --- .../ActionSheetPlugin/ActionSheetPlugin.swift | 49 +++++++------------ 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift b/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift index 39e312565..a630fd43a 100644 --- a/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift +++ b/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift @@ -6,22 +6,21 @@ import Capacitor * here: https://capacitorjs.com/docs/plugins/ios */ @objc(ActionSheetPlugin) -public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin { +public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin, UIAdaptivePresentationControllerDelegate { public let identifier = "ActionSheetPlugin" public let jsName = "ActionSheet" public let pluginMethods: [CAPPluginMethod] = [ CAPPluginMethod(name: "showActions", returnType: CAPPluginReturnPromise) ] private let implementation = ActionSheet() + private var currentCall: CAPPluginCall? @objc func showActions(_ call: CAPPluginCall) { - let title = call.getString("title") - let message = call.getString("message") - let cancelable = call.getBool("cancelable", false) + let title = call.options["title"] as? String + let message = call.options["message"] as? String let options = call.getArray("options", JSObject.self) ?? [] var alertActions = [UIAlertAction]() - var forceCancelableOnClickOutside = cancelable for (index, option) in options.enumerated() { let style = option["style"] as? String ?? "DEFAULT" let title = option["title"] as? String ?? "" @@ -30,49 +29,37 @@ public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin { buttonStyle = .destructive } else if style == "CANCEL" { // if there's a cancel action, then it will already be cancelable when clicked outside - forceCancelableOnClickOutside = false buttonStyle = .cancel } - let action = UIAlertAction(title: title, style: buttonStyle, handler: { (_) in + let action = UIAlertAction(title: title, style: buttonStyle, handler: { [weak self] (_) in call.resolve([ "index": index, "canceled": false ]) + self?.currentCall = nil }) alertActions.append(action) } DispatchQueue.main.async { [weak self] in if let alertController = self?.implementation.buildActionSheet(title: title, message: message, actions: alertActions) { + self?.currentCall = call + + alertController.presentationController?.delegate = self + self?.setCenteredPopover(alertController) - self?.bridge?.viewController?.present(alertController, animated: true) { - if forceCancelableOnClickOutside { - let gestureRecognizer = TapGestureRecognizerWithClosure { - alertController.dismiss(animated: true, completion: nil) - call.resolve([ - "index": -1, - "canceled": true - ]) - } - let backroundView = alertController.view.superview?.subviews[0] - backroundView?.addGestureRecognizer(gestureRecognizer) - } - } + self?.bridge?.viewController?.present(alertController, animated: true, completion: nil) } } } - private final class TapGestureRecognizerWithClosure: UITapGestureRecognizer { - private let onTap: () -> Void + // MARK: - UIAdaptivePresentationControllerDelegate - init(onTap: @escaping () -> Void) { - self.onTap = onTap - super.init(target: nil, action: nil) - self.addTarget(self, action: #selector(action)) - } - - @objc private func action() { - onTap() - } + public func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { + self.currentCall?.resolve([ + "index": -1, + "canceled": true + ]) + self.currentCall = nil } } From c3203e604c24668ed9055a6d1e2245e60652286a Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Fri, 20 Feb 2026 14:08:12 +0000 Subject: [PATCH 12/15] fix(ios): return canceled with .cancel button --- .../ActionSheetPlugin/ActionSheetPlugin.swift | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift b/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift index a630fd43a..cc9283fe1 100644 --- a/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift +++ b/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift @@ -28,14 +28,20 @@ public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin, UIAdaptivePresentat if style == "DESTRUCTIVE" { buttonStyle = .destructive } else if style == "CANCEL" { - // if there's a cancel action, then it will already be cancelable when clicked outside buttonStyle = .cancel } let action = UIAlertAction(title: title, style: buttonStyle, handler: { [weak self] (_) in - call.resolve([ - "index": index, - "canceled": false - ]) + if (buttonStyle == .cancel) { + call.resolve([ + "index": -1, + "canceled": true + ]) + } else { + call.resolve([ + "index": index, + "canceled": false + ]) + } self?.currentCall = nil }) alertActions.append(action) From 81c80f99b757ea8c7f272ea05f87d0c721839676 Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Fri, 20 Feb 2026 14:09:24 +0000 Subject: [PATCH 13/15] chore: fix lint issues --- .../ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift b/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift index cc9283fe1..76b168429 100644 --- a/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift +++ b/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift @@ -31,7 +31,7 @@ public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin, UIAdaptivePresentat buttonStyle = .cancel } let action = UIAlertAction(title: title, style: buttonStyle, handler: { [weak self] (_) in - if (buttonStyle == .cancel) { + if buttonStyle == .cancel { call.resolve([ "index": -1, "canceled": true From 1c1394e389fe8ea330af8e6f59e24d5f6ae97026 Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Fri, 20 Feb 2026 14:10:32 +0000 Subject: [PATCH 14/15] docs: Update new action sheet parameters --- action-sheet/README.md | 12 ++++++------ action-sheet/src/definitions.ts | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/action-sheet/README.md b/action-sheet/README.md index b34e26e04..ea657acd4 100644 --- a/action-sheet/README.md +++ b/action-sheet/README.md @@ -92,12 +92,12 @@ to select. #### ShowActionsOptions -| Prop | Type | Description | Since | -| ---------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | -| **`title`** | string | The title of the Action Sheet. | 1.0.0 | -| **`message`** | string | A message to show under the title. This option is only supported on iOS. | 1.0.0 | -| **`options`** | ActionSheetButton[] | Options the user can choose from. | 1.0.0 | -| **`cancelable`** | boolean | If true, sheet is canceled when clicked outside; If false, it is not. By default, false. On iOS, the sheet is also cancelable if a button with ActionSheetButtonStyle.Cancel is provided and cancelable is false. On Web, requires having @ionic/pwa-elements version 3.4.0 or higher. | 8.1.0 | +| Prop | Type | Description | Since | +| ---------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | +| **`title`** | string | The title of the Action Sheet. | 1.0.0 | +| **`message`** | string | A message to show under the title. This option is only supported on iOS. | 1.0.0 | +| **`options`** | ActionSheetButton[] | Options the user can choose from. | 1.0.0 | +| **`cancelable`** | boolean | If true, sheet is canceled when clicked outside; If false, it is not. By default, false. Not available on iOS, sheet is always cancelable by clicking outside of it. On Web, requires having @ionic/pwa-elements version 3.4.0 or higher. | 8.1.0 | #### ActionSheetButton diff --git a/action-sheet/src/definitions.ts b/action-sheet/src/definitions.ts index 1af080092..09b0470e2 100644 --- a/action-sheet/src/definitions.ts +++ b/action-sheet/src/definitions.ts @@ -25,8 +25,8 @@ export interface ShowActionsOptions { /** * If true, sheet is canceled when clicked outside; If false, it is not. By default, false. * - * On iOS, the sheet is also cancelable if a button with ActionSheetButtonStyle.Cancel is provided and cancelable is false. - * + * Not available on iOS, sheet is always cancelable by clicking outside of it. + * * On Web, requires having @ionic/pwa-elements version 3.4.0 or higher. * * @since 8.1.0 @@ -96,9 +96,9 @@ export interface ShowActionsResult { index: number; /** * True if sheet was canceled by user; False otherwise - * + * * On Web, requires having @ionic/pwa-elements version 3.4.0 or higher. - * + * * @since 8.1.0 */ canceled: boolean; From 9d2181ffa26e58b663e68f06b318bbaca2678e87 Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Fri, 20 Feb 2026 16:01:41 +0000 Subject: [PATCH 15/15] fix(ios): Backwards compatible behavior with iOS 18 and below --- .../ActionSheetPlugin/ActionSheetPlugin.swift | 59 +++++++++++++++---- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift b/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift index 76b168429..b4a2b83ff 100644 --- a/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift +++ b/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift @@ -21,6 +21,7 @@ public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin, UIAdaptivePresentat let options = call.getArray("options", JSObject.self) ?? [] var alertActions = [UIAlertAction]() + var hasCancellableButton = false for (index, option) in options.enumerated() { let style = option["style"] as? String ?? "DEFAULT" let title = option["title"] as? String ?? "" @@ -28,14 +29,12 @@ public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin, UIAdaptivePresentat if style == "DESTRUCTIVE" { buttonStyle = .destructive } else if style == "CANCEL" { + hasCancellableButton = true buttonStyle = .cancel } let action = UIAlertAction(title: title, style: buttonStyle, handler: { [weak self] (_) in if buttonStyle == .cancel { - call.resolve([ - "index": -1, - "canceled": true - ]) + call.actionSheetCanceled() } else { call.resolve([ "index": index, @@ -49,23 +48,61 @@ public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin, UIAdaptivePresentat DispatchQueue.main.async { [weak self] in if let alertController = self?.implementation.buildActionSheet(title: title, message: message, actions: alertActions) { - self?.currentCall = call - - alertController.presentationController?.delegate = self - self?.setCenteredPopover(alertController) - self?.bridge?.viewController?.present(alertController, animated: true, completion: nil) + self?.bridge?.viewController?.present(alertController, animated: true) { + if !hasCancellableButton { + self?.setupCancelationListerners(alertController, call) + } + } } } } + private func setupCancelationListerners(_ alertController: UIAlertController, _ call: CAPPluginCall) { + if #available(iOS 26, *) { + self.currentCall = call + alertController.presentationController?.delegate = self + } else { + // For iOS versions below 26, setting the presentation controller delegate would result in a crash + // "Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'The presentation controller of an alert controller presenting as an alert must not have its delegate modified" + // Hence, the alternative by adding a gesture recognizer (which only works for iOS versions below 26) + let gestureRecognizer = TapGestureRecognizerWithClosure { + alertController.dismiss(animated: true, completion: nil) + call.actionSheetCanceled() + } + let backroundView = alertController.view.superview?.subviews[0] + backroundView?.addGestureRecognizer(gestureRecognizer) + } + } + // MARK: - UIAdaptivePresentationControllerDelegate public func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { - self.currentCall?.resolve([ + self.currentCall?.actionSheetCanceled() + self.currentCall = nil + } +} + +// MARK: - TapGestureRecognizerWithClosure +private final class TapGestureRecognizerWithClosure: UITapGestureRecognizer { + private let onTap: () -> Void + + init(onTap: @escaping () -> Void) { + self.onTap = onTap + super.init(target: nil, action: nil) + self.addTarget(self, action: #selector(action)) + } + + @objc private func action() { + onTap() + } +} + +private extension CAPPluginCall { + func actionSheetCanceled() { + resolve([ "index": -1, "canceled": true ]) - self.currentCall = nil } }