@capacitor-plus/android 8.3.4 → 8.3.5
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.
- package/capacitor/src/main/assets/native-bridge.js +3 -3
- package/capacitor/src/main/java/com/getcapacitor/Bridge.java +34 -32
- package/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java +92 -0
- package/capacitor/src/main/java/com/getcapacitor/CapacitorWebView.java +18 -2
- package/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java +15 -14
- package/package.json +1 -1
|
@@ -48,14 +48,14 @@ var nativeBridge = (function (exports) {
|
|
|
48
48
|
const newFormData = [];
|
|
49
49
|
for (const pair of formData.entries()) {
|
|
50
50
|
const [key, value] = pair;
|
|
51
|
-
if (value instanceof
|
|
51
|
+
if (value instanceof Blob) {
|
|
52
52
|
const base64File = await readFileAsBase64(value);
|
|
53
53
|
newFormData.push({
|
|
54
54
|
key,
|
|
55
55
|
value: base64File,
|
|
56
56
|
type: 'base64File',
|
|
57
57
|
contentType: value.type,
|
|
58
|
-
fileName: value.name,
|
|
58
|
+
fileName: value.name || 'blob',
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
61
|
else {
|
|
@@ -128,7 +128,7 @@ var nativeBridge = (function (exports) {
|
|
|
128
128
|
type: 'formData',
|
|
129
129
|
};
|
|
130
130
|
}
|
|
131
|
-
else if (body instanceof
|
|
131
|
+
else if (body instanceof Blob) {
|
|
132
132
|
const fileData = await readFileAsBase64(body);
|
|
133
133
|
return {
|
|
134
134
|
data: fileData,
|
|
@@ -1214,42 +1214,44 @@ public class Bridge {
|
|
|
1214
1214
|
protected Map<String, PermissionState> getPermissionStates(Plugin plugin) {
|
|
1215
1215
|
Map<String, PermissionState> permissionsResults = new HashMap<>();
|
|
1216
1216
|
CapacitorPlugin annotation = plugin.getPluginHandle().getPluginAnnotation();
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
}
|
|
1230
|
-
} else {
|
|
1231
|
-
for (String permString : perm.strings()) {
|
|
1232
|
-
String key = perm.alias().isEmpty() ? permString : perm.alias();
|
|
1233
|
-
PermissionState permissionStatus;
|
|
1234
|
-
if (ActivityCompat.checkSelfPermission(this.getContext(), permString) == PackageManager.PERMISSION_GRANTED) {
|
|
1235
|
-
permissionStatus = PermissionState.GRANTED;
|
|
1236
|
-
} else {
|
|
1237
|
-
permissionStatus = PermissionState.PROMPT;
|
|
1238
|
-
|
|
1239
|
-
// Check if there is a cached permission state for the "Never ask again" state
|
|
1240
|
-
SharedPreferences prefs = getContext().getSharedPreferences(PERMISSION_PREFS_NAME, Activity.MODE_PRIVATE);
|
|
1241
|
-
String state = prefs.getString(permString, null);
|
|
1242
|
-
|
|
1243
|
-
if (state != null) {
|
|
1244
|
-
permissionStatus = PermissionState.byState(state);
|
|
1217
|
+
if (annotation != null) {
|
|
1218
|
+
for (Permission perm : annotation.permissions()) {
|
|
1219
|
+
// If a permission is defined with no permission constants, return GRANTED for it.
|
|
1220
|
+
// Otherwise, get its true state.
|
|
1221
|
+
if (perm.strings().length == 0 || (perm.strings().length == 1 && perm.strings()[0].isEmpty())) {
|
|
1222
|
+
String key = perm.alias();
|
|
1223
|
+
if (!key.isEmpty()) {
|
|
1224
|
+
PermissionState existingResult = permissionsResults.get(key);
|
|
1225
|
+
|
|
1226
|
+
// auto set permission state to GRANTED if the alias is empty.
|
|
1227
|
+
if (existingResult == null) {
|
|
1228
|
+
permissionsResults.put(key, PermissionState.GRANTED);
|
|
1245
1229
|
}
|
|
1246
1230
|
}
|
|
1231
|
+
} else {
|
|
1232
|
+
for (String permString : perm.strings()) {
|
|
1233
|
+
String key = perm.alias().isEmpty() ? permString : perm.alias();
|
|
1234
|
+
PermissionState permissionStatus;
|
|
1235
|
+
if (ActivityCompat.checkSelfPermission(this.getContext(), permString) == PackageManager.PERMISSION_GRANTED) {
|
|
1236
|
+
permissionStatus = PermissionState.GRANTED;
|
|
1237
|
+
} else {
|
|
1238
|
+
permissionStatus = PermissionState.PROMPT;
|
|
1239
|
+
|
|
1240
|
+
// Check if there is a cached permission state for the "Never ask again" state
|
|
1241
|
+
SharedPreferences prefs = getContext().getSharedPreferences(PERMISSION_PREFS_NAME, Activity.MODE_PRIVATE);
|
|
1242
|
+
String state = prefs.getString(permString, null);
|
|
1243
|
+
|
|
1244
|
+
if (state != null) {
|
|
1245
|
+
permissionStatus = PermissionState.byState(state);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1247
1248
|
|
|
1248
|
-
|
|
1249
|
+
PermissionState existingResult = permissionsResults.get(key);
|
|
1249
1250
|
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1251
|
+
// multiple permissions with the same alias must all be true, otherwise all false.
|
|
1252
|
+
if (existingResult == null || existingResult == PermissionState.GRANTED) {
|
|
1253
|
+
permissionsResults.put(key, permissionStatus);
|
|
1254
|
+
}
|
|
1253
1255
|
}
|
|
1254
1256
|
}
|
|
1255
1257
|
}
|
|
@@ -46,6 +46,17 @@ public class BridgeWebChromeClient extends WebChromeClient {
|
|
|
46
46
|
void onActivityResult(ActivityResult result);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
// Static variables to store pending file chooser state to survive activity recreation
|
|
50
|
+
private static ValueCallback<Uri[]> pendingFilePathCallback;
|
|
51
|
+
private static Uri pendingImageFileUri;
|
|
52
|
+
private static FileChooserType pendingFileChooserType;
|
|
53
|
+
|
|
54
|
+
private enum FileChooserType {
|
|
55
|
+
IMAGE_CAPTURE,
|
|
56
|
+
VIDEO_CAPTURE,
|
|
57
|
+
FILE_PICKER
|
|
58
|
+
}
|
|
59
|
+
|
|
49
60
|
private ActivityResultLauncher permissionLauncher;
|
|
50
61
|
private ActivityResultLauncher activityLauncher;
|
|
51
62
|
private PermissionListener permissionListener;
|
|
@@ -70,10 +81,70 @@ public class BridgeWebChromeClient extends WebChromeClient {
|
|
|
70
81
|
activityLauncher = bridge.registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), (result) -> {
|
|
71
82
|
if (activityListener != null) {
|
|
72
83
|
activityListener.onActivityResult(result);
|
|
84
|
+
} else if (pendingFilePathCallback != null) {
|
|
85
|
+
// Handle case where activity was recreated and instance callback is null
|
|
86
|
+
// Use the static callback instead
|
|
87
|
+
handlePendingFileChooserResult(result);
|
|
73
88
|
}
|
|
74
89
|
});
|
|
75
90
|
}
|
|
76
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Handle file chooser result when the activity was recreated and instance callbacks were lost.
|
|
94
|
+
* This uses the static pending callback to deliver the result.
|
|
95
|
+
*/
|
|
96
|
+
private void handlePendingFileChooserResult(ActivityResult result) {
|
|
97
|
+
if (pendingFilePathCallback == null) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
Uri[] uriResult = null;
|
|
103
|
+
if (result.getResultCode() == Activity.RESULT_OK) {
|
|
104
|
+
switch (pendingFileChooserType) {
|
|
105
|
+
case IMAGE_CAPTURE:
|
|
106
|
+
if (pendingImageFileUri != null) {
|
|
107
|
+
uriResult = new Uri[] { pendingImageFileUri };
|
|
108
|
+
}
|
|
109
|
+
break;
|
|
110
|
+
case VIDEO_CAPTURE:
|
|
111
|
+
Intent videoData = result.getData();
|
|
112
|
+
if (videoData != null && videoData.getData() != null) {
|
|
113
|
+
uriResult = new Uri[] { videoData.getData() };
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
116
|
+
case FILE_PICKER:
|
|
117
|
+
Intent fileData = result.getData();
|
|
118
|
+
if (fileData != null) {
|
|
119
|
+
if (fileData.getClipData() != null) {
|
|
120
|
+
final int numFiles = fileData.getClipData().getItemCount();
|
|
121
|
+
uriResult = new Uri[numFiles];
|
|
122
|
+
for (int i = 0; i < numFiles; i++) {
|
|
123
|
+
uriResult[i] = fileData.getClipData().getItemAt(i).getUri();
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
uriResult = WebChromeClient.FileChooserParams.parseResult(result.getResultCode(), fileData);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
pendingFilePathCallback.onReceiveValue(uriResult);
|
|
133
|
+
} finally {
|
|
134
|
+
// Clear the static state after handling
|
|
135
|
+
clearPendingFileChooserState();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Clear the static pending file chooser state.
|
|
141
|
+
*/
|
|
142
|
+
private static void clearPendingFileChooserState() {
|
|
143
|
+
pendingFilePathCallback = null;
|
|
144
|
+
pendingImageFileUri = null;
|
|
145
|
+
pendingFileChooserType = null;
|
|
146
|
+
}
|
|
147
|
+
|
|
77
148
|
/**
|
|
78
149
|
* Render web content in `view`.
|
|
79
150
|
*
|
|
@@ -340,12 +411,19 @@ public class BridgeWebChromeClient extends WebChromeClient {
|
|
|
340
411
|
return false;
|
|
341
412
|
}
|
|
342
413
|
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageFileUri);
|
|
414
|
+
|
|
415
|
+
// Store in static variables to survive activity recreation
|
|
416
|
+
pendingFilePathCallback = filePathCallback;
|
|
417
|
+
pendingImageFileUri = imageFileUri;
|
|
418
|
+
pendingFileChooserType = FileChooserType.IMAGE_CAPTURE;
|
|
419
|
+
|
|
343
420
|
activityListener = (activityResult) -> {
|
|
344
421
|
Uri[] result = null;
|
|
345
422
|
if (activityResult.getResultCode() == Activity.RESULT_OK) {
|
|
346
423
|
result = new Uri[] { imageFileUri };
|
|
347
424
|
}
|
|
348
425
|
filePathCallback.onReceiveValue(result);
|
|
426
|
+
clearPendingFileChooserState();
|
|
349
427
|
};
|
|
350
428
|
activityLauncher.launch(takePictureIntent);
|
|
351
429
|
|
|
@@ -359,12 +437,18 @@ public class BridgeWebChromeClient extends WebChromeClient {
|
|
|
359
437
|
return false;
|
|
360
438
|
}
|
|
361
439
|
|
|
440
|
+
// Store in static variables to survive activity recreation
|
|
441
|
+
pendingFilePathCallback = filePathCallback;
|
|
442
|
+
pendingImageFileUri = null;
|
|
443
|
+
pendingFileChooserType = FileChooserType.VIDEO_CAPTURE;
|
|
444
|
+
|
|
362
445
|
activityListener = (activityResult) -> {
|
|
363
446
|
Uri[] result = null;
|
|
364
447
|
if (activityResult.getResultCode() == Activity.RESULT_OK) {
|
|
365
448
|
result = new Uri[] { activityResult.getData().getData() };
|
|
366
449
|
}
|
|
367
450
|
filePathCallback.onReceiveValue(result);
|
|
451
|
+
clearPendingFileChooserState();
|
|
368
452
|
};
|
|
369
453
|
activityLauncher.launch(takeVideoIntent);
|
|
370
454
|
|
|
@@ -383,6 +467,12 @@ public class BridgeWebChromeClient extends WebChromeClient {
|
|
|
383
467
|
intent.setType(validTypes[0]);
|
|
384
468
|
}
|
|
385
469
|
}
|
|
470
|
+
|
|
471
|
+
// Store in static variables to survive activity recreation
|
|
472
|
+
pendingFilePathCallback = filePathCallback;
|
|
473
|
+
pendingImageFileUri = null;
|
|
474
|
+
pendingFileChooserType = FileChooserType.FILE_PICKER;
|
|
475
|
+
|
|
386
476
|
try {
|
|
387
477
|
activityListener = (activityResult) -> {
|
|
388
478
|
Uri[] result;
|
|
@@ -397,10 +487,12 @@ public class BridgeWebChromeClient extends WebChromeClient {
|
|
|
397
487
|
result = WebChromeClient.FileChooserParams.parseResult(activityResult.getResultCode(), resultIntent);
|
|
398
488
|
}
|
|
399
489
|
filePathCallback.onReceiveValue(result);
|
|
490
|
+
clearPendingFileChooserState();
|
|
400
491
|
};
|
|
401
492
|
activityLauncher.launch(intent);
|
|
402
493
|
} catch (ActivityNotFoundException e) {
|
|
403
494
|
filePathCallback.onReceiveValue(null);
|
|
495
|
+
clearPendingFileChooserState();
|
|
404
496
|
}
|
|
405
497
|
}
|
|
406
498
|
|
|
@@ -2,6 +2,7 @@ package com.getcapacitor;
|
|
|
2
2
|
|
|
3
3
|
import android.content.Context;
|
|
4
4
|
import android.os.Build;
|
|
5
|
+
import android.text.InputType;
|
|
5
6
|
import android.util.AttributeSet;
|
|
6
7
|
import android.util.TypedValue;
|
|
7
8
|
import android.view.KeyEvent;
|
|
@@ -17,6 +18,7 @@ public class CapacitorWebView extends WebView {
|
|
|
17
18
|
|
|
18
19
|
private BaseInputConnection capInputConnection;
|
|
19
20
|
private Bridge bridge;
|
|
21
|
+
private boolean autosuggestionEnabled = true;
|
|
20
22
|
|
|
21
23
|
public CapacitorWebView(Context context, AttributeSet attrs) {
|
|
22
24
|
super(context, attrs);
|
|
@@ -26,6 +28,10 @@ public class CapacitorWebView extends WebView {
|
|
|
26
28
|
this.bridge = bridge;
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
public void setAutosuggestionEnabled(boolean enabled) {
|
|
32
|
+
autosuggestionEnabled = enabled;
|
|
33
|
+
}
|
|
34
|
+
|
|
29
35
|
@Override
|
|
30
36
|
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
|
31
37
|
CapConfig config;
|
|
@@ -36,13 +42,23 @@ public class CapacitorWebView extends WebView {
|
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
boolean captureInput = config.isInputCaptured();
|
|
45
|
+
InputConnection inputConnection;
|
|
39
46
|
if (captureInput) {
|
|
40
47
|
if (capInputConnection == null) {
|
|
41
48
|
capInputConnection = new BaseInputConnection(this, false);
|
|
42
49
|
}
|
|
43
|
-
|
|
50
|
+
inputConnection = capInputConnection;
|
|
51
|
+
} else {
|
|
52
|
+
inputConnection = super.onCreateInputConnection(outAttrs);
|
|
44
53
|
}
|
|
45
|
-
|
|
54
|
+
|
|
55
|
+
if (!autosuggestionEnabled) {
|
|
56
|
+
// See https://stackoverflow.com/a/28009054/640584
|
|
57
|
+
outAttrs.inputType &= ~EditorInfo.TYPE_MASK_VARIATION;
|
|
58
|
+
outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return inputConnection;
|
|
46
62
|
}
|
|
47
63
|
|
|
48
64
|
@Override
|
|
@@ -164,13 +164,18 @@ public class SystemBars extends Plugin {
|
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
private void initSafeAreaCSSVariables() {
|
|
167
|
-
|
|
167
|
+
WindowInsetsCompat insets;
|
|
168
|
+
|
|
169
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
|
168
170
|
View v = (View) this.getBridge().getWebView().getParent();
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
171
|
+
insets = ViewCompat.getRootWindowInsets(v);
|
|
172
|
+
} else {
|
|
173
|
+
insets = WindowInsetsCompat.CONSUMED;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (insets != null) {
|
|
177
|
+
Insets safeAreaInsets = calcSafeAreaInsets(insets);
|
|
178
|
+
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
|
|
174
179
|
}
|
|
175
180
|
}
|
|
176
181
|
|
|
@@ -186,10 +191,8 @@ public class SystemBars extends Plugin {
|
|
|
186
191
|
// We need to correct for a possible shown IME
|
|
187
192
|
v.setPadding(0, 0, 0, keyboardVisible ? imeInsets.bottom : 0);
|
|
188
193
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
|
|
192
|
-
}
|
|
194
|
+
Insets safeAreaInsets = calcSafeAreaInsets(insets);
|
|
195
|
+
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
|
|
193
196
|
|
|
194
197
|
return new WindowInsetsCompat.Builder(insets)
|
|
195
198
|
.setInsets(
|
|
@@ -221,10 +224,8 @@ public class SystemBars extends Plugin {
|
|
|
221
224
|
.setInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(), Insets.of(0, 0, 0, 0))
|
|
222
225
|
.build();
|
|
223
226
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
|
|
227
|
-
}
|
|
227
|
+
Insets safeAreaInsets = calcSafeAreaInsets(newInsets);
|
|
228
|
+
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
|
|
228
229
|
|
|
229
230
|
return newInsets;
|
|
230
231
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capacitor-plus/android",
|
|
3
|
-
"version": "8.3.
|
|
3
|
+
"version": "8.3.5",
|
|
4
4
|
"description": "Capacitor+: Enhanced Capacitor with automated upstream sync - Cross-platform apps with JavaScript and the web",
|
|
5
5
|
"homepage": "https://capgo.app/docs/plugins/capacitor-plus/",
|
|
6
6
|
"author": "Capgo Team <support@capgo.app> (https://capgo.app)",
|