@capgo/capacitor-patch 8.1.0 → 8.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +195 -11
  2. package/package.json +1 -1
  3. package/patches/catalog.json +657 -9
  4. package/patches/upstream-pr-6991-android-native-bridge.patch +15 -0
  5. package/patches/upstream-pr-6991-ios-native-bridge.patch +15 -0
  6. package/patches/upstream-pr-7301-core.patch +38 -0
  7. package/patches/upstream-pr-7419-ios.patch +13 -0
  8. package/patches/upstream-pr-7420-ios.patch +13 -0
  9. package/patches/upstream-pr-7446-android.patch +14 -0
  10. package/patches/upstream-pr-7490-android.patch +13 -0
  11. package/patches/upstream-pr-7490-ios.patch +13 -0
  12. package/patches/upstream-pr-7535-android.patch +13 -0
  13. package/patches/upstream-pr-7599-ios.patch +86 -0
  14. package/patches/upstream-pr-7732-android-native-bridge.patch +12 -0
  15. package/patches/upstream-pr-7732-ios-native-bridge.patch +12 -0
  16. package/patches/upstream-pr-7781-android.patch +24 -0
  17. package/patches/upstream-pr-7803-android.patch +13 -0
  18. package/patches/upstream-pr-7831-ios.patch +13 -0
  19. package/patches/upstream-pr-7987-android.patch +42 -0
  20. package/patches/upstream-pr-8087-android.patch +64 -0
  21. package/patches/upstream-pr-8188-cli.patch +18 -0
  22. package/patches/upstream-pr-8190-android.patch +13 -0
  23. package/patches/upstream-pr-8249-ios.patch +32 -0
  24. package/patches/upstream-pr-8252-cli.patch +31 -0
  25. package/patches/upstream-pr-8271-core.patch +30 -0
  26. package/patches/upstream-pr-8275-android.patch +158 -0
  27. package/patches/upstream-pr-8304-ios.patch +36 -0
  28. package/patches/upstream-pr-8418-android.patch +82 -0
  29. package/patches/upstream-pr-8424-android.patch +55 -0
  30. package/patches/upstream-pr-8429-android.patch +19 -0
  31. package/patches/upstream-pr-8454-android.patch +207 -0
  32. package/patches/upstream-pr-8458-cli.patch +14 -0
  33. package/scripts/capacitor-patch/runner.mjs +38 -4
  34. package/patches/capacitor-cli-spm-ios-minor-platform.patch +0 -40
