@capgo/inappbrowser 7.3.2 → 7.4.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.
Files changed (29) hide show
  1. package/README.md +47 -41
  2. package/android/src/main/java/ee/forgr/capacitor_inappbrowser/InAppBrowserPlugin.java +149 -41
  3. package/android/src/main/java/ee/forgr/capacitor_inappbrowser/Options.java +65 -22
  4. package/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewCallbacks.java +2 -0
  5. package/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java +647 -92
  6. package/android/src/main/res/drawable/ic_share.xml +10 -0
  7. package/android/src/main/res/layout/activity_browser.xml +8 -0
  8. package/android/src/main/res/layout/tool_bar.xml +19 -7
  9. package/android/src/main/res/values/strings.xml +2 -0
  10. package/dist/docs.json +169 -35
  11. package/dist/esm/definitions.d.ts +222 -29
  12. package/dist/esm/definitions.js +12 -1
  13. package/dist/esm/definitions.js.map +1 -1
  14. package/dist/plugin.cjs.js +12 -1
  15. package/dist/plugin.cjs.js.map +1 -1
  16. package/dist/plugin.js +12 -1
  17. package/dist/plugin.js.map +1 -1
  18. package/ios/Plugin/InAppBrowserPlugin.swift +339 -44
  19. package/ios/Plugin/WKWebViewController.swift +478 -45
  20. package/package.json +1 -1
  21. package/ios/Plugin/Assets.xcassets/Back.imageset/Back.png +0 -0
  22. package/ios/Plugin/Assets.xcassets/Back.imageset/Back@2x.png +0 -0
  23. package/ios/Plugin/Assets.xcassets/Back.imageset/Back@3x.png +0 -0
  24. package/ios/Plugin/Assets.xcassets/Back.imageset/Contents.json +0 -26
  25. package/ios/Plugin/Assets.xcassets/Contents.json +0 -6
  26. package/ios/Plugin/Assets.xcassets/Forward.imageset/Contents.json +0 -26
  27. package/ios/Plugin/Assets.xcassets/Forward.imageset/Forward.png +0 -0
  28. package/ios/Plugin/Assets.xcassets/Forward.imageset/Forward@2x.png +0 -0
  29. package/ios/Plugin/Assets.xcassets/Forward.imageset/Forward@3x.png +0 -0
@@ -9,15 +9,21 @@ import android.content.Context;
9
9
  import android.content.DialogInterface;
10
10
  import android.content.Intent;
11
11
  import android.content.res.AssetManager;
12
+ import android.content.res.Resources;
12
13
  import android.graphics.Bitmap;
14
+ import android.graphics.Canvas;
13
15
  import android.graphics.Color;
16
+ import android.graphics.Paint;
14
17
  import android.graphics.Picture;
18
+ import android.graphics.PorterDuff;
19
+ import android.graphics.PorterDuffColorFilter;
15
20
  import android.graphics.drawable.PictureDrawable;
16
21
  import android.net.Uri;
17
22
  import android.net.http.SslError;
18
23
  import android.text.TextUtils;
19
24
  import android.util.Base64;
20
25
  import android.util.Log;
26
+ import android.util.TypedValue;
21
27
  import android.view.View;
22
28
  import android.view.Window;
23
29
  import android.view.WindowManager;
@@ -33,6 +39,7 @@ import android.webkit.WebResourceResponse;
33
39
  import android.webkit.WebView;
34
40
  import android.webkit.WebViewClient;
35
41
  import android.widget.ImageButton;
42
+ import android.widget.ImageView;
36
43
  import android.widget.TextView;
37
44
  import android.widget.Toast;
38
45
  import android.widget.Toolbar;
