@capgo/inappbrowser 8.1.26 → 8.3.0-alpha.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.
@@ -6,6 +6,11 @@ import android.content.ComponentName;
6
6
  import android.content.Intent;
7
7
  import android.content.pm.PackageManager;
8
8
  import android.content.pm.ResolveInfo;
9
+ import android.graphics.Bitmap;
10
+ import android.graphics.BitmapFactory;
11
+ import android.graphics.Canvas;
12
+ import android.graphics.Color;
13
+ import android.graphics.drawable.Drawable;
9
14
  import android.net.Uri;
10
15
  import android.os.Bundle;
11
16
  import android.text.TextUtils;
@@ -16,6 +21,8 @@ import androidx.activity.result.ActivityResult;
16
21
  import androidx.activity.result.ActivityResultLauncher;
17
22
  import androidx.activity.result.contract.ActivityResultContracts;
18
23
  import androidx.annotation.NonNull;
24
+ import androidx.appcompat.content.res.AppCompatResources;
25
+ import androidx.browser.customtabs.CustomTabColorSchemeParams;
19
26
  import androidx.browser.customtabs.CustomTabsCallback;
20
27
  import androidx.browser.customtabs.CustomTabsClient;
21
28
  import androidx.browser.customtabs.CustomTabsIntent;
@@ -39,8 +46,6 @@ import java.util.Iterator;
39
46
  import java.util.List;
40
47
  import java.util.Map;
41
48
  import java.util.UUID;
42
- import java.util.regex.Pattern;
43
- import java.util.regex.PatternSyntaxException;
44
49
  import org.json.JSONException;
45
50
  import org.json.JSONObject;
46
51
 
@@ -55,7 +60,7 @@ import org.json.JSONObject;
55
60
  )
56
61
  public class InAppBrowserPlugin extends Plugin implements WebViewDialog.PermissionHandler {
57
62
 
58
- private final String pluginVersion = "8.1.26";
63
+ private final String pluginVersion = "8.3.0-alpha.0";
59
64
 
60
65
  public static final String CUSTOM_TAB_PACKAGE_NAME = "com.android.chrome"; // Change when in stable
61
66
  private CustomTabsClient customTabsClient;
@@ -337,7 +342,66 @@ public class InAppBrowserPlugin extends Plugin implements WebViewDialog.Permissi
337
342
  }
338
343
  currentUrl = url;
339
344
  CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(getCustomTabsSession());
345
+
346
+ // --- Chrome Custom Tab UI customization ---
347
+
348
+ // Toolbar color (applied to both light and dark color schemes)
349
+ String toolbarColor = call.getString("toolbarColor");
350
+ if (toolbarColor != null) {
351
+ try {
352
+ int colorInt = Color.parseColor(toolbarColor);
353
+ CustomTabColorSchemeParams colorParams = new CustomTabColorSchemeParams.Builder()
354
+ .setToolbarColor(colorInt)
355
+ .setNavigationBarColor(colorInt)
356
+ .build();
357
+ builder.setDefaultColorSchemeParams(colorParams);
358
+ builder.setColorSchemeParams(CustomTabsIntent.COLOR_SCHEME_DARK, colorParams);
359
+ } catch (IllegalArgumentException e) {
360
+ Log.w(getLogTag(), "Invalid toolbarColor value: " + toolbarColor, e);
361
+ }
362
+ }
363
+
364
+ // Auto-hide URL bar on scroll
365
+ if (call.getBoolean("urlBarHidingEnabled", false)) {
366
+ builder.setUrlBarHidingEnabled(true);
367
+ }
368
+
369
+ // Show page <title> instead of URL
370
+ if (call.getBoolean("showTitle", false)) {
371
+ builder.setShowTitle(true);
372
+ }
373
+
374
+ // Replace X close icon with a back arrow
375
+ if (call.getBoolean("showArrow", false)) {
376
+ Drawable arrowDrawable = AppCompatResources.getDrawable(getContext(), R.drawable.arrow_back_enabled);
377
+ if (arrowDrawable != null) {
378
+ Bitmap backArrow = Bitmap.createBitmap(
379
+ arrowDrawable.getIntrinsicWidth(),
380
+ arrowDrawable.getIntrinsicHeight(),
381
+ Bitmap.Config.ARGB_8888
382
+ );
383
+ Canvas canvas = new Canvas(backArrow);
384
+ arrowDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
385
+ arrowDrawable.draw(canvas);
386
+ builder.setCloseButtonIcon(backArrow);
387
+ }
388
+ }
389
+
390
+ // Remove share action from overflow menu
391
+ if (call.getBoolean("disableShare", false)) {
392
+ builder.setShareState(CustomTabsIntent.SHARE_STATE_OFF);
393
+ }
394
+
340
395
  CustomTabsIntent tabsIntent = builder.build();