@@ -0,0 +1,158 @@
1
+ diff --git a/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java b/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java
2
+ index 6ca1c8a8..6b9f603d 100644
3
+ --- a/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java
4
+ +++ b/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java
5
+ @@ -46,6 +46,17 @@ public class BridgeWebChromeClient extends WebChromeClient {
6
+ void onActivityResult(ActivityResult result);
7
+ }
8
+
9
+ + // Static variables to store pending file chooser state to survive activity recreation
10
+ + private static ValueCallback<Uri[]> pendingFilePathCallback;
11
+ + private static Uri pendingImageFileUri;
12
+ + private static FileChooserType pendingFileChooserType;
13
+ +
14
+ + private enum FileChooserType {
15
+ + IMAGE_CAPTURE,
16
+ + VIDEO_CAPTURE,
17
+ + FILE_PICKER
18
+ + }
19
+ +
20
+ private ActivityResultLauncher permissionLauncher;
21
+ private ActivityResultLauncher activityLauncher;
22
+ private PermissionListener permissionListener;
23
+ @@ -70,10 +81,70 @@ public class BridgeWebChromeClient extends WebChromeClient {
24
+ activityLauncher = bridge.registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), (result) -> {
25
+ if (activityListener != null) {
26
+ activityListener.onActivityResult(result);
27
+ + } else if (pendingFilePathCallback != null) {
28
+ + // Handle case where activity was recreated and instance callback is null
29
+ + // Use the static callback instead
30
+ + handlePendingFileChooserResult(result);
31
+ }
32
+ });
33
+ }
34
+
35
+ + /**
36
+ + * Handle file chooser result when the activity was recreated and instance callbacks were lost.
37
+ + * This uses the static pending callback to deliver the result.
38
+ + */
39
+ + private void handlePendingFileChooserResult(ActivityResult result) {
40
+ + if (pendingFilePathCallback == null) {
41
+ + return;
42
+ + }
43
+ +
44
+ + try {
45
+ + Uri[] uriResult = null;
46
+ + if (result.getResultCode() == Activity.RESULT_OK) {
47
+ + switch (pendingFileChooserType) {
48
+ + case IMAGE_CAPTURE:
49
+ + if (pendingImageFileUri != null) {
50
+ + uriResult = new Uri[] { pendingImageFileUri };
51
+ + }
52
+ + break;
53
+ + case VIDEO_CAPTURE:
54
+ + Intent videoData = result.getData();
55
+ + if (videoData != null && videoData.getData() != null) {
56
+ + uriResult = new Uri[] { videoData.getData() };
57
+ + }
58
+ + break;
59
+ + case FILE_PICKER:
60
+ + Intent fileData = result.getData();
61
+ + if (fileData != null) {
62
+ + if (fileData.getClipData() != null) {
63
+ + final int numFiles = fileData.getClipData().getItemCount();
64
+ + uriResult = new Uri[numFiles];
65
+ + for (int i = 0; i < numFiles; i++) {
66
+ + uriResult[i] = fileData.getClipData().getItemAt(i).getUri();
67
+ + }
68
+ + } else {
69
+ + uriResult = WebChromeClient.FileChooserParams.parseResult(result.getResultCode(), fileData);
70
+ + }
71
+ + }
72
+ + break;
73
+ + }
74
+ + }
75
+ + pendingFilePathCallback.onReceiveValue(uriResult);
76
+ + } finally {
77
+ + // Clear the static state after handling
78
+ + clearPendingFileChooserState();
79
+ + }
80
+ + }
81
+ +
82
+ + /**
83
+ + * Clear the static pending file chooser state.
84
+ + */
85
+ + private static void clearPendingFileChooserState() {
86
+ + pendingFilePathCallback = null;
87
+ + pendingImageFileUri = null;
88
+ + pendingFileChooserType = null;
89
+ + }
90
+ +
91
+ /**
92
+ * Render web content in `view`.
93
+ *
94
+ @@ -340,12 +411,19 @@ public class BridgeWebChromeClient extends WebChromeClient {
95
+ return false;
96
+ }
97
+ takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageFileUri);
98
+ +
99
+ + // Store in static variables to survive activity recreation
100
+ + pendingFilePathCallback = filePathCallback;
101
+ + pendingImageFileUri = imageFileUri;
102
+ + pendingFileChooserType = FileChooserType.IMAGE_CAPTURE;
103
+ +
104
+ activityListener = (activityResult) -> {
105
+ Uri[] result = null;
106
+ if (activityResult.getResultCode() == Activity.RESULT_OK) {
107
+ result = new Uri[] { imageFileUri };
108
+ }
109
+ filePathCallback.onReceiveValue(result);
110
+ + clearPendingFileChooserState();
111
+ };
112
+ activityLauncher.launch(takePictureIntent);
113
+
114
+ @@ -359,12 +437,18 @@ public class BridgeWebChromeClient extends WebChromeClient {
115
+ return false;
116
+ }
117
+
118
+ + // Store in static variables to survive activity recreation
119
+ + pendingFilePathCallback = filePathCallback;
120
+ + pendingImageFileUri = null;
121
+ + pendingFileChooserType = FileChooserType.VIDEO_CAPTURE;
122
+ +
123
+ activityListener = (activityResult) -> {
124
+ Uri[] result = null;
125
+ if (activityResult.getResultCode() == Activity.RESULT_OK) {
126
+ result = new Uri[] { activityResult.getData().getData() };
127
+ }
128
+ filePathCallback.onReceiveValue(result);
129
+ + clearPendingFileChooserState();
130
+ };
131
+ activityLauncher.launch(takeVideoIntent);
132
+
133
+ @@ -383,6 +467,12 @@ public class BridgeWebChromeClient extends WebChromeClient {
134
+ intent.setType(validTypes[0]);
135
+ }
136
+ }
137
+ +
138
+ + // Store in static variables to survive activity recreation
139
+ + pendingFilePathCallback = filePathCallback;
140
+ + pendingImageFileUri = null;
141
+ + pendingFileChooserType = FileChooserType.FILE_PICKER;
142
+ +
143
+ try {
144
+ activityListener = (activityResult) -> {
145
+ Uri[] result;
146
+ @@ -397,10 +487,12 @@ public class BridgeWebChromeClient extends WebChromeClient {
147
+ result = WebChromeClient.FileChooserParams.parseResult(activityResult.getResultCode(), resultIntent);
148
+ }
149
+ filePathCallback.onReceiveValue(result);
150
+ + clearPendingFileChooserState();
151
+ };
152
+ activityLauncher.launch(intent);
153
+ } catch (ActivityNotFoundException e) {
154
+ filePathCallback.onReceiveValue(null);
155
+ + clearPendingFileChooserState();
156
+ }
157
+ }
158
+
@@ -0,0 +1,36 @@
1
+ diff --git a/Capacitor/Capacitor/Plugins/HttpRequestHandler.swift b/Capacitor/Capacitor/Plugins/HttpRequestHandler.swift
2
+ index 651c1b76..2230b32d 100644
3
+ --- a/Capacitor/Capacitor/Plugins/HttpRequestHandler.swift
4
+ +++ b/Capacitor/Capacitor/Plugins/HttpRequestHandler.swift
5
+ @@ -224,11 +224,26 @@ open class HttpRequestHandler {
6
+ call.reject(error.localizedDescription, (error as NSError).domain, error, nil)
7
+ return
8
+ }
9
+ -
10
+ - setCookiesFromResponse(response as! HTTPURLResponse, config)
11
+ -
12
+ - let type = ResponseType(rawValue: responseType) ?? .default
13
+ - call.resolve(self.buildResponse(data, response as! HTTPURLResponse, responseType: type))
14
+ +
15
+ + if let response = response as? HTTPURLResponse {
16
+ + setCookiesFromResponse(response, config)
17
+ +
18
+ + let type = ResponseType(rawValue: responseType) ?? .default
19
+ + call.resolve(self.buildResponse(data, response, responseType: type))
20
+ + } else if let response = response as? NSURLResponse {
21
+ + // applicable to data: URI requests
22
+ + var headers = [:] as [String: Any]
23
+ + headers["Content-Type"] = response.mimeType
24
+ +
25
+ + var output = [:] as [String: Any]
26
+ + output["status"] = 200
27
+ + output["headers"] = headers
28
+ + output["data"] = String(data: data!, encoding: .utf8)
29
+ + call.resolve(output)
30
+ + } else {
31
+ + call.reject("Unknown response kind")
32
+ + return
33
+ + }
34
+ }
35
+
36
+ task.resume()
@@ -0,0 +1,82 @@
1
+ diff --git a/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java b/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java
2
+ index a39483de..a952f2ca 100755
3
+ --- a/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java
4
+ +++ b/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java
5
+ @@ -348,16 +348,22 @@ public class WebViewLocalServer {
6
+ Map<String, String> tempResponseHeaders = handler.buildDefaultResponseHeaders();
7
+ int statusCode = 206;
8
+ try {
9
+ - int totalRange = responseStream.available();
10
+ + int totalSize = responseStream.available();
11
+ String[] parts = rangeString.split("=");
12
+ String[] streamParts = parts[1].split("-");
13
+ - String fromRange = streamParts[0];
14
+ - int range = totalRange - 1;
15
+ - if (streamParts.length > 1) {
16
+ - range = Integer.parseInt(streamParts[1]);
17
+ + int fromRange = Integer.parseInt(streamParts[0]);
18
+ + int endRange = totalSize - 1;
19
+ + if (streamParts.length > 1 && !streamParts[1].isEmpty()) {
20
+ + endRange = Integer.parseInt(streamParts[1]);
21
+ }
22
+ +
23
+ + // Truncate the stream at the end of the requested range.
24
+ + // Somewhere in the request pipeline, the stream gets automatically
25
+ + // seeked to the correct start position, so we don't need to skip to the fromRange position here.
26
+ + responseStream = new BoundedInputStream(responseStream, endRange + 1);
27
+ +
28
+ tempResponseHeaders.put("Accept-Ranges", "bytes");
29
+ - tempResponseHeaders.put("Content-Range", "bytes " + fromRange + "-" + range + "/" + totalRange);
30
+ + tempResponseHeaders.put("Content-Range", "bytes " + fromRange + "-" + endRange + "/" + totalSize);
31
+ } catch (IOException e) {
32
+ statusCode = 404;
33
+ }
34
+ @@ -750,6 +756,48 @@ public class WebViewLocalServer {
35
+ }
36
+ }
37
+
38
+ + /**
39
+ + * An InputStream wrapper that limits the number of bytes that can be read.
40
+ + */
41
+ + static class BoundedInputStream extends InputStream {
42
+ +
43
+ + private final InputStream in;
44
+ + private long remaining;
45
+ +
46
+ + public BoundedInputStream(InputStream in, long limit) {
47
+ + this.in = in;
48
+ + this.remaining = limit;
49
+ + }
50
+ +
51
+ + @Override
52
+ + public int available() throws IOException {
53
+ + int available = in.available();
54
+ + return (int) Math.min(available, remaining);
55
+ + }
56
+ +
57
+ + @Override
58
+ + public int read() throws IOException {
59
+ + if (remaining <= 0) return -1;
60
+ + int result = in.read();
61
+ + if (result != -1) remaining--;
62
+ + return result;
63
+ + }
64
+ +
65
+ + @Override
66
+ + public int read(byte[] b, int off, int len) throws IOException {
67
+ + if (remaining <= 0) return -1;
68
+ + int toRead = (int) Math.min(len, remaining);
69
+ + int result = in.read(b, off, toRead);
70
+ + if (result > 0) remaining -= result;
71
+ + return result;
72
+ + }
73
+ +
74
+ + @Override
75
+ + public void close() throws IOException {
76
+ + in.close();
77
+ + }
78
+ + }
79
+ +
80
+ // For L and above.
81
+ private static class LollipopLazyInputStream extends LazyInputStream {
82
+
@@ -0,0 +1,55 @@
1
+ diff --git a/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java b/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java
2
+ index b335b4b1..76e74207 100644
3
+ --- a/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java
4
+ +++ b/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java
5
+ @@ -164,13 +164,18 @@ public class SystemBars extends Plugin {
6
+ }
7
+
8
+ private void initSafeAreaCSSVariables() {
9
+ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && insetHandlingEnabled) {
10
+ + WindowInsetsCompat insets;
11
+ +
12
+ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
13
+ View v = (View) this.getBridge().getWebView().getParent();
14
+ - WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(v);
15
+ - if (insets != null) {
16
+ - Insets safeAreaInsets = calcSafeAreaInsets(insets);
17
+ - injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
18
+ - }
19
+ + insets = ViewCompat.getRootWindowInsets(v);
20
+ + } else {
21
+ + insets = WindowInsetsCompat.CONSUMED;
22
+ + }
23
+ +
24
+ + if (insets != null) {
25
+ + Insets safeAreaInsets = calcSafeAreaInsets(insets);
26
+ + injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
27
+ }
28
+ }
29
+
30
+ @@ -186,10 +191,8 @@ public class SystemBars extends Plugin {
31
+ // We need to correct for a possible shown IME
32
+ v.setPadding(0, 0, 0, keyboardVisible ? imeInsets.bottom : 0);
33
+
34
+ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && hasViewportCover && insetHandlingEnabled) {
35
+ - Insets safeAreaInsets = calcSafeAreaInsets(insets);
36
+ - injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
37
+ - }
38
+ + Insets safeAreaInsets = calcSafeAreaInsets(insets);
39
+ + injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
40
+
41
+ return new WindowInsetsCompat.Builder(insets)
42
+ .setInsets(
43
+ @@ -221,10 +224,8 @@ public class SystemBars extends Plugin {
44
+ .setInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(), Insets.of(0, 0, 0, 0))
45
+ .build();
46
+
47
+ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && hasViewportCover && insetHandlingEnabled) {
48
+ - Insets safeAreaInsets = calcSafeAreaInsets(newInsets);
49
+ - injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
50
+ - }
51
+ + Insets safeAreaInsets = calcSafeAreaInsets(newInsets);
52
+ + injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
53
+
54
+ return newInsets;
55
+ });
@@ -0,0 +1,19 @@
1
+ diff --git a/capacitor/src/main/java/com/getcapacitor/Bridge.java b/capacitor/src/main/java/com/getcapacitor/Bridge.java
2
+ index d4995fbb..71b658f5 100644
3
+ --- a/capacitor/src/main/java/com/getcapacitor/Bridge.java
4
+ +++ b/capacitor/src/main/java/com/getcapacitor/Bridge.java
5
+ @@ -265,8 +265,13 @@ public class Bridge {
6
+ JSInjector injector = getJSInjector();
7
+ if (WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) {
8
+ String allowedOrigin = Uri.parse(appUrl).buildUpon().path(null).fragment(null).clearQuery().build().toString();
9
+ + final String finalAllowedOrigin = allowedOrigin;
10
+ + Set<String> allowedOrigins = new HashSet<String>() {{
11
+ + add(finalAllowedOrigin);
12
+ + addAll(allowedOriginRules);
13
+ + }};
14
+ try {
15
+ - WebViewCompat.addDocumentStartJavaScript(webView, injector.getScriptString(), Collections.singleton(allowedOrigin));
16
+ + WebViewCompat.addDocumentStartJavaScript(webView, injector.getScriptString(), allowedOrigins);
17
+ injector = null;
18
+ } catch (IllegalArgumentException ex) {
19
+ Logger.warn("Invalid url, using fallback");
@@ -0,0 +1,207 @@
1
+ diff --git a/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java b/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java
2
+ index b335b4b1..c77efda7 100644
3
+ --- a/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java
4
+ +++ b/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java
5
+ @@ -59,6 +59,8 @@ public class SystemBars extends Plugin {
6
+ private String currentStatusBarStyle = STYLE_DEFAULT;
7
+ private String currentGestureBarStyle = STYLE_DEFAULT;
8
+
9
+ + private boolean navBarVisible = true;
10
+ +
11
+ @Override
12
+ public void load() {
13
+ getBridge().getWebView().addJavascriptInterface(this, "CapacitorSystemBarsAndroidInterface");
14
+ @@ -76,7 +78,15 @@ public class SystemBars extends Plugin {
15
+ @Override
16
+ public void onPageCommitVisible(WebView view, String url) {
17
+ super.onPageCommitVisible(view, url);
18
+ - getBridge().getWebView().requestApplyInsets();
19
+ + View parentView = (View) getBridge().getWebView().getParent();
20
+ + ViewCompat.requestApplyInsets(parentView);
21
+ + if (insetHandlingEnabled) {
22
+ + WindowInsetsCompat rootInsets = ViewCompat.getRootWindowInsets(parentView);
23
+ + if (rootInsets != null) {
24
+ + Insets safeArea = calcSafeAreaInsets(rootInsets);
25
+ + injectSafeAreaCSS(safeArea.top, safeArea.right, safeArea.bottom, safeArea.left);
26
+ + }
27
+ + }
28
+ }
29
+ }
30
+ );
31
+ @@ -103,8 +113,17 @@ public class SystemBars extends Plugin {
32
+ initSafeAreaCSSVariables();
33
+
34
+ getBridge().executeOnMainThread(() -> {
35
+ + Window window = getActivity().getWindow();
36
+ + WindowCompat.setDecorFitsSystemWindows(window, false);
37
+ + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
38
+ + window.setNavigationBarColor(android.graphics.Color.TRANSPARENT);
39
+ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
40
+ + window.setNavigationBarContrastEnforced(false);
41
+ + }
42
+ + }
43
+ setStyle(style, "");
44
+ setHidden(hidden, "");
45
+ + ViewCompat.requestApplyInsets((View) getBridge().getWebView().getParent());
46
+ });
47
+ }
48
+
49
+ @@ -144,27 +163,64 @@ public class SystemBars extends Plugin {
50
+ call.resolve();
51
+ }
52
+
53
+ + @Override
54
+ + protected void handleOnResume() {
55
+ + super.handleOnResume();
56
+ + getBridge().executeOnMainThread(() -> {
57
+ + Window window = getActivity().getWindow();
58
+ + WindowCompat.setDecorFitsSystemWindows(window, false);
59
+ + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
60
+ + window.setNavigationBarColor(android.graphics.Color.TRANSPARENT);
61
+ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
62
+ + window.setNavigationBarContrastEnforced(false);
63
+ + }
64
+ + }
65
+ + setStyle(currentGestureBarStyle, BAR_GESTURE_BAR);
66
+ + setStyle(currentStatusBarStyle, BAR_STATUS_BAR);
67
+ + ViewCompat.requestApplyInsets((View) getBridge().getWebView().getParent());
68
+ + });
69
+ + }
70
+ +
71
+ @JavascriptInterface
72
+ public void onDOMReady() {
73
+ getActivity().runOnUiThread(() -> {
74
+ this.bridge.getWebView().evaluateJavascript(viewportMetaJSFunction, (res) -> {
75
+ hasViewportCover = res.equals("true");
76
+ -
77
+ - getBridge().getWebView().requestApplyInsets();
78
+ + ViewCompat.requestApplyInsets((View) getBridge().getWebView().getParent());
79
+ });
80
+ });
81
+ }
82
+
83
+ private Insets calcSafeAreaInsets(WindowInsetsCompat insets) {
84
+ Insets safeArea = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
85
+ - if (insets.isVisible(WindowInsetsCompat.Type.ime())) {
86
+ +
87
+ + int bottom = safeArea.bottom;
88
+ +
89
+ + if (bottom == 0
90
+ + && Build.VERSION.SDK_INT < Build.VERSION_CODES.R
91
+ + && safeArea.left == 0 && safeArea.right == 0) { // skip if nav bar is on a side (landscape)
92
+ + bottom = getNavBarHeightFromResources();
93
+ + }
94
+ +
95
+ + boolean imeVisible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
96
+ + ? insets.isVisible(WindowInsetsCompat.Type.ime())
97
+ + : insets.getInsets(WindowInsetsCompat.Type.ime()).bottom > 0;
98
+ +
99
+ + if (imeVisible) {
100
+ return Insets.of(safeArea.left, safeArea.top, safeArea.right, 0);
101
+ }
102
+ - return Insets.of(safeArea.left, safeArea.top, safeArea.right, safeArea.bottom);
103
+ + return Insets.of(safeArea.left, safeArea.top, safeArea.right, bottom);
104
+ + }
105
+ +
106
+ + private int getNavBarHeightFromResources() {
107
+ + if (!navBarVisible) return 0;
108
+ + android.content.res.Resources res = getActivity().getResources();
109
+ + int heightId = res.getIdentifier("navigation_bar_height", "dimen", "android");
110
+ + return heightId > 0 ? res.getDimensionPixelSize(heightId) : 0;
111
+ }
112
+
113
+ private void initSafeAreaCSSVariables() {
114
+ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && insetHandlingEnabled) {
115
+ + if (insetHandlingEnabled) {
116
+ View v = (View) this.getBridge().getWebView().getParent();
117
+ WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(v);
118
+ if (insets != null) {
119
+ @@ -176,6 +232,10 @@ public class SystemBars extends Plugin {
120
+
121
+ private void initWindowInsetsListener() {
122
+ ViewCompat.setOnApplyWindowInsetsListener((View) getBridge().getWebView().getParent(), (v, insets) -> {
123
+ + // getRootWindowInsets() bypasses AppCompat's intermediate view consuming the bottom inset on API < 30
124
+ + WindowInsetsCompat rawInsets = ViewCompat.getRootWindowInsets(v);
125
+ + WindowInsetsCompat safeAreaSource = (rawInsets != null) ? rawInsets : insets;
126
+ +
127
+ boolean shouldPassthroughInsets = getWebViewMajorVersion() >= WEBVIEW_VERSION_WITH_SAFE_AREA_FIX && hasViewportCover;
128
+
129
+ Insets systemBarsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
130
+ @@ -186,8 +246,8 @@ public class SystemBars extends Plugin {
131
+ // We need to correct for a possible shown IME
132
+ v.setPadding(0, 0, 0, keyboardVisible ? imeInsets.bottom : 0);
133
+
134
+ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && hasViewportCover && insetHandlingEnabled) {
135
+ - Insets safeAreaInsets = calcSafeAreaInsets(insets);
136
+ + if (hasViewportCover && insetHandlingEnabled) {
137
+ + Insets safeAreaInsets = calcSafeAreaInsets(safeAreaSource);
138
+ injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
139
+ }
140
+
141
+ @@ -204,15 +264,7 @@ public class SystemBars extends Plugin {
142
+ .build();
143
+ }
144
+
145
+ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
146
+ - // We need to correct for a possible shown IME
147
+ - v.setPadding(
148
+ - systemBarsInsets.left,
149
+ - systemBarsInsets.top,
150
+ - systemBarsInsets.right,
151
+ - keyboardVisible ? imeInsets.bottom : systemBarsInsets.bottom
152
+ - );
153
+ - }
154
+ + v.setPadding(0, 0, 0, keyboardVisible ? imeInsets.bottom : 0);
155
+
156
+ // Returning `WindowInsetsCompat.CONSUMED` breaks recalculation of safe area insets
157
+ // So we have to explicitly set insets to `0`
158
+ @@ -221,8 +273,8 @@ public class SystemBars extends Plugin {
159
+ .setInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(), Insets.of(0, 0, 0, 0))
160
+ .build();
161
+
162
+ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && hasViewportCover && insetHandlingEnabled) {
163
+ - Insets safeAreaInsets = calcSafeAreaInsets(newInsets);
164
+ + if (insetHandlingEnabled) {
165
+ + Insets safeAreaInsets = calcSafeAreaInsets(safeAreaSource);
166
+ injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
167
+ }
168
+
169
+ @@ -263,6 +315,7 @@ public class SystemBars extends Plugin {
170
+ }
171
+
172
+ private void setStyle(String style, String bar) {
173
+ + String requestedStyle = style;
174
+ if (style.equals(STYLE_DEFAULT)) {
175
+ style = getStyleForTheme();
176
+ }
177
+ @@ -270,12 +323,12 @@ public class SystemBars extends Plugin {
178
+ Window window = getActivity().getWindow();
179
+ WindowInsetsControllerCompat windowInsetsControllerCompat = WindowCompat.getInsetsController(window, window.getDecorView());
180
+ if (bar.isEmpty() || bar.equals(BAR_STATUS_BAR)) {
181
+ - currentStatusBarStyle = style;
182
+ + currentStatusBarStyle = requestedStyle;
183
+ windowInsetsControllerCompat.setAppearanceLightStatusBars(!style.equals(STYLE_DARK));
184
+ }
185
+
186
+ if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {
187
+ - currentGestureBarStyle = style;
188
+ + currentGestureBarStyle = requestedStyle;
189
+ windowInsetsControllerCompat.setAppearanceLightNavigationBars(!style.equals(STYLE_DARK));
190
+ }
191
+
192
+ @@ -292,6 +345,7 @@ public class SystemBars extends Plugin {
193
+ }
194
+ if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {
195
+ windowInsetsControllerCompat.hide(WindowInsetsCompat.Type.navigationBars());
196
+ + navBarVisible = false;
197
+ }
198
+ return;
199
+ }
200
+ @@ -301,6 +355,7 @@ public class SystemBars extends Plugin {
201
+ }
202
+ if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {
203
+ windowInsetsControllerCompat.show(WindowInsetsCompat.Type.navigationBars());
204
+ + navBarVisible = true;
205
+ }
206
+ }
207
+
@@ -0,0 +1,14 @@
1
+ diff --git a/dist/ios/build.js b/dist/ios/build.js
2
+ index 249518e..c293d47 100644
3
+ --- a/dist/ios/build.js
4
+ +++ b/dist/ios/build.js
5
+ @@ -44,6 +44,9 @@ async function buildiOS(config, buildOptions) {
6
+ if (buildOptions.xcodeSigningType == 'manual') {
7
+ buildArgs.push(`PROVISIONING_PROFILE_SPECIFIER=${buildOptions.xcodeProvisioningProfile}`);
8
+ }
9
+ + else {
10
+ + buildArgs.push('-allowProvisioningUpdates');
11
+ + }
12
+ await (0, common_1.runTask)('Building xArchive', async () => (0, subprocess_1.runCommand)('xcodebuild', buildArgs, {
13
+ cwd: config.ios.nativeProjectDirAbs,
14
+ }));
@@ -28,6 +28,18 @@ export async function runCapacitorPatch(options) {
28
28
  });
29
29
  }
30
30
 
31
+ for (const entry of selected.superseded) {
32
+ results.push({
33
+ id: entry.patch.id,
34
+ title: entry.patch.title,
35
+ phase,
36
+ selectedBy: entry.selectedBy,
37
+ status: 'skipped',
38
+ changedFiles: [],
39
+ reason: `Superseded by ${entry.supersededBy}.`,
40
+ });
41
+ }
42
+
31
43
  for (const entry of selected.patches) {
32
44
  results.push(
33
45
  await applyCatalogPatch({
@@ -50,7 +62,7 @@ export async function runCapacitorPatch(options) {
50
62
 
51
63
  return {
52
64
  phase,
53
- selectedCount: selected.patches.length + selected.unknownIds.length,
65
+ selectedCount: selected.patches.length + selected.unknownIds.length + selected.superseded.length,
54
66
  results,
55
67
  };
56
68
  }
@@ -59,7 +71,7 @@ export function selectPatches(catalog, patchConfig, phase) {
59
71
  const disabled = new Set(patchConfig.disabled);
60
72
  const explicit = new Set(patchConfig.patches);
61
73
  const catalogById = new Map(catalog.map((patch) => [patch.id, patch]));
62
- const patches = [];
74
+ const selectedEntries = [];
63
75
 
64
76
  for (const patch of catalog) {
65
77
  if (!patch?.id || patch.phase !== phase || disabled.has(patch.id)) {
@@ -67,14 +79,36 @@ export function selectPatches(catalog, patchConfig, phase) {
67
79
  }
68
80
 
69
81
  if (explicit.has(patch.id)) {
70
- patches.push({ patch, selectedBy: 'explicit' });
82
+ selectedEntries.push({ patch, selectedBy: 'explicit' });
71
83
  } else if (patchConfig.recommended && patch.recommended === true) {
72
- patches.push({ patch, selectedBy: 'recommended' });
84
+ selectedEntries.push({ patch, selectedBy: 'recommended' });
85
+ }
86
+ }
87
+
88
+ const selectedIds = new Set(selectedEntries.map((entry) => entry.patch.id));
89
+ const supersededBy = new Map();
90
+ for (const entry of selectedEntries) {
91
+ for (const supersededId of entry.patch.supersedes ?? []) {
92
+ if (selectedIds.has(supersededId) && !disabled.has(supersededId)) {
93
+ supersededBy.set(supersededId, entry.patch.id);
94
+ }
95
+ }
96
+ }
97
+
98
+ const patches = [];
99
+ const superseded = [];
100
+ for (const entry of selectedEntries) {
101
+ const replacementId = supersededBy.get(entry.patch.id);
102
+ if (replacementId) {
103
+ superseded.push({ ...entry, supersededBy: replacementId });
104
+ } else {
105
+ patches.push(entry);
73
106
  }
74
107
  }
75
108
 
76
109
  return {
77
110
  patches,
111
+ superseded,
78
112
  unknownIds: patchConfig.patches.filter((id) => !disabled.has(id) && !catalogById.has(id)),
79
113
  };
80
114
  }