@@ -89,6 +96,7 @@ public class WebViewDialog extends Dialog {
89
96
  new HashMap<>();
90
97
  private final ExecutorService executorService =
91
98
  Executors.newCachedThreadPool();
99
+ private int iconColor = Color.BLACK; // Default icon color
92
100
 
93
101
  Semaphore preShowSemaphore = null;
94
102
  String preShowError = null;
@@ -200,14 +208,78 @@ public class WebViewDialog extends Dialog {
200
208
  setContentView(R.layout.activity_browser);
201
209
  getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
202
210
 
211
+ // Make status bar transparent
212
+ if (getWindow() != null) {
213
+ getWindow().setStatusBarColor(Color.TRANSPARENT);
214
+
215
+ // Add FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag to the window
216
+ getWindow()
217
+ .addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
218
+
219
+ // On Android 30+ clear FLAG_TRANSLUCENT_STATUS flag
220
+ getWindow()
221
+ .clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
222
+ }
223
+
203
224
  WindowInsetsControllerCompat insetsController =
204
- new WindowInsetsControllerCompat(getWindow(), getWindow().getDecorView());
205
- insetsController.setAppearanceLightStatusBars(false);
206
- getWindow()
207
- .getDecorView()
208
- .post(() -> {
209
- getWindow().setStatusBarColor(Color.BLACK);
210
- });
225
+ new WindowInsetsControllerCompat(
226
+ getWindow(),
227
+ getWindow() != null ? getWindow().getDecorView() : null
228
+ );
229
+
230
+ if (getWindow() != null) {
231
+ getWindow()
232
+ .getDecorView()
233
+ .post(() -> {
234
+ // Get status bar height
235
+ int statusBarHeight = 0;
236
+ int resourceId = getContext()
237
+ .getResources()
238
+ .getIdentifier("status_bar_height", "dimen", "android");
239
+ if (resourceId > 0) {
240
+ statusBarHeight = getContext()
241
+ .getResources()
242
+ .getDimensionPixelSize(resourceId);
243
+ }
244
+
245
+ // Find the status bar color view
246
+ View statusBarColorView = findViewById(R.id.status_bar_color_view);
247
+
248
+ // Set the height of the status bar color view
249
+ if (statusBarColorView != null) {
250
+ statusBarColorView.getLayoutParams().height = statusBarHeight;
251
+ statusBarColorView.requestLayout();
252
+
253
+ // Set color based on toolbar color or dark mode
254
+ if (
255
+ _options.getToolbarColor() != null &&
256
+ !_options.getToolbarColor().isEmpty()
257
+ ) {
258
+ try {
259
+ // Use explicitly provided toolbar color for status bar
260
+ int toolbarColor = Color.parseColor(_options.getToolbarColor());
261
+ statusBarColorView.setBackgroundColor(toolbarColor);
262
+
263
+ // Set status bar text to white or black based on background
264
+ boolean isDarkBackground = isDarkColor(toolbarColor);
265
+ insetsController.setAppearanceLightStatusBars(
266
+ !isDarkBackground
267
+ );
268
+ } catch (IllegalArgumentException e) {
269
+ // Fallback to default black if color parsing fails
270
+ statusBarColorView.setBackgroundColor(Color.BLACK);
271
+ insetsController.setAppearanceLightStatusBars(false);
272
+ }
273
+ } else {
274
+ // Follow system dark mode if no toolbar color provided
275
+ boolean isDarkTheme = isDarkThemeEnabled();
276
+ int statusBarColor = isDarkTheme ? Color.BLACK : Color.WHITE;
277
+ statusBarColorView.setBackgroundColor(statusBarColor);
278
+ insetsController.setAppearanceLightStatusBars(!isDarkTheme);
279
+ }
280
+ }
281
+ });
282
+ }
211
283
 
212
284
  getWindow()
213
285
  .setLayout(
@@ -246,9 +318,25 @@ public class WebViewDialog extends Dialog {
246
318
  ValueCallback<Uri[]> filePathCallback,
247
319
  FileChooserParams fileChooserParams
248
320
  ) {
321
+ // Get the accept type safely
322
+ String acceptType = "*/*"; // Default to all file types
323
+ if (
324
+ fileChooserParams.getAcceptTypes() != null &&
325
+ fileChooserParams.getAcceptTypes().length > 0 &&
326
+ !TextUtils.isEmpty(fileChooserParams.getAcceptTypes()[0])
327
+ ) {
328
+ acceptType = fileChooserParams.getAcceptTypes()[0];
329
+ }
330
+
331
+ // Check if the file chooser is already open
332
+ if (mFilePathCallback != null) {
333
+ mFilePathCallback.onReceiveValue(null);
334
+ mFilePathCallback = null;
335
+ }
336
+
249
337
  openFileChooser(
250
338
  filePathCallback,
251
- fileChooserParams.getAcceptTypes()[0],
339
+ acceptType,
252
340
  fileChooserParams.getMode() == FileChooserParams.MODE_OPEN_MULTIPLE
253
341
  );
254
342
  return true;
@@ -433,16 +521,100 @@ public class WebViewDialog extends Dialog {
433
521
  mFilePathCallback = filePathCallback;
434
522
  Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
435
523
  intent.addCategory(Intent.CATEGORY_OPENABLE);
436
- intent.setType(acceptType); // Default to */*
524
+
525
+ // Fix MIME type handling
526
+ if (
527
+ acceptType == null ||
528
+ acceptType.isEmpty() ||
529
+ acceptType.equals("undefined")
530
+ ) {
531
+ acceptType = "*/*";
532
+ } else {
533
+ // Handle common web input accept types
534
+ if (acceptType.equals("image/*")) {
535
+ // Keep as is - image/*
536
+ } else if (acceptType.contains("image/")) {
537
+ // Specific image type requested but keep it general for better compatibility
538
+ acceptType = "image/*";
539
+ } else if (
540
+ acceptType.equals("audio/*") || acceptType.contains("audio/")
541
+ ) {
542
+ acceptType = "audio/*";
543
+ } else if (
544
+ acceptType.equals("video/*") || acceptType.contains("video/")
545
+ ) {
546
+ acceptType = "video/*";
547
+ } else if (acceptType.startsWith(".") || acceptType.contains(",")) {
548
+ // Handle file extensions like ".pdf, .docx" by using a general mime type
549
+ if (acceptType.contains(".pdf")) {
550
+ acceptType = "application/pdf";
551
+ } else if (
552
+ acceptType.contains(".doc") || acceptType.contains(".docx")
553
+ ) {
554
+ acceptType = "application/msword";
555
+ } else if (
556
+ acceptType.contains(".xls") || acceptType.contains(".xlsx")
557
+ ) {
558
+ acceptType = "application/vnd.ms-excel";
559
+ } else if (
560
+ acceptType.contains(".txt") || acceptType.contains(".text")
561
+ ) {
562
+ acceptType = "text/plain";
563
+ } else {
564
+ // Default for extension lists
565
+ acceptType = "*/*";
566
+ }
567
+ }
568
+ }
569
+
570
+ Log.d("InAppBrowser", "File picker using MIME type: " + acceptType);
571
+ intent.setType(acceptType);
437
572
  intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, isMultiple);
438
- activity.startActivityForResult(
439
- Intent.createChooser(intent, "Select File"),
440
- FILE_CHOOSER_REQUEST_CODE
441
- );
573
+
574
+ try {
575
+ activity.startActivityForResult(
576
+ Intent.createChooser(intent, "Select File"),
577
+ FILE_CHOOSER_REQUEST_CODE
578
+ );
579
+ } catch (ActivityNotFoundException e) {
580
+ // If no app can handle the specific MIME type, try with a more generic one
581
+ Log.e(
582
+ "InAppBrowser",
583
+ "No app available for type: " + acceptType + ", trying with */*"
584
+ );
585
+ intent.setType("*/*");
586
+ try {
587
+ activity.startActivityForResult(
588
+ Intent.createChooser(intent, "Select File"),
589
+ FILE_CHOOSER_REQUEST_CODE
590
+ );
591
+ } catch (ActivityNotFoundException ex) {
592
+ // If still failing, report error
593
+ Log.e("InAppBrowser", "No app can handle file picker", ex);
594
+ if (mFilePathCallback != null) {
595
+ mFilePathCallback.onReceiveValue(null);
596
+ mFilePathCallback = null;
597
+ }
598
+ }
599
+ }
442
600
  }
443
601
 
444
602
  public void reload() {
445
- _webView.reload();
603
+ if (_webView != null) {
604
+ // First stop any ongoing loading
605
+ _webView.stopLoading();
606
+
607
+ // Check if there's a URL to reload
608
+ if (_webView.getUrl() != null) {
609
+ // Reload the current page
610
+ _webView.reload();
611
+ Log.d("InAppBrowser", "Reloading page: " + _webView.getUrl());
612
+ } else if (_options != null && _options.getUrl() != null) {
613
+ // If webView URL is null but we have an initial URL, load that
614
+ setUrl(_options.getUrl());
615
+ Log.d("InAppBrowser", "Loading initial URL: " + _options.getUrl());
616
+ }
617
+ }
446
618
  }
447
619
 
448
620
  public void destroy() {
@@ -485,56 +657,72 @@ public class WebViewDialog extends Dialog {
485
657
  }
486
658
 
487
659
  private void setupToolbar() {
488
- _toolbar = this.findViewById(R.id.tool_bar);
489
- int color = Color.parseColor("#ffffff");
490
- try {
491
- color = Color.parseColor(_options.getToolbarColor());
492
- } catch (IllegalArgumentException e) {
493
- // Do nothing
494
- }
495
- _toolbar.setBackgroundColor(color);
496
- _toolbar.findViewById(R.id.backButton).setBackgroundColor(color);
497
- _toolbar.findViewById(R.id.forwardButton).setBackgroundColor(color);
498
- _toolbar.findViewById(R.id.closeButton).setBackgroundColor(color);
499
- _toolbar.findViewById(R.id.reloadButton).setBackgroundColor(color);
500
-
501
- if (!TextUtils.isEmpty(_options.getTitle())) {
502
- this.setTitle(_options.getTitle());
503
- } else {
660
+ _toolbar = findViewById(R.id.tool_bar);
661
+
662
+ // Apply toolbar color early, for ALL toolbar types, before any view configuration
663
+ if (
664
+ _options.getToolbarColor() != null &&
665
+ !_options.getToolbarColor().isEmpty()
666
+ ) {
504
667
  try {
505
- URI uri = new URI(_options.getUrl());
506
- this.setTitle(uri.getHost());
507
- } catch (URISyntaxException e) {
508
- this.setTitle(_options.getTitle());
509
- }
510
- }
668
+ int toolbarColor = Color.parseColor(_options.getToolbarColor());
669
+ _toolbar.setBackgroundColor(toolbarColor);
511
670
 
512
- View backButton = _toolbar.findViewById(R.id.backButton);
513
- backButton.setOnClickListener(
514
- new View.OnClickListener() {
515
- @Override
516
- public void onClick(View view) {
517
- if (_webView.canGoBack()) {
518
- _webView.goBack();
671
+ // Get toolbar title and ensure it gets the right color
672
+ TextView titleText = _toolbar.findViewById(R.id.titleText);
673
+
674
+ // Determine icon and text color
675
+ int iconColor;
676
+ if (
677
+ _options.getToolbarTextColor() != null &&
678
+ !_options.getToolbarTextColor().isEmpty()
679
+ ) {
680
+ try {
681
+ iconColor = Color.parseColor(_options.getToolbarTextColor());
682
+ } catch (IllegalArgumentException e) {
683
+ // Fallback to automatic detection if parsing fails
684
+ boolean isDarkBackground = isDarkColor(toolbarColor);
685
+ iconColor = isDarkBackground ? Color.WHITE : Color.BLACK;
519
686
  }
687
+ } else {
688
+ // No explicit toolbarTextColor, use automatic detection based on background
689
+ boolean isDarkBackground = isDarkColor(toolbarColor);
690
+ iconColor = isDarkBackground ? Color.WHITE : Color.BLACK;
520
691
  }
521
- }
522
- );
523
692
 
524
- View forwardButton = _toolbar.findViewById(R.id.forwardButton);
525
- forwardButton.setOnClickListener(
526
- new View.OnClickListener() {
527
- @Override
528
- public void onClick(View view) {
529
- if (_webView.canGoForward()) {
530
- _webView.goForward();
531
- }
693
+ // Store for later use with navigation buttons
694
+ this.iconColor = iconColor;
695
+
696
+ // Set title text color directly
697
+ titleText.setTextColor(iconColor);
698
+
699
+ // Apply colors to all buttons
700
+ applyColorToAllButtons(toolbarColor, iconColor);
701
+
702
+ // Also ensure status bar gets the color
703
+ if (getWindow() != null) {
704
+ // Set status bar color
705
+ getWindow().setStatusBarColor(toolbarColor);
706
+
707
+ // Determine proper status bar text color (light or dark icons)
708
+ boolean isDarkBackground = isDarkColor(toolbarColor);
709
+ WindowInsetsControllerCompat insetsController =
710
+ new WindowInsetsControllerCompat(
711
+ getWindow(),
712
+ getWindow().getDecorView()
713
+ );
714
+ insetsController.setAppearanceLightStatusBars(!isDarkBackground);
532
715
  }
716
+ } catch (IllegalArgumentException e) {
717
+ Log.e(
718
+ "InAppBrowser",
719
+ "Invalid toolbar color: " + _options.getToolbarColor()
720
+ );
533
721
  }
534
- );
722
+ }
535
723
 
536
- ImageButton closeButton = _toolbar.findViewById(R.id.closeButton);
537
- closeButton.setOnClickListener(
724
+ ImageButton closeButtonView = _toolbar.findViewById(R.id.closeButton);
725
+ closeButtonView.setOnClickListener(
538
726
  new View.OnClickListener() {
539
727
  @Override
540
728
  public void onClick(View view) {
@@ -572,78 +760,292 @@ public class WebViewDialog extends Dialog {
572
760
  );
573
761
 
574
762
  if (_options.showArrow()) {
575
- closeButton.setImageResource(R.drawable.arrow_back_enabled);
763
+ closeButtonView.setImageResource(R.drawable.arrow_back_enabled);
576
764
  }
577
765
 
578
- if (_options.getShowReloadButton()) {
579
- View reloadButton = _toolbar.findViewById(R.id.reloadButton);
580
- reloadButton.setVisibility(View.VISIBLE);
581
- reloadButton.setOnClickListener(
766
+ // Handle reload button visibility
767
+ if (
768
+ _options.getShowReloadButton() &&
769
+ !TextUtils.equals(_options.getToolbarType(), "activity")
770
+ ) {
771
+ View reloadButtonView = _toolbar.findViewById(R.id.reloadButton);
772
+ reloadButtonView.setVisibility(View.VISIBLE);
773
+ reloadButtonView.setOnClickListener(
582
774
  new View.OnClickListener() {
583
775
  @Override
584
776
  public void onClick(View view) {
585
- _webView.reload();
777
+ if (_webView != null) {
778
+ // First stop any ongoing loading
779
+ _webView.stopLoading();
780
+
781
+ // Check if there's a URL to reload
782
+ if (_webView.getUrl() != null) {
783
+ // Reload the current page
784
+ _webView.reload();
785
+ Log.d("InAppBrowser", "Reloading page: " + _webView.getUrl());
786
+ } else if (_options.getUrl() != null) {
787
+ // If webView URL is null but we have an initial URL, load that
788
+ setUrl(_options.getUrl());
789
+ Log.d(
790
+ "InAppBrowser",
791
+ "Loading initial URL: " + _options.getUrl()
792
+ );
793
+ }
794
+ }
586
795
  }
587
796
  }
588
797
  );
798
+ } else {
799
+ View reloadButtonView = _toolbar.findViewById(R.id.reloadButton);
800
+ reloadButtonView.setVisibility(View.GONE);
589
801
  }
590
802
 
591
803
  if (TextUtils.equals(_options.getToolbarType(), "activity")) {
804
+ // Activity mode should ONLY have:
805
+ // 1. Close button
806
+ // 2. Share button (if shareSubject is provided)
807
+
808
+ // Hide all navigation buttons
592
809
  _toolbar.findViewById(R.id.forwardButton).setVisibility(View.GONE);
593
810
  _toolbar.findViewById(R.id.backButton).setVisibility(View.GONE);
811
+
812
+ // Hide buttonNearDone
594
813
  ImageButton buttonNearDoneView = _toolbar.findViewById(
595
814
  R.id.buttonNearDone
596
815
  );
597
816
  buttonNearDoneView.setVisibility(View.GONE);
598
- //TODO: Add share button functionality
817
+
818
+ // In activity mode, always make the share button visible by setting a default shareSubject if not provided
819
+ if (
820
+ _options.getShareSubject() == null ||
821
+ _options.getShareSubject().isEmpty()
822
+ ) {
823
+ _options.setShareSubject("Share");
824
+ Log.d("InAppBrowser", "Activity mode: Setting default shareSubject");
825
+ }
826
+ // Status bar color is already set at the top of this method, no need to set again
827
+
828
+ // Share button visibility is handled separately later
599
829
  } else if (TextUtils.equals(_options.getToolbarType(), "navigation")) {
600
830
  ImageButton buttonNearDoneView = _toolbar.findViewById(
601
831
  R.id.buttonNearDone
602
832
  );
603
833
  buttonNearDoneView.setVisibility(View.GONE);
604
- //TODO: Remove share button when implemented
834
+ // Status bar color is already set at the top of this method, no need to set again
605
835
  } else if (TextUtils.equals(_options.getToolbarType(), "blank")) {
606
836
  _toolbar.setVisibility(View.GONE);
837
+
838
+ // Also set window background color to match status bar for blank toolbar
839
+ View statusBarColorView = findViewById(R.id.status_bar_color_view);
840
+ if (
841
+ _options.getToolbarColor() != null &&
842
+ !_options.getToolbarColor().isEmpty()
843
+ ) {
844
+ try {
845
+ int toolbarColor = Color.parseColor(_options.getToolbarColor());
846
+ if (getWindow() != null) {
847
+ getWindow().getDecorView().setBackgroundColor(toolbarColor);
848
+ }
849
+ // Also set status bar color view background if available
850
+ if (statusBarColorView != null) {
851
+ statusBarColorView.setBackgroundColor(toolbarColor);
852
+ }
853
+ } catch (IllegalArgumentException e) {
854
+ // Fallback to system default if color parsing fails
855
+ boolean isDarkTheme = isDarkThemeEnabled();
856
+ int windowBackgroundColor = isDarkTheme ? Color.BLACK : Color.WHITE;
857
+ if (getWindow() != null) {
858
+ getWindow()
859
+ .getDecorView()
860
+ .setBackgroundColor(windowBackgroundColor);
861
+ }
862
+ // Also set status bar color view background if available
863
+ if (statusBarColorView != null) {
864
+ statusBarColorView.setBackgroundColor(windowBackgroundColor);
865
+ }
866
+ }
867
+ } else {
868
+ // Follow system dark mode
869
+ boolean isDarkTheme = isDarkThemeEnabled();
870
+ int windowBackgroundColor = isDarkTheme ? Color.BLACK : Color.WHITE;
871
+ if (getWindow() != null) {
872
+ getWindow().getDecorView().setBackgroundColor(windowBackgroundColor);
873
+ }
874
+ // Also set status bar color view background if available
875
+ if (statusBarColorView != null) {
876
+ statusBarColorView.setBackgroundColor(windowBackgroundColor);
877
+ }
878
+ }
607
879
  } else {
608
880
  _toolbar.findViewById(R.id.forwardButton).setVisibility(View.GONE);
609
881
  _toolbar.findViewById(R.id.backButton).setVisibility(View.GONE);
610
882
 
883
+ // Status bar color is already set at the top of this method, no need to set again
884
+
611
885
  Options.ButtonNearDone buttonNearDone = _options.getButtonNearDone();
612
886
  if (buttonNearDone != null) {
613
- AssetManager assetManager = _context.getAssets();
614
-
615
- // Open the SVG file from assets
616
- InputStream inputStream = null;
617
- try {
618
- ImageButton buttonNearDoneView = _toolbar.findViewById(
619
- R.id.buttonNearDone
620
- );
621
- buttonNearDoneView.setVisibility(View.VISIBLE);
622
-
623
- inputStream = assetManager.open(buttonNearDone.getIcon());
887
+ ImageButton buttonNearDoneView = _toolbar.findViewById(
888
+ R.id.buttonNearDone
889
+ );
890
+ buttonNearDoneView.setVisibility(View.VISIBLE);
624
891
 
625
- SVG svg = SVG.getFromInputStream(inputStream);
626
- Picture picture = svg.renderToPicture(
627
- buttonNearDone.getWidth(),
628
- buttonNearDone.getHeight()
629
- );
630
- PictureDrawable pictureDrawable = new PictureDrawable(picture);
892
+ // Handle different icon types
893
+ String iconType = buttonNearDone.getIconType();
894
+ if ("vector".equals(iconType)) {
895
+ // Use native Android vector drawable
896
+ try {
897
+ String iconName = buttonNearDone.getIcon();
898
+ // Convert name to Android resource ID (remove file extension if present)
899
+ if (iconName.endsWith(".xml")) {
900
+ iconName = iconName.substring(0, iconName.length() - 4);
901
+ }
631
902
 
632
- buttonNearDoneView.setImageDrawable(pictureDrawable);
633
- buttonNearDoneView.setOnClickListener(view ->
634
- _options.getCallbacks().buttonNearDoneClicked()
635
- );
636
- } catch (IOException | SVGParseException e) {
637
- throw new RuntimeException(e);
638
- } finally {
639
- if (inputStream != null) {
903
+ // Get resource ID
904
+ int resourceId = _context
905
+ .getResources()
906
+ .getIdentifier(iconName, "drawable", _context.getPackageName());
907
+
908
+ if (resourceId != 0) {
909
+ // Set the vector drawable
910
+ buttonNearDoneView.setImageResource(resourceId);
911
+ // Apply color filter
912
+ buttonNearDoneView.setColorFilter(iconColor);
913
+ Log.d(
914
+ "InAppBrowser",
915
+ "Successfully loaded vector drawable: " + iconName
916
+ );
917
+ } else {
918
+ Log.e(
919
+ "InAppBrowser",
920
+ "Vector drawable not found: " + iconName + ", using fallback"
921
+ );
922
+ // Fallback to a common system icon
923
+ buttonNearDoneView.setImageResource(
924
+ android.R.drawable.ic_menu_info_details
925
+ );
926
+ buttonNearDoneView.setColorFilter(iconColor);
927
+ }
928
+ } catch (Exception e) {
929
+ Log.e(
930
+ "InAppBrowser",
931
+ "Error loading vector drawable: " + e.getMessage()
932
+ );
933
+ // Fallback to a common system icon
934
+ buttonNearDoneView.setImageResource(
935
+ android.R.drawable.ic_menu_info_details
936
+ );
937
+ buttonNearDoneView.setColorFilter(iconColor);
938
+ }
939
+ } else if ("asset".equals(iconType)) {
940
+ // Handle SVG from assets
941
+ AssetManager assetManager = _context.getAssets();
942
+ InputStream inputStream = null;
943
+ try {
944
+ // Try to load from public folder first
945
+ String iconPath = "public/" + buttonNearDone.getIcon();
640
946
  try {
641
- inputStream.close();
947
+ inputStream = assetManager.open(iconPath);
642
948
  } catch (IOException e) {
643
- e.printStackTrace();
949
+ // If not found in public, try root assets
950
+ try {
951
+ inputStream = assetManager.open(buttonNearDone.getIcon());
952
+ } catch (IOException e2) {
953
+ Log.e(
954
+ "InAppBrowser",
955
+ "SVG file not found in assets: " + buttonNearDone.getIcon()
956
+ );
957
+ buttonNearDoneView.setVisibility(View.GONE);
958
+ return;
959
+ }
960
+ }
961
+
962
+ if (inputStream == null) {
963
+ Log.e(
964
+ "InAppBrowser",
965
+ "Failed to load SVG icon: " + buttonNearDone.getIcon()
966
+ );
967
+ buttonNearDoneView.setVisibility(View.GONE);
968
+ return;
969
+ }
970
+
971
+ // Parse and render SVG
972
+ SVG svg = SVG.getFromInputStream(inputStream);
973
+ if (svg == null) {
974
+ Log.e(
975
+ "InAppBrowser",
976
+ "Failed to parse SVG icon: " + buttonNearDone.getIcon()
977
+ );
978
+ buttonNearDoneView.setVisibility(View.GONE);
979
+ return;
980
+ }
981
+
982
+ // Get the dimensions from options or use SVG's size
983
+ float width = buttonNearDone.getWidth() > 0
984
+ ? buttonNearDone.getWidth()
985
+ : 24;
986
+ float height = buttonNearDone.getHeight() > 0
987
+ ? buttonNearDone.getHeight()
988
+ : 24;
989
+
990
+ // Get density for proper scaling
991
+ float density = _context.getResources().getDisplayMetrics().density;
992
+ int targetWidth = Math.round(width * density);
993
+ int targetHeight = Math.round(height * density);
994
+
995
+ // Set document size
996
+ svg.setDocumentWidth(targetWidth);
997
+ svg.setDocumentHeight(targetHeight);
998
+
999
+ // Create a bitmap and render SVG to it for better quality
1000
+ Bitmap bitmap = Bitmap.createBitmap(
1001
+ targetWidth,
1002
+ targetHeight,
1003
+ Bitmap.Config.ARGB_8888
1004
+ );
1005
+ Canvas canvas = new Canvas(bitmap);
1006
+ svg.renderToCanvas(canvas);
1007
+
1008
+ // Apply color filter to the bitmap
1009
+ Paint paint = new Paint();
1010
+ paint.setColorFilter(
1011
+ new PorterDuffColorFilter(iconColor, PorterDuff.Mode.SRC_IN)
1012
+ );
1013
+ Canvas colorFilterCanvas = new Canvas(bitmap);
1014
+ colorFilterCanvas.drawBitmap(bitmap, 0, 0, paint);
1015
+
1016
+ // Set the colored bitmap as image
1017
+ buttonNearDoneView.setImageBitmap(bitmap);
1018
+ buttonNearDoneView.setScaleType(ImageView.ScaleType.FIT_CENTER);
1019
+ buttonNearDoneView.setPadding(12, 12, 12, 12); // Standard button padding
1020
+ } catch (SVGParseException e) {
1021
+ Log.e(
1022
+ "InAppBrowser",
1023
+ "Error loading SVG icon: " + e.getMessage(),
1024
+ e
1025
+ );
1026
+ buttonNearDoneView.setVisibility(View.GONE);
1027
+ } finally {
1028
+ if (inputStream != null) {
1029
+ try {
1030
+ inputStream.close();
1031
+ } catch (IOException e) {
1032
+ Log.e(
1033
+ "InAppBrowser",
1034
+ "Error closing input stream: " + e.getMessage()
1035
+ );
1036
+ }
644
1037
  }
645
1038
  }
1039
+ } else {
1040
+ // Default fallback or unsupported type
1041
+ Log.e("InAppBrowser", "Unsupported icon type: " + iconType);
1042
+ buttonNearDoneView.setVisibility(View.GONE);
646
1043
  }
1044
+
1045
+ // Set the click listener
1046
+ buttonNearDoneView.setOnClickListener(view ->
1047
+ _options.getCallbacks().buttonNearDoneClicked()
1048
+ );
647
1049
  } else {
648
1050
  ImageButton buttonNearDoneView = _toolbar.findViewById(
649
1051
  R.id.buttonNearDone
@@ -651,6 +1053,94 @@ public class WebViewDialog extends Dialog {
651
1053
  buttonNearDoneView.setVisibility(View.GONE);
652
1054
  }
653
1055
  }
1056
+
1057
+ // Add share button functionality
1058
+ ImageButton shareButton = _toolbar.findViewById(R.id.shareButton);
1059
+ if (
1060
+ _options.getShareSubject() != null &&
1061
+ !_options.getShareSubject().isEmpty()
1062
+ ) {
1063
+ shareButton.setVisibility(View.VISIBLE);
1064
+ Log.d(
1065
+ "InAppBrowser",
1066
+ "Share button should be visible, shareSubject: " +
1067
+ _options.getShareSubject()
1068
+ );
1069
+
1070
+ // Apply the same color filter as other buttons to ensure visibility
1071
+ shareButton.setColorFilter(iconColor);
1072
+
1073
+ // The color filter is now applied in applyColorToAllButtons
1074
+ shareButton.setOnClickListener(view -> {
1075
+ JSObject shareDisclaimer = _options.getShareDisclaimer();
1076
+ if (shareDisclaimer != null) {
1077
+ new AlertDialog.Builder(_context)
1078
+ .setTitle(shareDisclaimer.getString("title", "Title"))
1079
+ .setMessage(shareDisclaimer.getString("message", "Message"))
1080
+ .setPositiveButton(
1081
+ shareDisclaimer.getString("confirmBtn", "Confirm"),
1082
+ (dialog, which) -> {
1083
+ _options.getCallbacks().confirmBtnClicked();
1084
+ shareUrl();
1085
+ }
1086
+ )
1087
+ .setNegativeButton(
1088
+ shareDisclaimer.getString("cancelBtn", "Cancel"),
1089
+ null
1090
+ )
1091
+ .show();
1092
+ } else {
1093
+ shareUrl();
1094
+ }
1095
+ });
1096
+ } else {
1097
+ shareButton.setVisibility(View.GONE);
1098
+ }
1099
+
1100
+ // Also color the title text
1101
+ TextView titleText = _toolbar.findViewById(R.id.titleText);
1102
+ if (titleText != null) {
1103
+ titleText.setTextColor(iconColor);
1104
+
1105
+ // Set the title text
1106
+ if (!TextUtils.isEmpty(_options.getTitle())) {
1107
+ this.setTitle(_options.getTitle());
1108
+ } else {
1109
+ try {
1110
+ URI uri = new URI(_options.getUrl());
1111
+ this.setTitle(uri.getHost());
1112
+ } catch (URISyntaxException e) {
1113
+ this.setTitle(_options.getTitle());
1114
+ }
1115
+ }
1116
+ }
1117
+ }
1118
+
1119
+ /**
1120
+ * Applies background and tint colors to all buttons in the toolbar
1121
+ */
1122
+ private void applyColorToAllButtons(int backgroundColor, int iconColor) {
1123
+ // Get all buttons
1124
+ ImageButton backButton = _toolbar.findViewById(R.id.backButton);
1125
+ ImageButton forwardButton = _toolbar.findViewById(R.id.forwardButton);
1126
+ ImageButton closeButton = _toolbar.findViewById(R.id.closeButton);
1127
+ ImageButton reloadButton = _toolbar.findViewById(R.id.reloadButton);
1128
+ ImageButton shareButton = _toolbar.findViewById(R.id.shareButton);
1129
+ ImageButton buttonNearDoneView = _toolbar.findViewById(R.id.buttonNearDone);
1130
+
1131
+ // Set button backgrounds
1132
+ backButton.setBackgroundColor(backgroundColor);
1133
+ forwardButton.setBackgroundColor(backgroundColor);
1134
+ closeButton.setBackgroundColor(backgroundColor);
1135
+ reloadButton.setBackgroundColor(backgroundColor);
1136
+
1137
+ // Apply tint colors to buttons
1138
+ backButton.setColorFilter(iconColor);
1139
+ forwardButton.setColorFilter(iconColor);
1140
+ closeButton.setColorFilter(iconColor);
1141
+ reloadButton.setColorFilter(iconColor);
1142
+ shareButton.setColorFilter(iconColor);
1143
+ buttonNearDoneView.setColorFilter(iconColor);
654
1144
  }
655
1145
 
656
1146
  public void handleProxyResultError(String result, String id) {
@@ -840,7 +1330,7 @@ public class WebViewDialog extends Dialog {
840
1330
  );
841
1331
  }
842
1332
  String s = String.format(
843
- "try {function getHeaders() {const h = {}; %s return h}; window.InAppBrowserProxyRequest(new Request(atob('%s'), {headers: getHeaders(), method: '%s'})).then(async (res) => Capacitor.Plugins.InAppBrowser.lsuakdchgbbaHandleProxiedRequest({ok: true, result: (!!res ? {headers: Object.fromEntries(res.headers.entries()), code: res.status, body: (await res.text())} : null), id: '%s'})).catch((e) => Capacitor.Plugins.InAppBrowser.lsuakdchgbbaHandleProxiedRequest({ok: false, result: e.toString(), id: '%s'}))} catch (e) {Capacitor.Plugins.InAppBrowser.lsuakdchgbbaHandleProxiedRequest({ok: false, result: e.toString(), id: '%s'})}",
1333
+ "try {function getHeaders() {const h = {}; %s return h}; window.InAppBrowserProxyRequest(new Request(atob('%s'), {headers: getHeaders(), method: '%s'})).then(async (res) => Capacitor.Plugins.InAppBrowser.lsuakdchgbbaHandleProxiedRequest({ok: true, result: (!!res ? {headers: Object.fromEntries(res.headers.entries()), code: res.status, body: (await res.text())} : null), id: '%s'})).catch((e) => Capacitor.Plugins.InAppBrowser.lsuakdchgbbaHandleProxiedRequest({ok: false, result: e.toString(), id: '%s'})} catch (e) {Capacitor.Plugins.InAppBrowser.lsuakdchgbbaHandleProxiedRequest({ok: false, result: e.toString(), id: '%s'})}",
844
1334
  headers,
845
1335
  toBase64(request.getUrl().toString()),
846
1336
  request.getMethod(),
@@ -1023,18 +1513,36 @@ public class WebViewDialog extends Dialog {
1023
1513
  if (_webView.canGoBack()) {
1024
1514
  backButton.setImageResource(R.drawable.arrow_back_enabled);
1025
1515
  backButton.setEnabled(true);
1516
+ backButton.setColorFilter(iconColor);
1026
1517
  } else {
1027
1518
  backButton.setImageResource(R.drawable.arrow_back_disabled);
1028
1519
  backButton.setEnabled(false);
1520
+ backButton.setColorFilter(
1521
+ Color.argb(
1522
+ 128,
1523
+ Color.red(iconColor),
1524
+ Color.green(iconColor),
1525
+ Color.blue(iconColor)
1526
+ )
1527
+ );
1029
1528
  }
1030
1529
 
1031
1530
  ImageButton forwardButton = _toolbar.findViewById(R.id.forwardButton);
1032
1531
  if (_webView.canGoForward()) {
1033
1532
  forwardButton.setImageResource(R.drawable.arrow_forward_enabled);
1034
1533
  forwardButton.setEnabled(true);
1534
+ forwardButton.setColorFilter(iconColor);
1035
1535
  } else {
1036
1536
  forwardButton.setImageResource(R.drawable.arrow_forward_disabled);
1037
1537
  forwardButton.setEnabled(false);
1538
+ forwardButton.setColorFilter(
1539
+ Color.argb(
1540
+ 128,
1541
+ Color.red(iconColor),
1542
+ Color.green(iconColor),
1543
+ Color.blue(iconColor)
1544
+ )
1545
+ );
1038
1546
  }
1039
1547
 
1040
1548
  _options.getCallbacks().pageLoaded();
@@ -1180,4 +1688,51 @@ public class WebViewDialog extends Dialog {
1180
1688
  proxiedRequestsHashmap.remove(key);
1181
1689
  }
1182
1690
  }
1691
+
1692
+ private void shareUrl() {
1693
+ Intent shareIntent = new Intent(Intent.ACTION_SEND);
1694
+ shareIntent.setType("text/plain");
1695
+ shareIntent.putExtra(Intent.EXTRA_SUBJECT, _options.getShareSubject());
1696
+ shareIntent.putExtra(Intent.EXTRA_TEXT, _options.getUrl());
1697
+ _context.startActivity(Intent.createChooser(shareIntent, "Share"));
1698
+ }
1699
+
1700
+ private boolean isDarkColor(int color) {
1701
+ int red = Color.red(color);
1702
+ int green = Color.green(color);
1703
+ int blue = Color.blue(color);
1704
+ double luminance = (0.299 * red + 0.587 * green + 0.114 * blue) / 255.0;
1705
+ return luminance < 0.5;
1706
+ }
1707
+
1708
+ private boolean isDarkThemeEnabled() {
1709
+ // This method checks if dark theme is currently enabled without using Configuration class
1710
+ try {
1711
+ // On Android 10+, check via resources for night mode
1712
+ Resources.Theme theme = _context.getTheme();
1713
+ TypedValue typedValue = new TypedValue();
1714
+
1715
+ if (
1716
+ theme.resolveAttribute(android.R.attr.isLightTheme, typedValue, true)
1717
+ ) {
1718
+ // isLightTheme exists - returns true if light, false if dark
1719
+ return typedValue.data != 1;
1720
+ }
1721
+
1722
+ // Fallback method - check background color of window
1723
+ if (
1724
+ theme.resolveAttribute(
1725
+ android.R.attr.windowBackground,
1726
+ typedValue,
1727
+ true
1728
+ )
1729
+ ) {
1730
+ int backgroundColor = typedValue.data;
1731
+ return isDarkColor(backgroundColor);
1732
+ }
1733
+ } catch (Exception e) {
1734
+ // Ignore and fallback to light theme
1735
+ }
1736
+ return false;
1737
+ }
1183
1738
  }