396
+
397
+ // Undocumented Chromium flags — these may stop working on future Chrome updates
398
+ if (call.getBoolean("disableBookmark", false)) {
399
+ tabsIntent.intent.putExtra("org.chromium.chrome.browser.customtabs.EXTRA_DISABLE_STAR_BUTTON", true);
400
+ }
401
+ if (call.getBoolean("disableDownload", false)) {
402
+ tabsIntent.intent.putExtra("org.chromium.chrome.browser.customtabs.EXTRA_DISABLE_DOWNLOAD_BUTTON", true);
403
+ }
404
+
341
405
  tabsIntent.intent.putExtra(Intent.EXTRA_REFERRER, Uri.parse(Intent.URI_ANDROID_APP_SCHEME + "//" + getContext().getPackageName()));
342
406
  tabsIntent.intent.putExtra(android.provider.Browser.EXTRA_HEADERS, this.getHeaders(call));
343
407
 
@@ -511,14 +575,7 @@ public class InAppBrowserPlugin extends Plugin implements WebViewDialog.Permissi
511
575
  options.setTextZoom(textZoom);
512
576
  }
513
577
 
514
- String proxyRequestsStr = call.getString("proxyRequests");
515
- if (proxyRequestsStr != null) {
516
- try {
517
- options.setProxyRequestsPattern(Pattern.compile(proxyRequestsStr));
518
- } catch (PatternSyntaxException e) {
519
- Log.e("WebViewDialog", String.format("Pattern '%s' is not a valid pattern", proxyRequestsStr));
520
- }
521
- }
578
+ options.setProxyRequests(Boolean.TRUE.equals(call.getBoolean("proxyRequests", false)));
522
579
 
523
580
  try {
524
581
  // Try to set buttonNearDone if present, with better error handling
@@ -667,6 +724,22 @@ public class InAppBrowserPlugin extends Plugin implements WebViewDialog.Permissi
667
724
  notifyListeners("confirmBtnClicked", new JSObject().put("id", webViewId).put("url", url));
668
725
  }
669
726
 
727
+ @Override
728
+ public void proxyRequestEvent(String requestId, String url, String method, String headersJson, String body, String wvId) {
729
+ JSObject data = new JSObject();
730
+ data.put("requestId", requestId);
731
+ data.put("url", url);
732
+ data.put("method", method);
733
+ try {
734
+ data.put("headers", new JSObject(headersJson));
735
+ } catch (Exception e) {
736
+ data.put("headers", new JSObject());
737
+ }
738
+ data.put("body", body);
739
+ data.put("webviewId", wvId);
740
+ notifyListeners("proxyRequest", data);
741
+ }
742
+
670
743
  @Override
671
744
  public void javascriptCallback(String message) {
672
745
  // Handle the message received from JavaScript
@@ -1004,24 +1077,20 @@ public class InAppBrowserPlugin extends Plugin implements WebViewDialog.Permissi
1004
1077
  }
1005
1078
 
1006
1079
  @PluginMethod
