@capgo/capacitor-inappbrowser 8.15.3 → 8.16.0
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/README.md +3 -1
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/CapgoInAppBrowserPlugin.java +15 -65
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/InAppBrowserPlugin.java +1 -2
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/OpenTimeBrowsingDataClearSupport.java +40 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/Options.java +20 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/SafeAreaInsetsSupport.java +7 -2
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/SystemUiChromeSupport.java +109 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java +203 -199
- package/dist/docs.json +33 -1
- package/dist/esm/definitions.d.ts +26 -3
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Sources/InAppBrowserPlugin/InAppBrowserPlugin.swift +36 -1
- package/ios/Sources/InAppBrowserPlugin/WKWebViewController.swift +13 -1
- package/package.json +1 -1
|
@@ -69,7 +69,9 @@ import android.widget.RelativeLayout;
|
|
|
69
69
|
import android.widget.TextView;
|
|
70
70
|
import android.widget.Toast;
|
|
71
71
|
import android.widget.Toolbar;
|
|
72
|
+
import androidx.activity.ComponentActivity;
|
|
72
73
|
import androidx.activity.result.ActivityResult;
|
|
74
|
+
import androidx.activity.result.ActivityResultLauncher;
|
|
73
75
|
import androidx.activity.result.contract.ActivityResultContracts;
|
|
74
76
|
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
|
75
77
|
import androidx.core.content.FileProvider;
|
|
@@ -409,10 +411,11 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
409
411
|
private boolean preShowInjectedAtDocumentStart = false;
|
|
410
412
|
|
|
411
413
|
public PermissionRequest currentPermissionRequest;
|
|
412
|
-
public static final int FILE_CHOOSER_REQUEST_CODE = 1000;
|
|
413
414
|
public ValueCallback<Uri> mUploadMessage;
|
|
414
415
|
public ValueCallback<Uri[]> mFilePathCallback;
|
|
415
416
|
FileChooserRequestSupport.FileChooserRequest activeFileChooserRequest;
|
|
417
|
+
private ActivityResultLauncher<Intent> cameraCaptureLauncher;
|
|
418
|
+
private ActivityResultLauncher<Intent> fileChooserLauncher;
|
|
416
419
|
private boolean openWebViewResolved;
|
|
417
420
|
private boolean isDismissing = false;
|
|
418
421
|
private PermissionRequest pendingCameraLaunchPermissionRequest;
|
|
@@ -447,6 +450,11 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
447
450
|
this.instanceId = id != null ? id : "";
|
|
448
451
|
}
|
|
449
452
|
|
|
453
|
+
void bindToHostActivity() {
|
|
454
|
+
ensureFileChooserLaunchers();
|
|
455
|
+
registerDialogBackHandler();
|
|
456
|
+
}
|
|
457
|
+
|
|
450
458
|
public String getInstanceId() {
|
|
451
459
|
return instanceId;
|
|
452
460
|
}
|
|
@@ -2116,10 +2124,6 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
2116
2124
|
public void presentWebView() {
|
|
2117
2125
|
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
|
2118
2126
|
setCancelable(true);
|
|
2119
|
-
Objects.requireNonNull(getWindow()).setFlags(
|
|
2120
|
-
WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
|
2121
|
-
WindowManager.LayoutParams.FLAG_FULLSCREEN
|
|
2122
|
-
);
|
|
2123
2127
|
setContentView(R.layout.activity_browser);
|
|
2124
2128
|
|
|
2125
2129
|
// If custom dimensions are set, configure for touch passthrough
|
|
@@ -2143,18 +2147,10 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
2143
2147
|
if (appBar != null) appBar.setFitsSystemWindows(true);
|
|
2144
2148
|
}
|
|
2145
2149
|
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
// Make status bar transparent
|
|
2149
|
-
if (getWindow() != null) {
|
|
2150
|
-
getWindow().setStatusBarColor(Color.TRANSPARENT);
|
|
2151
|
-
|
|
2152
|
-
// Add FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag to the window
|
|
2153
|
-
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
|
2150
|
+
// Set dimensions if specified, otherwise fullscreen
|
|
2151
|
+
applyDimensions();
|
|
2154
2152
|
|
|
2155
|
-
|
|
2156
|
-
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
|
2157
|
-
}
|
|
2153
|
+
SystemUiChromeSupport.prepareInitialDialogWindow(getWindow());
|
|
2158
2154
|
|
|
2159
2155
|
WindowInsetsControllerCompat insetsController = new WindowInsetsControllerCompat(
|
|
2160
2156
|
getWindow(),
|
|
@@ -2206,9 +2202,6 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
2206
2202
|
});
|
|
2207
2203
|
}
|
|
2208
2204
|
|
|
2209
|
-
// Set dimensions if specified, otherwise fullscreen
|
|
2210
|
-
applyDimensions();
|
|
2211
|
-
|
|
2212
2205
|
this._webView = findViewById(R.id.browser_view);
|
|
2213
2206
|
|
|
2214
2207
|
this.swipeRefreshLayout = findViewById(R.id.content_browser_layout);
|
|
@@ -2250,13 +2243,9 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
2250
2243
|
if (!_options.getPersistWebViewData()) {
|
|
2251
2244
|
_webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
|
|
2252
2245
|
_webView.getSettings().setDatabaseEnabled(false);
|
|
2253
|
-
_webView.getSettings().setDomStorageEnabled(false);
|
|
2254
2246
|
_webView.clearCache(true);
|
|
2255
2247
|
_webView.clearHistory();
|
|
2256
2248
|
_webView.clearFormData();
|
|
2257
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
2258
|
-
CookieManager.getInstance().setAcceptThirdPartyCookies(_webView, false);
|
|
2259
|
-
}
|
|
2260
2249
|
}
|
|
2261
2250
|
_webView.getSettings().setMediaPlaybackRequiresUserGesture(false);
|
|
2262
2251
|
injectDocumentStartJavaScriptInterface();
|
|
@@ -2583,38 +2572,12 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
2583
2572
|
}
|
|
2584
2573
|
|
|
2585
2574
|
try {
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
componentActivity
|
|
2590
|
-
.getActivityResultRegistry()
|
|
2591
|
-
.register(
|
|
2592
|
-
"camera_capture",
|
|
2593
|
-
new androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult(),
|
|
2594
|
-
(result) -> {
|
|
2595
|
-
if (!FileChooserRequestSupport.isActive(request, activeFileChooserRequest)) {
|
|
2596
|
-
return;
|
|
2597
|
-
}
|
|
2598
|
-
Uri[] results = null;
|
|
2599
|
-
if (result.getResultCode() == Activity.RESULT_OK && request.tempCameraUri != null) {
|
|
2600
|
-
results = new Uri[] { request.tempCameraUri };
|
|
2601
|
-
}
|
|
2602
|
-
if (
|
|
2603
|
-
FileChooserRequestSupport.completeIfActive(
|
|
2604
|
-
request,
|
|
2605
|
-
activeFileChooserRequest,
|
|
2606
|
-
results
|
|
2607
|
-
)
|
|
2608
|
-
) {
|
|
2609
|
-
request.tempCameraUri = null;
|
|
2610
|
-
clearActiveFileChooserRequest(request);
|
|
2611
|
-
}
|
|
2612
|
-
}
|
|
2613
|
-
)
|
|
2614
|
-
.launch(takePictureIntent);
|
|
2575
|
+
ensureFileChooserLaunchers();
|
|
2576
|
+
if (cameraCaptureLauncher != null) {
|
|
2577
|
+
cameraCaptureLauncher.launch(takePictureIntent);
|
|
2615
2578
|
} else {
|
|
2616
|
-
|
|
2617
|
-
|
|
2579
|
+
Log.e("InAppBrowser", "ComponentActivity required for camera capture, falling back to file picker");
|
|
2580
|
+
fallbackToFilePicker(request);
|
|
2618
2581
|
}
|
|
2619
2582
|
} catch (SecurityException e) {
|
|
2620
2583
|
Log.e("InAppBrowser", "Security exception launching camera: " + e.getMessage(), e);
|
|
@@ -2817,6 +2780,15 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
2817
2780
|
Map<String, String> requestHeaders = buildRequestHeadersExcludingUserAgent();
|
|
2818
2781
|
applyWebViewUserAgent();
|
|
2819
2782
|
|
|
2783
|
+
if (_options.getClearCookiesOnOpen() || _options.getClearCacheOnOpen()) {
|
|
2784
|
+
OpenTimeBrowsingDataClearSupport.applyBeforeFirstNavigation(
|
|
2785
|
+
CookieManager.getInstance(),
|
|
2786
|
+
_webView,
|
|
2787
|
+
_options.getClearCookiesOnOpen(),
|
|
2788
|
+
_options.getClearCacheOnOpen()
|
|
2789
|
+
);
|
|
2790
|
+
}
|
|
2791
|
+
|
|
2820
2792
|
// Load URL with optional HTTP method and body
|
|
2821
2793
|
String httpMethod = _options.getHttpMethod();
|
|
2822
2794
|
String httpBody = _options.getHttpBody();
|
|
@@ -2887,6 +2859,52 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
2887
2859
|
registerConfigurationCallbacks();
|
|
2888
2860
|
}
|
|
2889
2861
|
|
|
2862
|
+
private void registerDialogBackHandler() {
|
|
2863
|
+
setOnKeyListener((dialogInterface, keyCode, event) -> {
|
|
2864
|
+
if (keyCode != KeyEvent.KEYCODE_BACK) {
|
|
2865
|
+
return false;
|
|
2866
|
+
}
|
|
2867
|
+
if (event.getAction() != KeyEvent.ACTION_UP) {
|
|
2868
|
+
return true;
|
|
2869
|
+
}
|
|
2870
|
+
if (!shouldConsumeBackPress()) {
|
|
2871
|
+
return false;
|
|
2872
|
+
}
|
|
2873
|
+
handleBrowserBackNavigation();
|
|
2874
|
+
return true;
|
|
2875
|
+
});
|
|
2876
|
+
}
|
|
2877
|
+
|
|
2878
|
+
private boolean shouldConsumeBackPress() {
|
|
2879
|
+
return isShowing() && !isHiddenModeActive && !backLayerActive;
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2882
|
+
private void handleBrowserBackNavigation() {
|
|
2883
|
+
if (_options == null) {
|
|
2884
|
+
dismiss();
|
|
2885
|
+
return;
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
if (
|
|
2889
|
+
_webView != null &&
|
|
2890
|
+
_webView.canGoBack() &&
|
|
2891
|
+
(TextUtils.equals(_options.getToolbarType(), "navigation") || _options.getActiveNativeNavigationForWebview())
|
|
2892
|
+
) {
|
|
2893
|
+
_webView.goBack();
|
|
2894
|
+
return;
|
|
2895
|
+
}
|
|
2896
|
+
|
|
2897
|
+
if (_options.getDisableGoBackOnNativeApplication()) {
|
|
2898
|
+
return;
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
String currentUrl = getUrl();
|
|
2902
|
+
if (_options.getCallbacks() != null) {
|
|
2903
|
+
_options.getCallbacks().closeEvent(currentUrl);
|
|
2904
|
+
}
|
|
2905
|
+
dismiss();
|
|
2906
|
+
}
|
|
2907
|
+
|
|
2890
2908
|
private void applyHiddenMode() {
|
|
2891
2909
|
Window window = getWindow();
|
|
2892
2910
|
if (window == null) {
|
|
@@ -3033,7 +3051,7 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
3033
3051
|
}
|
|
3034
3052
|
|
|
3035
3053
|
// Check if we need Android 15+ specific fixes
|
|
3036
|
-
boolean isAndroid15Plus = Build.VERSION.SDK_INT
|
|
3054
|
+
boolean isAndroid15Plus = SystemUiChromeSupport.requiresEdgeToEdgeChrome(Build.VERSION.SDK_INT);
|
|
3037
3055
|
|
|
3038
3056
|
View toolbarView = findViewById(R.id.tool_bar);
|
|
3039
3057
|
|
|
@@ -3062,69 +3080,54 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
3062
3080
|
|
|
3063
3081
|
// Handle window decoration - version-specific handling
|
|
3064
3082
|
if (getWindow() != null) {
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
getWindow().setNavigationBarColor(Color.TRANSPARENT);
|
|
3083
|
+
View decorView = getWindow().getDecorView();
|
|
3084
|
+
Integer statusBarColor = null;
|
|
3085
|
+
Integer navigationBarColor = Color.TRANSPARENT;
|
|
3086
|
+
Boolean lightStatusBars = null;
|
|
3070
3087
|
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
// Set status bar text color
|
|
3088
|
+
if (isAndroid15Plus) {
|
|
3074
3089
|
if (_options.getToolbarColor() != null && !_options.getToolbarColor().isEmpty()) {
|
|
3075
3090
|
try {
|
|
3076
3091
|
int backgroundColor = Color.parseColor(_options.getToolbarColor());
|
|
3077
|
-
|
|
3078
|
-
controller.setAppearanceLightStatusBars(!isDarkBackground);
|
|
3092
|
+
lightStatusBars = !isDarkColor(backgroundColor);
|
|
3079
3093
|
} catch (IllegalArgumentException e) {
|
|
3080
3094
|
// Ignore color parsing errors
|
|
3081
3095
|
}
|
|
3082
3096
|
}
|
|
3083
|
-
} else if (Build.VERSION.SDK_INT >=
|
|
3084
|
-
// Android 11-14: Keep navigation bar transparent but respect status bar
|
|
3085
|
-
getWindow().setNavigationBarColor(Color.TRANSPARENT);
|
|
3086
|
-
|
|
3087
|
-
WindowInsetsControllerCompat controller = new WindowInsetsControllerCompat(getWindow(), getWindow().getDecorView());
|
|
3088
|
-
|
|
3089
|
-
// Set status bar color to match toolbar or use system default
|
|
3097
|
+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
3090
3098
|
if (_options.getToolbarColor() != null && !_options.getToolbarColor().isEmpty()) {
|
|
3091
3099
|
try {
|
|
3092
3100
|
int toolbarColor = Color.parseColor(_options.getToolbarColor());
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
controller.setAppearanceLightStatusBars(!isDarkBackground);
|
|
3101
|
+
statusBarColor = toolbarColor;
|
|
3102
|
+
lightStatusBars = !isDarkColor(toolbarColor);
|
|
3096
3103
|
} catch (IllegalArgumentException e) {
|
|
3097
|
-
// Follow system theme if color parsing fails
|
|
3098
3104
|
boolean isDarkTheme = isDarkThemeEnabled();
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
controller.setAppearanceLightStatusBars(!isDarkTheme);
|
|
3105
|
+
statusBarColor = isDarkTheme ? Color.BLACK : Color.WHITE;
|
|
3106
|
+
lightStatusBars = !isDarkTheme;
|
|
3102
3107
|
}
|
|
3103
3108
|
} else {
|
|
3104
|
-
// Follow system theme if no toolbar color provided
|
|
3105
3109
|
boolean isDarkTheme = isDarkThemeEnabled();
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
controller.setAppearanceLightStatusBars(!isDarkTheme);
|
|
3110
|
+
statusBarColor = isDarkTheme ? Color.BLACK : Color.WHITE;
|
|
3111
|
+
lightStatusBars = !isDarkTheme;
|
|
3109
3112
|
}
|
|
3110
3113
|
} else {
|
|
3111
|
-
// Pre-Android 11: Use deprecated flags for edge-to-edge navigation bar only
|
|
3112
|
-
getWindow()
|
|
3113
|
-
.getDecorView()
|
|
3114
|
-
.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
|
|
3115
|
-
|
|
3116
|
-
getWindow().setNavigationBarColor(Color.TRANSPARENT);
|
|
3117
|
-
|
|
3118
|
-
// Set status bar color to match toolbar
|
|
3119
3114
|
if (_options.getToolbarColor() != null && !_options.getToolbarColor().isEmpty()) {
|
|
3120
3115
|
try {
|
|
3121
|
-
|
|
3122
|
-
getWindow().setStatusBarColor(toolbarColor);
|
|
3116
|
+
statusBarColor = Color.parseColor(_options.getToolbarColor());
|
|
3123
3117
|
} catch (IllegalArgumentException e) {
|
|
3124
3118
|
// Use system default
|
|
3125
3119
|
}
|
|
3126
3120
|
}
|
|
3127
3121
|
}
|
|
3122
|
+
|
|
3123
|
+
SystemUiChromeSupport.applyDialogWindowChrome(
|
|
3124
|
+
getWindow(),
|
|
3125
|
+
decorView,
|
|
3126
|
+
isAndroid15Plus,
|
|
3127
|
+
statusBarColor,
|
|
3128
|
+
navigationBarColor,
|
|
3129
|
+
lightStatusBars
|
|
3130
|
+
);
|
|
3128
3131
|
}
|
|
3129
3132
|
|
|
3130
3133
|
requestSafeAreaInsets();
|
|
@@ -3443,7 +3446,7 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
3443
3446
|
return;
|
|
3444
3447
|
}
|
|
3445
3448
|
|
|
3446
|
-
boolean isAndroid15Plus = Build.VERSION.SDK_INT
|
|
3449
|
+
boolean isAndroid15Plus = SystemUiChromeSupport.requiresEdgeToEdgeChrome(Build.VERSION.SDK_INT);
|
|
3447
3450
|
View toolbarView = findBrowserContentDescendant(R.id.tool_bar);
|
|
3448
3451
|
applyWindowInsetsToWebView(windowInsets, isAndroid15Plus, toolbarView);
|
|
3449
3452
|
}
|
|
@@ -3515,7 +3518,13 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
3515
3518
|
return;
|
|
3516
3519
|
}
|
|
3517
3520
|
|
|
3518
|
-
boolean
|
|
3521
|
+
boolean layoutBehindNavigationBar =
|
|
3522
|
+
!backLayerActive && SystemUiChromeSupport.usesLayoutBehindNavigationBar(Build.VERSION.SDK_INT, isEdgeToEdge);
|
|
3523
|
+
boolean applyBottomInset = SafeAreaInsetsSupport.shouldInsetBottomForContainer(
|
|
3524
|
+
_options.getEnabledSafeMargin(),
|
|
3525
|
+
isEdgeToEdge,
|
|
3526
|
+
layoutBehindNavigationBar
|
|
3527
|
+
);
|
|
3519
3528
|
int statusBarTop = SafeAreaInsetsSupport.resolveStatusBarTop(
|
|
3520
3529
|
bars.top,
|
|
3521
3530
|
bars.bottom,
|
|
@@ -4086,6 +4095,88 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
4086
4095
|
}
|
|
4087
4096
|
}
|
|
4088
4097
|
|
|
4098
|
+
private void ensureFileChooserLaunchers() {
|
|
4099
|
+
if (!(activity instanceof ComponentActivity componentActivity)) {
|
|
4100
|
+
return;
|
|
4101
|
+
}
|
|
4102
|
+
if (fileChooserLauncher != null && cameraCaptureLauncher != null) {
|
|
4103
|
+
return;
|
|
4104
|
+
}
|
|
4105
|
+
|
|
4106
|
+
String launcherSuffix = (instanceId != null && !instanceId.isEmpty() ? instanceId + "_" : "") + Integer.toHexString(hashCode());
|
|
4107
|
+
if (cameraCaptureLauncher == null) {
|
|
4108
|
+
cameraCaptureLauncher = componentActivity
|
|
4109
|
+
.getActivityResultRegistry()
|
|
4110
|
+
.register(
|
|
4111
|
+
"inappbrowser_camera_capture_" + launcherSuffix,
|
|
4112
|
+
new ActivityResultContracts.StartActivityForResult(),
|
|
4113
|
+
this::handleCameraCaptureActivityResult
|
|
4114
|
+
);
|
|
4115
|
+
}
|
|
4116
|
+
if (fileChooserLauncher == null) {
|
|
4117
|
+
fileChooserLauncher = componentActivity
|
|
4118
|
+
.getActivityResultRegistry()
|
|
4119
|
+
.register(
|
|
4120
|
+
"inappbrowser_file_chooser_" + launcherSuffix,
|
|
4121
|
+
new ActivityResultContracts.StartActivityForResult(),
|
|
4122
|
+
this::handleFileChooserActivityResult
|
|
4123
|
+
);
|
|
4124
|
+
}
|
|
4125
|
+
}
|
|
4126
|
+
|
|
4127
|
+
private void clearActivityResultLaunchers() {
|
|
4128
|
+
if (cameraCaptureLauncher != null) {
|
|
4129
|
+
cameraCaptureLauncher.unregister();
|
|
4130
|
+
cameraCaptureLauncher = null;
|
|
4131
|
+
}
|
|
4132
|
+
if (fileChooserLauncher != null) {
|
|
4133
|
+
fileChooserLauncher.unregister();
|
|
4134
|
+
fileChooserLauncher = null;
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
|
|
4138
|
+
private void handleCameraCaptureActivityResult(ActivityResult result) {
|
|
4139
|
+
FileChooserRequestSupport.FileChooserRequest request = activeFileChooserRequest;
|
|
4140
|
+
if (!FileChooserRequestSupport.isActive(request, activeFileChooserRequest)) {
|
|
4141
|
+
return;
|
|
4142
|
+
}
|
|
4143
|
+
|
|
4144
|
+
Uri[] results = null;
|
|
4145
|
+
if (result.getResultCode() == Activity.RESULT_OK && request.tempCameraUri != null) {
|
|
4146
|
+
results = new Uri[] { request.tempCameraUri };
|
|
4147
|
+
}
|
|
4148
|
+
if (FileChooserRequestSupport.completeIfActive(request, activeFileChooserRequest, results)) {
|
|
4149
|
+
request.tempCameraUri = null;
|
|
4150
|
+
clearActiveFileChooserRequest(request);
|
|
4151
|
+
}
|
|
4152
|
+
}
|
|
4153
|
+
|
|
4154
|
+
private void handleFileChooserActivityResult(ActivityResult result) {
|
|
4155
|
+
FileChooserRequestSupport.FileChooserRequest request = activeFileChooserRequest;
|
|
4156
|
+
if (!FileChooserRequestSupport.isActive(request, activeFileChooserRequest)) {
|
|
4157
|
+
return;
|
|
4158
|
+
}
|
|
4159
|
+
|
|
4160
|
+
Uri[] results = null;
|
|
4161
|
+
if (result.getResultCode() == Activity.RESULT_OK) {
|
|
4162
|
+
Intent data = result.getData();
|
|
4163
|
+
if (data != null) {
|
|
4164
|
+
if (data.getClipData() != null) {
|
|
4165
|
+
int count = data.getClipData().getItemCount();
|
|
4166
|
+
results = new Uri[count];
|
|
4167
|
+
for (int i = 0; i < count; i++) {
|
|
4168
|
+
results[i] = data.getClipData().getItemAt(i).getUri();
|
|
4169
|
+
}
|
|
4170
|
+
} else if (data.getData() != null) {
|
|
4171
|
+
results = new Uri[] { data.getData() };
|
|
4172
|
+
}
|
|
4173
|
+
}
|
|
4174
|
+
}
|
|
4175
|
+
if (FileChooserRequestSupport.completeIfActive(request, activeFileChooserRequest, results)) {
|
|
4176
|
+
clearActiveFileChooserRequest(request);
|
|
4177
|
+
}
|
|
4178
|
+
}
|
|
4179
|
+
|
|
4089
4180
|
private void beginFileChooserRequest(ValueCallback<Uri[]> filePathCallback, String[] acceptTypes, boolean isMultiple) {
|
|
4090
4181
|
if (activeFileChooserRequest != null) {
|
|
4091
4182
|
FileChooserRequestSupport.cancel(activeFileChooserRequest);
|
|
@@ -4106,17 +4197,6 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
4106
4197
|
tempCameraUri = activeFileChooserRequest != null ? activeFileChooserRequest.tempCameraUri : null;
|
|
4107
4198
|
}
|
|
4108
4199
|
|
|
4109
|
-
void completeLegacyFileChooserResult(Uri[] results) {
|
|
4110
|
-
FileChooserRequestSupport.FileChooserRequest request = activeFileChooserRequest;
|
|
4111
|
-
if (request == null) {
|
|
4112
|
-
return;
|
|
4113
|
-
}
|
|
4114
|
-
if (FileChooserRequestSupport.completeIfActive(request, activeFileChooserRequest, results)) {
|
|
4115
|
-
request.tempCameraUri = null;
|
|
4116
|
-
clearActiveFileChooserRequest(request);
|
|
4117
|
-
}
|
|
4118
|
-
}
|
|
4119
|
-
|
|
4120
4200
|
private void openFileChooser(FileChooserRequestSupport.FileChooserRequest request) {
|
|
4121
4201
|
if (!FileChooserRequestSupport.isActive(request, activeFileChooserRequest)) {
|
|
4122
4202
|
return;
|
|
@@ -4127,88 +4207,14 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
4127
4207
|
Log.d("InAppBrowser", "File picker using action: " + intent.getAction() + ", MIME types: " + mimeTypes);
|
|
4128
4208
|
|
|
4129
4209
|
try {
|
|
4130
|
-
|
|
4131
|
-
androidx.activity.ComponentActivity componentActivity = (androidx.activity.ComponentActivity) activity;
|
|
4132
|
-
componentActivity
|
|
4133
|
-
.getActivityResultRegistry()
|
|
4134
|
-
.register(
|
|
4135
|
-
"file_chooser",
|
|
4136
|
-
new androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult(),
|
|
4137
|
-
(result) -> {
|
|
4138
|
-
if (!FileChooserRequestSupport.isActive(request, activeFileChooserRequest)) {
|
|
4139
|
-
return;
|
|
4140
|
-
}
|
|
4141
|
-
Uri[] results = null;
|
|
4142
|
-
if (result.getResultCode() == Activity.RESULT_OK) {
|
|
4143
|
-
Intent data = result.getData();
|
|
4144
|
-
if (data != null) {
|
|
4145
|
-
if (data.getClipData() != null) {
|
|
4146
|
-
// Handle multiple files
|
|
4147
|
-
int count = data.getClipData().getItemCount();
|
|
4148
|
-
results = new Uri[count];
|
|
4149
|
-
for (int i = 0; i < count; i++) {
|
|
4150
|
-
results[i] = data.getClipData().getItemAt(i).getUri();
|
|
4151
|
-
}
|
|
4152
|
-
} else if (data.getData() != null) {
|
|
4153
|
-
// Handle single file
|
|
4154
|
-
results = new Uri[] { data.getData() };
|
|
4155
|
-
}
|
|
4156
|
-
}
|
|
4157
|
-
}
|
|
4158
|
-
if (FileChooserRequestSupport.completeIfActive(request, activeFileChooserRequest, results)) {
|
|
4159
|
-
clearActiveFileChooserRequest(request);
|
|
4160
|
-
}
|
|
4161
|
-
}
|
|
4162
|
-
)
|
|
4163
|
-
.launch(Intent.createChooser(intent, "Select File"));
|
|
4164
|
-
} else {
|
|
4165
|
-
// Fallback for non-ComponentActivity
|
|
4166
|
-
activity.startActivityForResult(Intent.createChooser(intent, "Select File"), FILE_CHOOSER_REQUEST_CODE);
|
|
4167
|
-
}
|
|
4210
|
+
launchFileChooserIntent(Intent.createChooser(intent, "Select File"), request);
|
|
4168
4211
|
} catch (ActivityNotFoundException e) {
|
|
4169
4212
|
// If no app can handle the specific MIME type, try with a more generic one
|
|
4170
4213
|
Log.e("InAppBrowser", "No app available for types: " + mimeTypes + ", trying with */*");
|
|
4171
4214
|
intent.setType("*/*");
|
|
4172
4215
|
intent.removeExtra(Intent.EXTRA_MIME_TYPES);
|
|
4173
4216
|
try {
|
|
4174
|
-
|
|
4175
|
-
androidx.activity.ComponentActivity componentActivity = (androidx.activity.ComponentActivity) activity;
|
|
4176
|
-
componentActivity
|
|
4177
|
-
.getActivityResultRegistry()
|
|
4178
|
-
.register(
|
|
4179
|
-
"file_chooser",
|
|
4180
|
-
new androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult(),
|
|
4181
|
-
(result) -> {
|
|
4182
|
-
if (!FileChooserRequestSupport.isActive(request, activeFileChooserRequest)) {
|
|
4183
|
-
return;
|
|
4184
|
-
}
|
|
4185
|
-
Uri[] results = null;
|
|
4186
|
-
if (result.getResultCode() == Activity.RESULT_OK) {
|
|
4187
|
-
Intent data = result.getData();
|
|
4188
|
-
if (data != null) {
|
|
4189
|
-
if (data.getClipData() != null) {
|
|
4190
|
-
// Handle multiple files
|
|
4191
|
-
int count = data.getClipData().getItemCount();
|
|
4192
|
-
results = new Uri[count];
|
|
4193
|
-
for (int i = 0; i < count; i++) {
|
|
4194
|
-
results[i] = data.getClipData().getItemAt(i).getUri();
|
|
4195
|
-
}
|
|
4196
|
-
} else if (data.getData() != null) {
|
|
4197
|
-
// Handle single file
|
|
4198
|
-
results = new Uri[] { data.getData() };
|
|
4199
|
-
}
|
|
4200
|
-
}
|
|
4201
|
-
}
|
|
4202
|
-
if (FileChooserRequestSupport.completeIfActive(request, activeFileChooserRequest, results)) {
|
|
4203
|
-
clearActiveFileChooserRequest(request);
|
|
4204
|
-
}
|
|
4205
|
-
}
|
|
4206
|
-
)
|
|
4207
|
-
.launch(Intent.createChooser(intent, "Select File"));
|
|
4208
|
-
} else {
|
|
4209
|
-
// Fallback for non-ComponentActivity
|
|
4210
|
-
activity.startActivityForResult(Intent.createChooser(intent, "Select File"), FILE_CHOOSER_REQUEST_CODE);
|
|
4211
|
-
}
|
|
4217
|
+
launchFileChooserIntent(Intent.createChooser(intent, "Select File"), request);
|
|
4212
4218
|
} catch (ActivityNotFoundException ex) {
|
|
4213
4219
|
// If still failing, report error
|
|
4214
4220
|
Log.e("InAppBrowser", "No app can handle file picker", ex);
|
|
@@ -4219,6 +4225,18 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
4219
4225
|
}
|
|
4220
4226
|
}
|
|
4221
4227
|
|
|
4228
|
+
private void launchFileChooserIntent(Intent chooserIntent, FileChooserRequestSupport.FileChooserRequest request) {
|
|
4229
|
+
ensureFileChooserLaunchers();
|
|
4230
|
+
if (fileChooserLauncher == null) {
|
|
4231
|
+
Log.e("InAppBrowser", "ComponentActivity required for file chooser");
|
|
4232
|
+
if (FileChooserRequestSupport.cancelIfActive(request, activeFileChooserRequest)) {
|
|
4233
|
+
clearActiveFileChooserRequest(request);
|
|
4234
|
+
}
|
|
4235
|
+
return;
|
|
4236
|
+
}
|
|
4237
|
+
fileChooserLauncher.launch(chooserIntent);
|
|
4238
|
+
}
|
|
4239
|
+
|
|
4222
4240
|
public void reload() {
|
|
4223
4241
|
if (_webView == null) {
|
|
4224
4242
|
Log.w("InAppBrowser", "Cannot reload - WebView is null");
|
|
@@ -4700,8 +4718,7 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
4700
4718
|
|
|
4701
4719
|
// Also ensure status bar gets the color
|
|
4702
4720
|
if (getWindow() != null) {
|
|
4703
|
-
|
|
4704
|
-
getWindow().setStatusBarColor(toolbarColor);
|
|
4721
|
+
SystemUiChromeSupport.applyLegacySystemBarColors(getWindow(), toolbarColor, null);
|
|
4705
4722
|
|
|
4706
4723
|
// Determine proper status bar text color (light or dark icons)
|
|
4707
4724
|
boolean isDarkBackground = isDarkColor(toolbarColor);
|
|
@@ -6165,21 +6182,6 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
6165
6182
|
return false;
|
|
6166
6183
|
}
|
|
6167
6184
|
|
|
6168
|
-
@Override
|
|
6169
|
-
public void onBackPressed() {
|
|
6170
|
-
if (
|
|
6171
|
-
_webView != null &&
|
|
6172
|
-
_webView.canGoBack() &&
|
|
6173
|
-
(TextUtils.equals(_options.getToolbarType(), "navigation") || _options.getActiveNativeNavigationForWebview())
|
|
6174
|
-
) {
|
|
6175
|
-
_webView.goBack();
|
|
6176
|
-
} else if (!_options.getDisableGoBackOnNativeApplication()) {
|
|
6177
|
-
String currentUrl = getUrl();
|
|
6178
|
-
_options.getCallbacks().closeEvent(currentUrl);
|
|
6179
|
-
super.onBackPressed();
|
|
6180
|
-
}
|
|
6181
|
-
}
|
|
6182
|
-
|
|
6183
6185
|
@Override
|
|
6184
6186
|
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
|
6185
6187
|
// Forward volume key events to the MainActivity
|
|
@@ -6384,6 +6386,8 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
6384
6386
|
// Clear any remaining proxied requests
|
|
6385
6387
|
proxiedRequestsHashmap.clear();
|
|
6386
6388
|
|
|
6389
|
+
clearActivityResultLaunchers();
|
|
6390
|
+
|
|
6387
6391
|
try {
|
|
6388
6392
|
super.dismiss();
|
|
6389
6393
|
} catch (Exception e) {
|
package/dist/docs.json
CHANGED
|
@@ -1592,7 +1592,39 @@
|
|
|
1592
1592
|
"name": "since"
|
|
1593
1593
|
}
|
|
1594
1594
|
],
|
|
1595
|
-
"docs": "Controls whether the webview should persist website data such as cache, cookies, local storage,\nIndexedDB, and session data.\n\nWhen false, iOS uses a non-persistent `WKWebsiteDataStore`. Android disables per-view cache and\
|
|
1595
|
+
"docs": "Controls whether the webview should persist website data such as cache, cookies, local storage,\nIndexedDB, and session data.\n\nWhen false, iOS uses a non-persistent `WKWebsiteDataStore`. Android disables per-view cache and\ndatabase storage where the system WebView supports it while keeping DOM storage available so\nSPAs can use `localStorage`. Android cookies use the shared WebView cookie store; clearing them\nalso affects the host WebView, so prefer per-URL cookie helpers instead of relying on\nprocess-global wipes.",
|
|
1596
|
+
"complexTypes": [],
|
|
1597
|
+
"type": "boolean | undefined"
|
|
1598
|
+
},
|
|
1599
|
+
{
|
|
1600
|
+
"name": "clearCookiesOnOpen",
|
|
1601
|
+
"tags": [
|
|
1602
|
+
{
|
|
1603
|
+
"text": "false",
|
|
1604
|
+
"name": "default"
|
|
1605
|
+
},
|
|
1606
|
+
{
|
|
1607
|
+
"text": "8.16.0",
|
|
1608
|
+
"name": "since"
|
|
1609
|
+
}
|
|
1610
|
+
],
|
|
1611
|
+
"docs": "Clear all cookies from the InAppBrowser data store before the first navigation of this webview.\n\nCordova `clearcache: 'yes'` parity. On Android, uses process-global `CookieManager.removeAllCookies()`\nand blocks until the wipe completes. On iOS, clears cookies from the webview's `WKWebsiteDataStore`\n(isolated plugin store by default). On Web this is a no-op.",
|
|
1612
|
+
"complexTypes": [],
|
|
1613
|
+
"type": "boolean | undefined"
|
|
1614
|
+
},
|
|
1615
|
+
{
|
|
1616
|
+
"name": "clearCacheOnOpen",
|
|
1617
|
+
"tags": [
|
|
1618
|
+
{
|
|
1619
|
+
"text": "false",
|
|
1620
|
+
"name": "default"
|
|
1621
|
+
},
|
|
1622
|
+
{
|
|
1623
|
+
"text": "8.16.0",
|
|
1624
|
+
"name": "since"
|
|
1625
|
+
}
|
|
1626
|
+
],
|
|
1627
|
+
"docs": "Clear cached website data before the first navigation of this webview.\n\nCordova `cleardata: 'yes'` parity. On Android, calls `WebView.clearCache(true)` after the webview\nis created. On iOS, removes disk and memory cache from the webview's `WKWebsiteDataStore`.\nOn Web this is a no-op.",
|
|
1596
1628
|
"complexTypes": [],
|
|
1597
1629
|
"type": "boolean | undefined"
|
|
1598
1630
|
},
|
|
@@ -736,14 +736,37 @@ export interface OpenWebViewOptions {
|
|
|
736
736
|
* IndexedDB, and session data.
|
|
737
737
|
*
|
|
738
738
|
* When false, iOS uses a non-persistent `WKWebsiteDataStore`. Android disables per-view cache and
|
|
739
|
-
*
|
|
740
|
-
*
|
|
741
|
-
* helpers instead of relying on
|
|
739
|
+
* database storage where the system WebView supports it while keeping DOM storage available so
|
|
740
|
+
* SPAs can use `localStorage`. Android cookies use the shared WebView cookie store; clearing them
|
|
741
|
+
* also affects the host WebView, so prefer per-URL cookie helpers instead of relying on
|
|
742
|
+
* process-global wipes.
|
|
742
743
|
*
|
|
743
744
|
* @default true
|
|
744
745
|
* @since 8.6.36
|
|
745
746
|
*/
|
|
746
747
|
persistWebViewData?: boolean;
|
|
748
|
+
/**
|
|
749
|
+
* Clear all cookies from the InAppBrowser data store before the first navigation of this webview.
|
|
750
|
+
*
|
|
751
|
+
* Cordova `clearcache: 'yes'` parity. On Android, uses process-global `CookieManager.removeAllCookies()`
|
|
752
|
+
* and blocks until the wipe completes. On iOS, clears cookies from the webview's `WKWebsiteDataStore`
|
|
753
|
+
* (isolated plugin store by default). On Web this is a no-op.
|
|
754
|
+
*
|
|
755
|
+
* @default false
|
|
756
|
+
* @since 8.16.0
|
|
757
|
+
*/
|
|
758
|
+
clearCookiesOnOpen?: boolean;
|
|
759
|
+
/**
|
|
760
|
+
* Clear cached website data before the first navigation of this webview.
|
|
761
|
+
*
|
|
762
|
+
* Cordova `cleardata: 'yes'` parity. On Android, calls `WebView.clearCache(true)` after the webview
|
|
763
|
+
* is created. On iOS, removes disk and memory cache from the webview's `WKWebsiteDataStore`.
|
|
764
|
+
* On Web this is a no-op.
|
|
765
|
+
*
|
|
766
|
+
* @default false
|
|
767
|
+
* @since 8.16.0
|
|
768
|
+
*/
|
|
769
|
+
clearCacheOnOpen?: boolean;
|
|
747
770
|
/**
|
|
748
771
|
* Share the host Capacitor WebView's website data store (cookies, local storage, etc.).
|
|
749
772
|
*
|