1007
- public void lsuakdchgbbaHandleProxiedRequest(PluginCall call) {
1080
+ public void handleProxyRequest(PluginCall call) {
1081
+ String requestId = call.getString("requestId");
1082
+ if (requestId == null) {
1083
+ call.reject("requestId is required");
1084
+ return;
1085
+ }
1008
1086
  String webviewId = call.getString("webviewId");
1009
1087
  WebViewDialog webViewDialog = webviewId != null ? webViewDialogs.get(webviewId) : resolveDialog(null);
1010
- if (webViewDialog != null) {
1011
- Boolean ok = call.getBoolean("ok", false);
1012
- String id = call.getString("id");
1013
- if (id == null) {
1014
- Log.e("InAppBrowserProxy", "CRITICAL ERROR, proxy id = null");
1015
- return;
1016
- }
1017
- if (Boolean.FALSE.equals(ok)) {
1018
- String result = call.getString("result", "");
1019
- webViewDialog.handleProxyResultError(result, id);
1020
- } else {
1021
- JSONObject object = call.getObject("result");
1022
- webViewDialog.handleProxyResultOk(object, id);
1023
- }
1088
+ if (webViewDialog == null) {
1089
+ call.reject("Target WebView not found for proxy request");
1090
+ return;
1024
1091
  }
1092
+ JSObject response = call.getObject("response");
1093
+ webViewDialog.handleProxyResponse(requestId, response);
1025
1094
  call.resolve();
1026
1095
  }
1027
1096
 
@@ -9,8 +9,6 @@ import java.io.InputStream;
9
9
  import java.util.ArrayList;
10
10
  import java.util.List;
11
11
  import java.util.Objects;
12
- import java.util.Objects;
13
- import java.util.regex.Pattern;
14
12
 
15
13
  public class Options {
16
14
 
@@ -170,7 +168,7 @@ public class Options {
170
168
  private boolean ignoreUntrustedSSLError;
171
169
  private String preShowScript;
172
170
  private String toolbarTextColor;
173
- private Pattern proxyRequestsPattern = null;
171
+ private boolean proxyRequests = false;
174
172
  private boolean materialPicker = false;
175
173
  private int textZoom = 100; // Default text zoom is 100%
176
174
  private boolean preventDeeplink = false;
@@ -262,12 +260,12 @@ public class Options {
262
260
  this.useTopInset = useTopInset;
263
261
  }
264
262
 
265
- public Pattern getProxyRequestsPattern() {
266
- return proxyRequestsPattern;
263
+ public boolean getProxyRequests() {
264
+ return proxyRequests;
267
265
  }
268
266
 
269
- public void setProxyRequestsPattern(Pattern proxyRequestsPattern) {
270
- this.proxyRequestsPattern = proxyRequestsPattern;
267
+ public void setProxyRequests(boolean proxyRequests) {
268
+ this.proxyRequests = proxyRequests;
271
269
  }
272
270
 
273
271
  public PluginCall getPluginCall() {
@@ -0,0 +1,60 @@
1
+ package ee.forgr.capacitor_inappbrowser;
2
+
3
+ import android.webkit.JavascriptInterface;
4
+ import java.util.concurrent.ConcurrentHashMap;
5
+
6
+ public class ProxyBridge {
7
+
8
+ private static final int MAX_STORED_REQUESTS = 256;
9
+
10
+ public static class StoredRequest {
11
+
12
+ public final String method;
13
+ public final String headersJson;
14
+ public final String base64Body;
15
+
16
+ public StoredRequest(String method, String headersJson, String base64Body) {
17
+ this.method = method != null ? method : "";
18
+ this.headersJson = headersJson != null ? headersJson : "{}";
19
+ this.base64Body = base64Body != null ? base64Body : "";
20
+ }
21
+ }
22
+
23
+ private final ConcurrentHashMap<String, StoredRequest> storedRequests = new ConcurrentHashMap<>();
24
+ private final String accessToken;
25
+
26
+ /**
27
+ * @param accessToken Random token generated per webview instance.
28
+ * The injected proxy-bridge script receives this token and must
29
+ * pass it on every storeRequest call. Page JS that doesn't know
30
+ * the token cannot abuse the interface.
31
+ */
32
+ public ProxyBridge(String accessToken) {
33
+ this.accessToken = accessToken;
34
+ }
35
+
36
+ @JavascriptInterface
37
+ public void storeRequest(String token, String requestId, String method, String headersJson, String base64Body) {
38
+ if (token == null || !token.equals(accessToken)) {
39
+ return;
40
+ }
41
+ if (requestId == null || requestId.isEmpty()) {
42
+ return;
43
+ }
44
+ if (storedRequests.size() >= MAX_STORED_REQUESTS) {
45
+ // Remove an arbitrary entry to prevent unbounded growth
46
+ var it = storedRequests.keys().asIterator();
47
+ if (it.hasNext()) {
48
+ storedRequests.remove(it.next());
49
+ }
50
+ }
51
+ storedRequests.put(requestId, new StoredRequest(method, headersJson, base64Body));
52
+ }
53
+
54
+ public StoredRequest getAndRemove(String requestId) {
55
+ if (requestId == null) {
56
+ return null;
57
+ }
58
+ return storedRequests.remove(requestId);
59
+ }
60
+ }
@@ -14,4 +14,6 @@ public interface WebViewCallbacks {
14
14
  public void buttonNearDoneClicked();
15
15
 
16
16
  public void confirmBtnClicked(String url);
17
+
18
+ public void proxyRequestEvent(String requestId, String url, String method, String headersJson, String body, String webviewId);
17
19
  }