@capgo/inappbrowser 6.11.1 → 6.13.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 +50 -42
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/InAppBrowserPlugin.java +161 -41
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/Options.java +83 -22
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewCallbacks.java +2 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java +749 -96
- package/android/src/main/res/drawable/ic_share.xml +10 -0
- package/android/src/main/res/layout/activity_browser.xml +8 -0
- package/android/src/main/res/layout/content_browser.xml +10 -1
- package/android/src/main/res/layout/tool_bar.xml +19 -7
- package/android/src/main/res/values/strings.xml +2 -0
- package/android/src/main/res/values/themes.xml +27 -0
- package/dist/docs.json +211 -37
- package/dist/esm/definitions.d.ts +244 -31
- package/dist/esm/definitions.js +12 -1
- package/dist/esm/definitions.js.map +1 -1
- package/dist/plugin.cjs.js +12 -1
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +12 -1
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/InAppBrowserPlugin.swift +344 -44
- package/ios/Plugin/WKWebViewController.swift +499 -45
- package/package.json +7 -8
- package/ios/Plugin/Assets.xcassets/Back.imageset/Back.png +0 -0
- package/ios/Plugin/Assets.xcassets/Back.imageset/Back@2x.png +0 -0
- package/ios/Plugin/Assets.xcassets/Back.imageset/Back@3x.png +0 -0
- package/ios/Plugin/Assets.xcassets/Back.imageset/Contents.json +0 -26
- package/ios/Plugin/Assets.xcassets/Forward.imageset/Contents.json +0 -26
- package/ios/Plugin/Assets.xcassets/Forward.imageset/Forward.png +0 -0
- package/ios/Plugin/Assets.xcassets/Forward.imageset/Forward@2x.png +0 -0
- package/ios/Plugin/Assets.xcassets/Forward.imageset/Forward@3x.png +0 -0
|
@@ -9,15 +9,19 @@ 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;
|
|
14
|
-
import android.graphics.
|
|
15
|
-
import android.graphics.
|
|
16
|
+
import android.graphics.Paint;
|
|
17
|
+
import android.graphics.PorterDuff;
|
|
18
|
+
import android.graphics.PorterDuffColorFilter;
|
|
16
19
|
import android.net.Uri;
|
|
17
20
|
import android.net.http.SslError;
|
|
18
21
|
import android.text.TextUtils;
|
|
19
22
|
import android.util.Base64;
|
|
20
23
|
import android.util.Log;
|
|
24
|
+
import android.util.TypedValue;
|
|
21
25
|
import android.view.View;
|
|
22
26
|
import android.view.Window;
|
|
23
27
|
import android.view.WindowManager;
|
|
@@ -33,6 +37,7 @@ import android.webkit.WebResourceResponse;
|
|
|
33
37
|
import android.webkit.WebView;
|
|
34
38
|
import android.webkit.WebViewClient;
|
|
35
39
|
import android.widget.ImageButton;
|
|
40
|
+
import android.widget.ImageView;
|
|
36
41
|
import android.widget.TextView;
|
|
37
42
|
import android.widget.Toast;
|
|
38
43
|
import android.widget.Toolbar;
|
|
@@ -84,11 +89,13 @@ public class WebViewDialog extends Dialog {
|
|
|
84
89
|
private final Context _context;
|
|
85
90
|
public Activity activity;
|
|
86
91
|
private boolean isInitialized = false;
|
|
92
|
+
private boolean datePickerInjected = false; // Track if we've injected date picker fixes
|
|
87
93
|
private final WebView capacitorWebView;
|
|
88
94
|
private final Map<String, ProxiedRequest> proxiedRequestsHashmap =
|
|
89
95
|
new HashMap<>();
|
|
90
96
|
private final ExecutorService executorService =
|
|
91
97
|
Executors.newCachedThreadPool();
|
|
98
|
+
private int iconColor = Color.BLACK; // Default icon color
|
|
92
99
|
|
|
93
100
|
Semaphore preShowSemaphore = null;
|
|
94
101
|
String preShowError = null;
|
|
@@ -113,7 +120,11 @@ public class WebViewDialog extends Dialog {
|
|
|
113
120
|
PermissionHandler permissionHandler,
|
|
114
121
|
WebView capacitorWebView
|
|
115
122
|
) {
|
|
116
|
-
|
|
123
|
+
// Use Material theme only if materialPicker is enabled
|
|
124
|
+
super(
|
|
125
|
+
context,
|
|
126
|
+
options.getMaterialPicker() ? R.style.InAppBrowserMaterialTheme : theme
|
|
127
|
+
);
|
|
117
128
|
this._options = options;
|
|
118
129
|
this._context = context;
|
|
119
130
|
this.permissionHandler = permissionHandler;
|
|
@@ -200,14 +211,78 @@ public class WebViewDialog extends Dialog {
|
|
|
200
211
|
setContentView(R.layout.activity_browser);
|
|
201
212
|
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
|
202
213
|
|
|
214
|
+
// Make status bar transparent
|
|
215
|
+
if (getWindow() != null) {
|
|
216
|
+
getWindow().setStatusBarColor(Color.TRANSPARENT);
|
|
217
|
+
|
|
218
|
+
// Add FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag to the window
|
|
219
|
+
getWindow()
|
|
220
|
+
.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
|
221
|
+
|
|
222
|
+
// On Android 30+ clear FLAG_TRANSLUCENT_STATUS flag
|
|
223
|
+
getWindow()
|
|
224
|
+
.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
|
225
|
+
}
|
|
226
|
+
|
|
203
227
|
WindowInsetsControllerCompat insetsController =
|
|
204
|
-
new WindowInsetsControllerCompat(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
228
|
+
new WindowInsetsControllerCompat(
|
|
229
|
+
getWindow(),
|
|
230
|
+
getWindow() != null ? getWindow().getDecorView() : null
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
if (getWindow() != null) {
|
|
234
|
+
getWindow()
|
|
235
|
+
.getDecorView()
|
|
236
|
+
.post(() -> {
|
|
237
|
+
// Get status bar height
|
|
238
|
+
int statusBarHeight = 0;
|
|
239
|
+
int resourceId = getContext()
|
|
240
|
+
.getResources()
|
|
241
|
+
.getIdentifier("status_bar_height", "dimen", "android");
|
|
242
|
+
if (resourceId > 0) {
|
|
243
|
+
statusBarHeight = getContext()
|
|
244
|
+
.getResources()
|
|
245
|
+
.getDimensionPixelSize(resourceId);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Find the status bar color view
|
|
249
|
+
View statusBarColorView = findViewById(R.id.status_bar_color_view);
|
|
250
|
+
|
|
251
|
+
// Set the height of the status bar color view
|
|
252
|
+
if (statusBarColorView != null) {
|
|
253
|
+
statusBarColorView.getLayoutParams().height = statusBarHeight;
|
|
254
|
+
statusBarColorView.requestLayout();
|
|
255
|
+
|
|
256
|
+
// Set color based on toolbar color or dark mode
|
|
257
|
+
if (
|
|
258
|
+
_options.getToolbarColor() != null &&
|
|
259
|
+
!_options.getToolbarColor().isEmpty()
|
|
260
|
+
) {
|
|
261
|
+
try {
|
|
262
|
+
// Use explicitly provided toolbar color for status bar
|
|
263
|
+
int toolbarColor = Color.parseColor(_options.getToolbarColor());
|
|
264
|
+
statusBarColorView.setBackgroundColor(toolbarColor);
|
|
265
|
+
|
|
266
|
+
// Set status bar text to white or black based on background
|
|
267
|
+
boolean isDarkBackground = isDarkColor(toolbarColor);
|
|
268
|
+
insetsController.setAppearanceLightStatusBars(
|
|
269
|
+
!isDarkBackground
|
|
270
|
+
);
|
|
271
|
+
} catch (IllegalArgumentException e) {
|
|
272
|
+
// Fallback to default black if color parsing fails
|
|
273
|
+
statusBarColorView.setBackgroundColor(Color.BLACK);
|
|
274
|
+
insetsController.setAppearanceLightStatusBars(false);
|
|
275
|
+
}
|
|
276
|
+
} else {
|
|
277
|
+
// Follow system dark mode if no toolbar color provided
|
|
278
|
+
boolean isDarkTheme = isDarkThemeEnabled();
|
|
279
|
+
int statusBarColor = isDarkTheme ? Color.BLACK : Color.WHITE;
|
|
280
|
+
statusBarColorView.setBackgroundColor(statusBarColor);
|
|
281
|
+
insetsController.setAppearanceLightStatusBars(!isDarkTheme);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
}
|
|
211
286
|
|
|
212
287
|
getWindow()
|
|
213
288
|
.setLayout(
|
|
@@ -235,6 +310,52 @@ public class WebViewDialog extends Dialog {
|
|
|
235
310
|
_webView.getSettings().setAllowUniversalAccessFromFileURLs(true);
|
|
236
311
|
_webView.getSettings().setMediaPlaybackRequiresUserGesture(false);
|
|
237
312
|
|
|
313
|
+
// Set text zoom if specified in options
|
|
314
|
+
if (_options.getTextZoom() > 0) {
|
|
315
|
+
_webView.getSettings().setTextZoom(_options.getTextZoom());
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Fix for Android 15 (API 35) navigation bar overlap issue
|
|
319
|
+
if (android.os.Build.VERSION.SDK_INT >= 35) {
|
|
320
|
+
// Get the navigation bar space view
|
|
321
|
+
View navigationBarSpace = findViewById(R.id.navigation_bar_space);
|
|
322
|
+
|
|
323
|
+
if (navigationBarSpace != null) {
|
|
324
|
+
// Calculate navigation bar height
|
|
325
|
+
int navigationBarHeight = 0;
|
|
326
|
+
int navBarId = getContext()
|
|
327
|
+
.getResources()
|
|
328
|
+
.getIdentifier("navigation_bar_height", "dimen", "android");
|
|
329
|
+
|
|
330
|
+
if (navBarId > 0) {
|
|
331
|
+
navigationBarHeight = getContext()
|
|
332
|
+
.getResources()
|
|
333
|
+
.getDimensionPixelSize(navBarId);
|
|
334
|
+
|
|
335
|
+
// Set the height of the navigation bar space
|
|
336
|
+
navigationBarSpace.getLayoutParams().height = navigationBarHeight;
|
|
337
|
+
navigationBarSpace.requestLayout();
|
|
338
|
+
|
|
339
|
+
// Set the background color to match the toolbar or system theme
|
|
340
|
+
int navBarColor;
|
|
341
|
+
if (_options.getToolbarColor() != null && !_options.getToolbarColor().isEmpty()) {
|
|
342
|
+
try {
|
|
343
|
+
navBarColor = Color.parseColor(_options.getToolbarColor());
|
|
344
|
+
} catch (IllegalArgumentException e) {
|
|
345
|
+
// Default to system coloring if parsing fails
|
|
346
|
+
boolean isDarkTheme = isDarkThemeEnabled();
|
|
347
|
+
navBarColor = isDarkTheme ? Color.BLACK : Color.WHITE;
|
|
348
|
+
}
|
|
349
|
+
} else {
|
|
350
|
+
// Follow system theme
|
|
351
|
+
boolean isDarkTheme = isDarkThemeEnabled();
|
|
352
|
+
navBarColor = isDarkTheme ? Color.BLACK : Color.WHITE;
|
|
353
|
+
}
|
|
354
|
+
navigationBarSpace.setBackgroundColor(navBarColor);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
238
359
|
_webView.setWebViewClient(new WebViewClient());
|
|
239
360
|
|
|
240
361
|
_webView.setWebChromeClient(
|
|
@@ -246,9 +367,25 @@ public class WebViewDialog extends Dialog {
|
|
|
246
367
|
ValueCallback<Uri[]> filePathCallback,
|
|
247
368
|
FileChooserParams fileChooserParams
|
|
248
369
|
) {
|
|
370
|
+
// Get the accept type safely
|
|
371
|
+
String acceptType = "*/*"; // Default to all file types
|
|
372
|
+
if (
|
|
373
|
+
fileChooserParams.getAcceptTypes() != null &&
|
|
374
|
+
fileChooserParams.getAcceptTypes().length > 0 &&
|
|
375
|
+
!TextUtils.isEmpty(fileChooserParams.getAcceptTypes()[0])
|
|
376
|
+
) {
|
|
377
|
+
acceptType = fileChooserParams.getAcceptTypes()[0];
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Check if the file chooser is already open
|
|
381
|
+
if (mFilePathCallback != null) {
|
|
382
|
+
mFilePathCallback.onReceiveValue(null);
|
|
383
|
+
mFilePathCallback = null;
|
|
384
|
+
}
|
|
385
|
+
|
|
249
386
|
openFileChooser(
|
|
250
387
|
filePathCallback,
|
|
251
|
-
|
|
388
|
+
acceptType,
|
|
252
389
|
fileChooserParams.getMode() == FileChooserParams.MODE_OPEN_MULTIPLE
|
|
253
390
|
);
|
|
254
391
|
return true;
|
|
@@ -302,6 +439,22 @@ public class WebViewDialog extends Dialog {
|
|
|
302
439
|
currentPermissionRequest = null;
|
|
303
440
|
}
|
|
304
441
|
}
|
|
442
|
+
|
|
443
|
+
// This method will be called at page load, a good place to inject customizations
|
|
444
|
+
@Override
|
|
445
|
+
public void onProgressChanged(WebView view, int newProgress) {
|
|
446
|
+
super.onProgressChanged(view, newProgress);
|
|
447
|
+
|
|
448
|
+
// When the page is almost loaded, inject our date picker customization
|
|
449
|
+
// Only if materialPicker option is enabled
|
|
450
|
+
if (
|
|
451
|
+
newProgress > 75 &&
|
|
452
|
+
!datePickerInjected &&
|
|
453
|
+
_options.getMaterialPicker()
|
|
454
|
+
) {
|
|
455
|
+
injectDatePickerFixes();
|
|
456
|
+
}
|
|
457
|
+
}
|
|
305
458
|
}
|
|
306
459
|
);
|
|
307
460
|
|
|
@@ -433,16 +586,100 @@ public class WebViewDialog extends Dialog {
|
|
|
433
586
|
mFilePathCallback = filePathCallback;
|
|
434
587
|
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
|
435
588
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
|
436
|
-
|
|
589
|
+
|
|
590
|
+
// Fix MIME type handling
|
|
591
|
+
if (
|
|
592
|
+
acceptType == null ||
|
|
593
|
+
acceptType.isEmpty() ||
|
|
594
|
+
acceptType.equals("undefined")
|
|
595
|
+
) {
|
|
596
|
+
acceptType = "*/*";
|
|
597
|
+
} else {
|
|
598
|
+
// Handle common web input accept types
|
|
599
|
+
if (acceptType.equals("image/*")) {
|
|
600
|
+
// Keep as is - image/*
|
|
601
|
+
} else if (acceptType.contains("image/")) {
|
|
602
|
+
// Specific image type requested but keep it general for better compatibility
|
|
603
|
+
acceptType = "image/*";
|
|
604
|
+
} else if (
|
|
605
|
+
acceptType.equals("audio/*") || acceptType.contains("audio/")
|
|
606
|
+
) {
|
|
607
|
+
acceptType = "audio/*";
|
|
608
|
+
} else if (
|
|
609
|
+
acceptType.equals("video/*") || acceptType.contains("video/")
|
|
610
|
+
) {
|
|
611
|
+
acceptType = "video/*";
|
|
612
|
+
} else if (acceptType.startsWith(".") || acceptType.contains(",")) {
|
|
613
|
+
// Handle file extensions like ".pdf, .docx" by using a general mime type
|
|
614
|
+
if (acceptType.contains(".pdf")) {
|
|
615
|
+
acceptType = "application/pdf";
|
|
616
|
+
} else if (
|
|
617
|
+
acceptType.contains(".doc") || acceptType.contains(".docx")
|
|
618
|
+
) {
|
|
619
|
+
acceptType = "application/msword";
|
|
620
|
+
} else if (
|
|
621
|
+
acceptType.contains(".xls") || acceptType.contains(".xlsx")
|
|
622
|
+
) {
|
|
623
|
+
acceptType = "application/vnd.ms-excel";
|
|
624
|
+
} else if (
|
|
625
|
+
acceptType.contains(".txt") || acceptType.contains(".text")
|
|
626
|
+
) {
|
|
627
|
+
acceptType = "text/plain";
|
|
628
|
+
} else {
|
|
629
|
+
// Default for extension lists
|
|
630
|
+
acceptType = "*/*";
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
Log.d("InAppBrowser", "File picker using MIME type: " + acceptType);
|
|
636
|
+
intent.setType(acceptType);
|
|
437
637
|
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, isMultiple);
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
638
|
+
|
|
639
|
+
try {
|
|
640
|
+
activity.startActivityForResult(
|
|
641
|
+
Intent.createChooser(intent, "Select File"),
|
|
642
|
+
FILE_CHOOSER_REQUEST_CODE
|
|
643
|
+
);
|
|
644
|
+
} catch (ActivityNotFoundException e) {
|
|
645
|
+
// If no app can handle the specific MIME type, try with a more generic one
|
|
646
|
+
Log.e(
|
|
647
|
+
"InAppBrowser",
|
|
648
|
+
"No app available for type: " + acceptType + ", trying with */*"
|
|
649
|
+
);
|
|
650
|
+
intent.setType("*/*");
|
|
651
|
+
try {
|
|
652
|
+
activity.startActivityForResult(
|
|
653
|
+
Intent.createChooser(intent, "Select File"),
|
|
654
|
+
FILE_CHOOSER_REQUEST_CODE
|
|
655
|
+
);
|
|
656
|
+
} catch (ActivityNotFoundException ex) {
|
|
657
|
+
// If still failing, report error
|
|
658
|
+
Log.e("InAppBrowser", "No app can handle file picker", ex);
|
|
659
|
+
if (mFilePathCallback != null) {
|
|
660
|
+
mFilePathCallback.onReceiveValue(null);
|
|
661
|
+
mFilePathCallback = null;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
442
665
|
}
|
|
443
666
|
|
|
444
667
|
public void reload() {
|
|
445
|
-
_webView
|
|
668
|
+
if (_webView != null) {
|
|
669
|
+
// First stop any ongoing loading
|
|
670
|
+
_webView.stopLoading();
|
|
671
|
+
|
|
672
|
+
// Check if there's a URL to reload
|
|
673
|
+
if (_webView.getUrl() != null) {
|
|
674
|
+
// Reload the current page
|
|
675
|
+
_webView.reload();
|
|
676
|
+
Log.d("InAppBrowser", "Reloading page: " + _webView.getUrl());
|
|
677
|
+
} else if (_options != null && _options.getUrl() != null) {
|
|
678
|
+
// If webView URL is null but we have an initial URL, load that
|
|
679
|
+
setUrl(_options.getUrl());
|
|
680
|
+
Log.d("InAppBrowser", "Loading initial URL: " + _options.getUrl());
|
|
681
|
+
}
|
|
682
|
+
}
|
|
446
683
|
}
|
|
447
684
|
|
|
448
685
|
public void destroy() {
|
|
@@ -485,56 +722,72 @@ public class WebViewDialog extends Dialog {
|
|
|
485
722
|
}
|
|
486
723
|
|
|
487
724
|
private void setupToolbar() {
|
|
488
|
-
_toolbar =
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
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 {
|
|
725
|
+
_toolbar = findViewById(R.id.tool_bar);
|
|
726
|
+
|
|
727
|
+
// Apply toolbar color early, for ALL toolbar types, before any view configuration
|
|
728
|
+
if (
|
|
729
|
+
_options.getToolbarColor() != null &&
|
|
730
|
+
!_options.getToolbarColor().isEmpty()
|
|
731
|
+
) {
|
|
504
732
|
try {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
} catch (URISyntaxException e) {
|
|
508
|
-
this.setTitle(_options.getTitle());
|
|
509
|
-
}
|
|
510
|
-
}
|
|
733
|
+
int toolbarColor = Color.parseColor(_options.getToolbarColor());
|
|
734
|
+
_toolbar.setBackgroundColor(toolbarColor);
|
|
511
735
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
736
|
+
// Get toolbar title and ensure it gets the right color
|
|
737
|
+
TextView titleText = _toolbar.findViewById(R.id.titleText);
|
|
738
|
+
|
|
739
|
+
// Determine icon and text color
|
|
740
|
+
int iconColor;
|
|
741
|
+
if (
|
|
742
|
+
_options.getToolbarTextColor() != null &&
|
|
743
|
+
!_options.getToolbarTextColor().isEmpty()
|
|
744
|
+
) {
|
|
745
|
+
try {
|
|
746
|
+
iconColor = Color.parseColor(_options.getToolbarTextColor());
|
|
747
|
+
} catch (IllegalArgumentException e) {
|
|
748
|
+
// Fallback to automatic detection if parsing fails
|
|
749
|
+
boolean isDarkBackground = isDarkColor(toolbarColor);
|
|
750
|
+
iconColor = isDarkBackground ? Color.WHITE : Color.BLACK;
|
|
519
751
|
}
|
|
752
|
+
} else {
|
|
753
|
+
// No explicit toolbarTextColor, use automatic detection based on background
|
|
754
|
+
boolean isDarkBackground = isDarkColor(toolbarColor);
|
|
755
|
+
iconColor = isDarkBackground ? Color.WHITE : Color.BLACK;
|
|
520
756
|
}
|
|
521
|
-
}
|
|
522
|
-
);
|
|
523
757
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
758
|
+
// Store for later use with navigation buttons
|
|
759
|
+
this.iconColor = iconColor;
|
|
760
|
+
|
|
761
|
+
// Set title text color directly
|
|
762
|
+
titleText.setTextColor(iconColor);
|
|
763
|
+
|
|
764
|
+
// Apply colors to all buttons
|
|
765
|
+
applyColorToAllButtons(toolbarColor, iconColor);
|
|
766
|
+
|
|
767
|
+
// Also ensure status bar gets the color
|
|
768
|
+
if (getWindow() != null) {
|
|
769
|
+
// Set status bar color
|
|
770
|
+
getWindow().setStatusBarColor(toolbarColor);
|
|
771
|
+
|
|
772
|
+
// Determine proper status bar text color (light or dark icons)
|
|
773
|
+
boolean isDarkBackground = isDarkColor(toolbarColor);
|
|
774
|
+
WindowInsetsControllerCompat insetsController =
|
|
775
|
+
new WindowInsetsControllerCompat(
|
|
776
|
+
getWindow(),
|
|
777
|
+
getWindow().getDecorView()
|
|
778
|
+
);
|
|
779
|
+
insetsController.setAppearanceLightStatusBars(!isDarkBackground);
|
|
532
780
|
}
|
|
781
|
+
} catch (IllegalArgumentException e) {
|
|
782
|
+
Log.e(
|
|
783
|
+
"InAppBrowser",
|
|
784
|
+
"Invalid toolbar color: " + _options.getToolbarColor()
|
|
785
|
+
);
|
|
533
786
|
}
|
|
534
|
-
|
|
787
|
+
}
|
|
535
788
|
|
|
536
|
-
ImageButton
|
|
537
|
-
|
|
789
|
+
ImageButton closeButtonView = _toolbar.findViewById(R.id.closeButton);
|
|
790
|
+
closeButtonView.setOnClickListener(
|
|
538
791
|
new View.OnClickListener() {
|
|
539
792
|
@Override
|
|
540
793
|
public void onClick(View view) {
|
|
@@ -548,7 +801,9 @@ public class WebViewDialog extends Dialog {
|
|
|
548
801
|
new OnClickListener() {
|
|
549
802
|
public void onClick(DialogInterface dialog, int which) {
|
|
550
803
|
// Close button clicked, do something
|
|
551
|
-
String currentUrl = _webView != null
|
|
804
|
+
String currentUrl = _webView != null
|
|
805
|
+
? _webView.getUrl()
|
|
806
|
+
: "";
|
|
552
807
|
dismiss();
|
|
553
808
|
if (_options != null && _options.getCallbacks() != null) {
|
|
554
809
|
_options.getCallbacks().closeEvent(currentUrl);
|
|
@@ -570,78 +825,292 @@ public class WebViewDialog extends Dialog {
|
|
|
570
825
|
);
|
|
571
826
|
|
|
572
827
|
if (_options.showArrow()) {
|
|
573
|
-
|
|
828
|
+
closeButtonView.setImageResource(R.drawable.arrow_back_enabled);
|
|
574
829
|
}
|
|
575
830
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
831
|
+
// Handle reload button visibility
|
|
832
|
+
if (
|
|
833
|
+
_options.getShowReloadButton() &&
|
|
834
|
+
!TextUtils.equals(_options.getToolbarType(), "activity")
|
|
835
|
+
) {
|
|
836
|
+
View reloadButtonView = _toolbar.findViewById(R.id.reloadButton);
|
|
837
|
+
reloadButtonView.setVisibility(View.VISIBLE);
|
|
838
|
+
reloadButtonView.setOnClickListener(
|
|
580
839
|
new View.OnClickListener() {
|
|
581
840
|
@Override
|
|
582
841
|
public void onClick(View view) {
|
|
583
|
-
_webView
|
|
842
|
+
if (_webView != null) {
|
|
843
|
+
// First stop any ongoing loading
|
|
844
|
+
_webView.stopLoading();
|
|
845
|
+
|
|
846
|
+
// Check if there's a URL to reload
|
|
847
|
+
if (_webView.getUrl() != null) {
|
|
848
|
+
// Reload the current page
|
|
849
|
+
_webView.reload();
|
|
850
|
+
Log.d("InAppBrowser", "Reloading page: " + _webView.getUrl());
|
|
851
|
+
} else if (_options.getUrl() != null) {
|
|
852
|
+
// If webView URL is null but we have an initial URL, load that
|
|
853
|
+
setUrl(_options.getUrl());
|
|
854
|
+
Log.d(
|
|
855
|
+
"InAppBrowser",
|
|
856
|
+
"Loading initial URL: " + _options.getUrl()
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
584
860
|
}
|
|
585
861
|
}
|
|
586
862
|
);
|
|
863
|
+
} else {
|
|
864
|
+
View reloadButtonView = _toolbar.findViewById(R.id.reloadButton);
|
|
865
|
+
reloadButtonView.setVisibility(View.GONE);
|
|
587
866
|
}
|
|
588
867
|
|
|
589
868
|
if (TextUtils.equals(_options.getToolbarType(), "activity")) {
|
|
869
|
+
// Activity mode should ONLY have:
|
|
870
|
+
// 1. Close button
|
|
871
|
+
// 2. Share button (if shareSubject is provided)
|
|
872
|
+
|
|
873
|
+
// Hide all navigation buttons
|
|
590
874
|
_toolbar.findViewById(R.id.forwardButton).setVisibility(View.GONE);
|
|
591
875
|
_toolbar.findViewById(R.id.backButton).setVisibility(View.GONE);
|
|
876
|
+
|
|
877
|
+
// Hide buttonNearDone
|
|
592
878
|
ImageButton buttonNearDoneView = _toolbar.findViewById(
|
|
593
879
|
R.id.buttonNearDone
|
|
594
880
|
);
|
|
595
881
|
buttonNearDoneView.setVisibility(View.GONE);
|
|
596
|
-
|
|
882
|
+
|
|
883
|
+
// In activity mode, always make the share button visible by setting a default shareSubject if not provided
|
|
884
|
+
if (
|
|
885
|
+
_options.getShareSubject() == null ||
|
|
886
|
+
_options.getShareSubject().isEmpty()
|
|
887
|
+
) {
|
|
888
|
+
_options.setShareSubject("Share");
|
|
889
|
+
Log.d("InAppBrowser", "Activity mode: Setting default shareSubject");
|
|
890
|
+
}
|
|
891
|
+
// Status bar color is already set at the top of this method, no need to set again
|
|
892
|
+
|
|
893
|
+
// Share button visibility is handled separately later
|
|
597
894
|
} else if (TextUtils.equals(_options.getToolbarType(), "navigation")) {
|
|
598
895
|
ImageButton buttonNearDoneView = _toolbar.findViewById(
|
|
599
896
|
R.id.buttonNearDone
|
|
600
897
|
);
|
|
601
898
|
buttonNearDoneView.setVisibility(View.GONE);
|
|
602
|
-
//
|
|
899
|
+
// Status bar color is already set at the top of this method, no need to set again
|
|
603
900
|
} else if (TextUtils.equals(_options.getToolbarType(), "blank")) {
|
|
604
901
|
_toolbar.setVisibility(View.GONE);
|
|
902
|
+
|
|
903
|
+
// Also set window background color to match status bar for blank toolbar
|
|
904
|
+
View statusBarColorView = findViewById(R.id.status_bar_color_view);
|
|
905
|
+
if (
|
|
906
|
+
_options.getToolbarColor() != null &&
|
|
907
|
+
!_options.getToolbarColor().isEmpty()
|
|
908
|
+
) {
|
|
909
|
+
try {
|
|
910
|
+
int toolbarColor = Color.parseColor(_options.getToolbarColor());
|
|
911
|
+
if (getWindow() != null) {
|
|
912
|
+
getWindow().getDecorView().setBackgroundColor(toolbarColor);
|
|
913
|
+
}
|
|
914
|
+
// Also set status bar color view background if available
|
|
915
|
+
if (statusBarColorView != null) {
|
|
916
|
+
statusBarColorView.setBackgroundColor(toolbarColor);
|
|
917
|
+
}
|
|
918
|
+
} catch (IllegalArgumentException e) {
|
|
919
|
+
// Fallback to system default if color parsing fails
|
|
920
|
+
boolean isDarkTheme = isDarkThemeEnabled();
|
|
921
|
+
int windowBackgroundColor = isDarkTheme ? Color.BLACK : Color.WHITE;
|
|
922
|
+
if (getWindow() != null) {
|
|
923
|
+
getWindow()
|
|
924
|
+
.getDecorView()
|
|
925
|
+
.setBackgroundColor(windowBackgroundColor);
|
|
926
|
+
}
|
|
927
|
+
// Also set status bar color view background if available
|
|
928
|
+
if (statusBarColorView != null) {
|
|
929
|
+
statusBarColorView.setBackgroundColor(windowBackgroundColor);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
} else {
|
|
933
|
+
// Follow system dark mode
|
|
934
|
+
boolean isDarkTheme = isDarkThemeEnabled();
|
|
935
|
+
int windowBackgroundColor = isDarkTheme ? Color.BLACK : Color.WHITE;
|
|
936
|
+
if (getWindow() != null) {
|
|
937
|
+
getWindow().getDecorView().setBackgroundColor(windowBackgroundColor);
|
|
938
|
+
}
|
|
939
|
+
// Also set status bar color view background if available
|
|
940
|
+
if (statusBarColorView != null) {
|
|
941
|
+
statusBarColorView.setBackgroundColor(windowBackgroundColor);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
605
944
|
} else {
|
|
606
945
|
_toolbar.findViewById(R.id.forwardButton).setVisibility(View.GONE);
|
|
607
946
|
_toolbar.findViewById(R.id.backButton).setVisibility(View.GONE);
|
|
608
947
|
|
|
948
|
+
// Status bar color is already set at the top of this method, no need to set again
|
|
949
|
+
|
|
609
950
|
Options.ButtonNearDone buttonNearDone = _options.getButtonNearDone();
|
|
610
951
|
if (buttonNearDone != null) {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
try {
|
|
616
|
-
ImageButton buttonNearDoneView = _toolbar.findViewById(
|
|
617
|
-
R.id.buttonNearDone
|
|
618
|
-
);
|
|
619
|
-
buttonNearDoneView.setVisibility(View.VISIBLE);
|
|
620
|
-
|
|
621
|
-
inputStream = assetManager.open(buttonNearDone.getIcon());
|
|
952
|
+
ImageButton buttonNearDoneView = _toolbar.findViewById(
|
|
953
|
+
R.id.buttonNearDone
|
|
954
|
+
);
|
|
955
|
+
buttonNearDoneView.setVisibility(View.VISIBLE);
|
|
622
956
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
957
|
+
// Handle different icon types
|
|
958
|
+
String iconType = buttonNearDone.getIconType();
|
|
959
|
+
if ("vector".equals(iconType)) {
|
|
960
|
+
// Use native Android vector drawable
|
|
961
|
+
try {
|
|
962
|
+
String iconName = buttonNearDone.getIcon();
|
|
963
|
+
// Convert name to Android resource ID (remove file extension if present)
|
|
964
|
+
if (iconName.endsWith(".xml")) {
|
|
965
|
+
iconName = iconName.substring(0, iconName.length() - 4);
|
|
966
|
+
}
|
|
629
967
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
968
|
+
// Get resource ID
|
|
969
|
+
int resourceId = _context
|
|
970
|
+
.getResources()
|
|
971
|
+
.getIdentifier(iconName, "drawable", _context.getPackageName());
|
|
972
|
+
|
|
973
|
+
if (resourceId != 0) {
|
|
974
|
+
// Set the vector drawable
|
|
975
|
+
buttonNearDoneView.setImageResource(resourceId);
|
|
976
|
+
// Apply color filter
|
|
977
|
+
buttonNearDoneView.setColorFilter(iconColor);
|
|
978
|
+
Log.d(
|
|
979
|
+
"InAppBrowser",
|
|
980
|
+
"Successfully loaded vector drawable: " + iconName
|
|
981
|
+
);
|
|
982
|
+
} else {
|
|
983
|
+
Log.e(
|
|
984
|
+
"InAppBrowser",
|
|
985
|
+
"Vector drawable not found: " + iconName + ", using fallback"
|
|
986
|
+
);
|
|
987
|
+
// Fallback to a common system icon
|
|
988
|
+
buttonNearDoneView.setImageResource(
|
|
989
|
+
android.R.drawable.ic_menu_info_details
|
|
990
|
+
);
|
|
991
|
+
buttonNearDoneView.setColorFilter(iconColor);
|
|
992
|
+
}
|
|
993
|
+
} catch (Exception e) {
|
|
994
|
+
Log.e(
|
|
995
|
+
"InAppBrowser",
|
|
996
|
+
"Error loading vector drawable: " + e.getMessage()
|
|
997
|
+
);
|
|
998
|
+
// Fallback to a common system icon
|
|
999
|
+
buttonNearDoneView.setImageResource(
|
|
1000
|
+
android.R.drawable.ic_menu_info_details
|
|
1001
|
+
);
|
|
1002
|
+
buttonNearDoneView.setColorFilter(iconColor);
|
|
1003
|
+
}
|
|
1004
|
+
} else if ("asset".equals(iconType)) {
|
|
1005
|
+
// Handle SVG from assets
|
|
1006
|
+
AssetManager assetManager = _context.getAssets();
|
|
1007
|
+
InputStream inputStream = null;
|
|
1008
|
+
try {
|
|
1009
|
+
// Try to load from public folder first
|
|
1010
|
+
String iconPath = "public/" + buttonNearDone.getIcon();
|
|
638
1011
|
try {
|
|
639
|
-
inputStream.
|
|
1012
|
+
inputStream = assetManager.open(iconPath);
|
|
640
1013
|
} catch (IOException e) {
|
|
641
|
-
|
|
1014
|
+
// If not found in public, try root assets
|
|
1015
|
+
try {
|
|
1016
|
+
inputStream = assetManager.open(buttonNearDone.getIcon());
|
|
1017
|
+
} catch (IOException e2) {
|
|
1018
|
+
Log.e(
|
|
1019
|
+
"InAppBrowser",
|
|
1020
|
+
"SVG file not found in assets: " + buttonNearDone.getIcon()
|
|
1021
|
+
);
|
|
1022
|
+
buttonNearDoneView.setVisibility(View.GONE);
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
if (inputStream == null) {
|
|
1028
|
+
Log.e(
|
|
1029
|
+
"InAppBrowser",
|
|
1030
|
+
"Failed to load SVG icon: " + buttonNearDone.getIcon()
|
|
1031
|
+
);
|
|
1032
|
+
buttonNearDoneView.setVisibility(View.GONE);
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
// Parse and render SVG
|
|
1037
|
+
SVG svg = SVG.getFromInputStream(inputStream);
|
|
1038
|
+
if (svg == null) {
|
|
1039
|
+
Log.e(
|
|
1040
|
+
"InAppBrowser",
|
|
1041
|
+
"Failed to parse SVG icon: " + buttonNearDone.getIcon()
|
|
1042
|
+
);
|
|
1043
|
+
buttonNearDoneView.setVisibility(View.GONE);
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
// Get the dimensions from options or use SVG's size
|
|
1048
|
+
float width = buttonNearDone.getWidth() > 0
|
|
1049
|
+
? buttonNearDone.getWidth()
|
|
1050
|
+
: 24;
|
|
1051
|
+
float height = buttonNearDone.getHeight() > 0
|
|
1052
|
+
? buttonNearDone.getHeight()
|
|
1053
|
+
: 24;
|
|
1054
|
+
|
|
1055
|
+
// Get density for proper scaling
|
|
1056
|
+
float density = _context.getResources().getDisplayMetrics().density;
|
|
1057
|
+
int targetWidth = Math.round(width * density);
|
|
1058
|
+
int targetHeight = Math.round(height * density);
|
|
1059
|
+
|
|
1060
|
+
// Set document size
|
|
1061
|
+
svg.setDocumentWidth(targetWidth);
|
|
1062
|
+
svg.setDocumentHeight(targetHeight);
|
|
1063
|
+
|
|
1064
|
+
// Create a bitmap and render SVG to it for better quality
|
|
1065
|
+
Bitmap bitmap = Bitmap.createBitmap(
|
|
1066
|
+
targetWidth,
|
|
1067
|
+
targetHeight,
|
|
1068
|
+
Bitmap.Config.ARGB_8888
|
|
1069
|
+
);
|
|
1070
|
+
Canvas canvas = new Canvas(bitmap);
|
|
1071
|
+
svg.renderToCanvas(canvas);
|
|
1072
|
+
|
|
1073
|
+
// Apply color filter to the bitmap
|
|
1074
|
+
Paint paint = new Paint();
|
|
1075
|
+
paint.setColorFilter(
|
|
1076
|
+
new PorterDuffColorFilter(iconColor, PorterDuff.Mode.SRC_IN)
|
|
1077
|
+
);
|
|
1078
|
+
Canvas colorFilterCanvas = new Canvas(bitmap);
|
|
1079
|
+
colorFilterCanvas.drawBitmap(bitmap, 0, 0, paint);
|
|
1080
|
+
|
|
1081
|
+
// Set the colored bitmap as image
|
|
1082
|
+
buttonNearDoneView.setImageBitmap(bitmap);
|
|
1083
|
+
buttonNearDoneView.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
|
1084
|
+
buttonNearDoneView.setPadding(12, 12, 12, 12); // Standard button padding
|
|
1085
|
+
} catch (SVGParseException e) {
|
|
1086
|
+
Log.e(
|
|
1087
|
+
"InAppBrowser",
|
|
1088
|
+
"Error loading SVG icon: " + e.getMessage(),
|
|
1089
|
+
e
|
|
1090
|
+
);
|
|
1091
|
+
buttonNearDoneView.setVisibility(View.GONE);
|
|
1092
|
+
} finally {
|
|
1093
|
+
if (inputStream != null) {
|
|
1094
|
+
try {
|
|
1095
|
+
inputStream.close();
|
|
1096
|
+
} catch (IOException e) {
|
|
1097
|
+
Log.e(
|
|
1098
|
+
"InAppBrowser",
|
|
1099
|
+
"Error closing input stream: " + e.getMessage()
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
642
1102
|
}
|
|
643
1103
|
}
|
|
1104
|
+
} else {
|
|
1105
|
+
// Default fallback or unsupported type
|
|
1106
|
+
Log.e("InAppBrowser", "Unsupported icon type: " + iconType);
|
|
1107
|
+
buttonNearDoneView.setVisibility(View.GONE);
|
|
644
1108
|
}
|
|
1109
|
+
|
|
1110
|
+
// Set the click listener
|
|
1111
|
+
buttonNearDoneView.setOnClickListener(view ->
|
|
1112
|
+
_options.getCallbacks().buttonNearDoneClicked()
|
|
1113
|
+
);
|
|
645
1114
|
} else {
|
|
646
1115
|
ImageButton buttonNearDoneView = _toolbar.findViewById(
|
|
647
1116
|
R.id.buttonNearDone
|
|
@@ -649,6 +1118,94 @@ public class WebViewDialog extends Dialog {
|
|
|
649
1118
|
buttonNearDoneView.setVisibility(View.GONE);
|
|
650
1119
|
}
|
|
651
1120
|
}
|
|
1121
|
+
|
|
1122
|
+
// Add share button functionality
|
|
1123
|
+
ImageButton shareButton = _toolbar.findViewById(R.id.shareButton);
|
|
1124
|
+
if (
|
|
1125
|
+
_options.getShareSubject() != null &&
|
|
1126
|
+
!_options.getShareSubject().isEmpty()
|
|
1127
|
+
) {
|
|
1128
|
+
shareButton.setVisibility(View.VISIBLE);
|
|
1129
|
+
Log.d(
|
|
1130
|
+
"InAppBrowser",
|
|
1131
|
+
"Share button should be visible, shareSubject: " +
|
|
1132
|
+
_options.getShareSubject()
|
|
1133
|
+
);
|
|
1134
|
+
|
|
1135
|
+
// Apply the same color filter as other buttons to ensure visibility
|
|
1136
|
+
shareButton.setColorFilter(iconColor);
|
|
1137
|
+
|
|
1138
|
+
// The color filter is now applied in applyColorToAllButtons
|
|
1139
|
+
shareButton.setOnClickListener(view -> {
|
|
1140
|
+
JSObject shareDisclaimer = _options.getShareDisclaimer();
|
|
1141
|
+
if (shareDisclaimer != null) {
|
|
1142
|
+
new AlertDialog.Builder(_context)
|
|
1143
|
+
.setTitle(shareDisclaimer.getString("title", "Title"))
|
|
1144
|
+
.setMessage(shareDisclaimer.getString("message", "Message"))
|
|
1145
|
+
.setPositiveButton(
|
|
1146
|
+
shareDisclaimer.getString("confirmBtn", "Confirm"),
|
|
1147
|
+
(dialog, which) -> {
|
|
1148
|
+
_options.getCallbacks().confirmBtnClicked();
|
|
1149
|
+
shareUrl();
|
|
1150
|
+
}
|
|
1151
|
+
)
|
|
1152
|
+
.setNegativeButton(
|
|
1153
|
+
shareDisclaimer.getString("cancelBtn", "Cancel"),
|
|
1154
|
+
null
|
|
1155
|
+
)
|
|
1156
|
+
.show();
|
|
1157
|
+
} else {
|
|
1158
|
+
shareUrl();
|
|
1159
|
+
}
|
|
1160
|
+
});
|
|
1161
|
+
} else {
|
|
1162
|
+
shareButton.setVisibility(View.GONE);
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
// Also color the title text
|
|
1166
|
+
TextView titleText = _toolbar.findViewById(R.id.titleText);
|
|
1167
|
+
if (titleText != null) {
|
|
1168
|
+
titleText.setTextColor(iconColor);
|
|
1169
|
+
|
|
1170
|
+
// Set the title text
|
|
1171
|
+
if (!TextUtils.isEmpty(_options.getTitle())) {
|
|
1172
|
+
this.setTitle(_options.getTitle());
|
|
1173
|
+
} else {
|
|
1174
|
+
try {
|
|
1175
|
+
URI uri = new URI(_options.getUrl());
|
|
1176
|
+
this.setTitle(uri.getHost());
|
|
1177
|
+
} catch (URISyntaxException e) {
|
|
1178
|
+
this.setTitle(_options.getTitle());
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
/**
|
|
1185
|
+
* Applies background and tint colors to all buttons in the toolbar
|
|
1186
|
+
*/
|
|
1187
|
+
private void applyColorToAllButtons(int backgroundColor, int iconColor) {
|
|
1188
|
+
// Get all buttons
|
|
1189
|
+
ImageButton backButton = _toolbar.findViewById(R.id.backButton);
|
|
1190
|
+
ImageButton forwardButton = _toolbar.findViewById(R.id.forwardButton);
|
|
1191
|
+
ImageButton closeButton = _toolbar.findViewById(R.id.closeButton);
|
|
1192
|
+
ImageButton reloadButton = _toolbar.findViewById(R.id.reloadButton);
|
|
1193
|
+
ImageButton shareButton = _toolbar.findViewById(R.id.shareButton);
|
|
1194
|
+
ImageButton buttonNearDoneView = _toolbar.findViewById(R.id.buttonNearDone);
|
|
1195
|
+
|
|
1196
|
+
// Set button backgrounds
|
|
1197
|
+
backButton.setBackgroundColor(backgroundColor);
|
|
1198
|
+
forwardButton.setBackgroundColor(backgroundColor);
|
|
1199
|
+
closeButton.setBackgroundColor(backgroundColor);
|
|
1200
|
+
reloadButton.setBackgroundColor(backgroundColor);
|
|
1201
|
+
|
|
1202
|
+
// Apply tint colors to buttons
|
|
1203
|
+
backButton.setColorFilter(iconColor);
|
|
1204
|
+
forwardButton.setColorFilter(iconColor);
|
|
1205
|
+
closeButton.setColorFilter(iconColor);
|
|
1206
|
+
reloadButton.setColorFilter(iconColor);
|
|
1207
|
+
shareButton.setColorFilter(iconColor);
|
|
1208
|
+
buttonNearDoneView.setColorFilter(iconColor);
|
|
652
1209
|
}
|
|
653
1210
|
|
|
654
1211
|
public void handleProxyResultError(String result, String id) {
|
|
@@ -838,7 +1395,7 @@ public class WebViewDialog extends Dialog {
|
|
|
838
1395
|
);
|
|
839
1396
|
}
|
|
840
1397
|
String s = String.format(
|
|
841
|
-
"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'})
|
|
1398
|
+
"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'})}",
|
|
842
1399
|
headers,
|
|
843
1400
|
toBase64(request.getUrl().toString()),
|
|
844
1401
|
request.getMethod(),
|
|
@@ -1021,18 +1578,36 @@ public class WebViewDialog extends Dialog {
|
|
|
1021
1578
|
if (_webView.canGoBack()) {
|
|
1022
1579
|
backButton.setImageResource(R.drawable.arrow_back_enabled);
|
|
1023
1580
|
backButton.setEnabled(true);
|
|
1581
|
+
backButton.setColorFilter(iconColor);
|
|
1024
1582
|
} else {
|
|
1025
1583
|
backButton.setImageResource(R.drawable.arrow_back_disabled);
|
|
1026
1584
|
backButton.setEnabled(false);
|
|
1585
|
+
backButton.setColorFilter(
|
|
1586
|
+
Color.argb(
|
|
1587
|
+
128,
|
|
1588
|
+
Color.red(iconColor),
|
|
1589
|
+
Color.green(iconColor),
|
|
1590
|
+
Color.blue(iconColor)
|
|
1591
|
+
)
|
|
1592
|
+
);
|
|
1027
1593
|
}
|
|
1028
1594
|
|
|
1029
1595
|
ImageButton forwardButton = _toolbar.findViewById(R.id.forwardButton);
|
|
1030
1596
|
if (_webView.canGoForward()) {
|
|
1031
1597
|
forwardButton.setImageResource(R.drawable.arrow_forward_enabled);
|
|
1032
1598
|
forwardButton.setEnabled(true);
|
|
1599
|
+
forwardButton.setColorFilter(iconColor);
|
|
1033
1600
|
} else {
|
|
1034
1601
|
forwardButton.setImageResource(R.drawable.arrow_forward_disabled);
|
|
1035
1602
|
forwardButton.setEnabled(false);
|
|
1603
|
+
forwardButton.setColorFilter(
|
|
1604
|
+
Color.argb(
|
|
1605
|
+
128,
|
|
1606
|
+
Color.red(iconColor),
|
|
1607
|
+
Color.green(iconColor),
|
|
1608
|
+
Color.blue(iconColor)
|
|
1609
|
+
)
|
|
1610
|
+
);
|
|
1036
1611
|
}
|
|
1037
1612
|
|
|
1038
1613
|
_options.getCallbacks().pageLoaded();
|
|
@@ -1178,4 +1753,82 @@ public class WebViewDialog extends Dialog {
|
|
|
1178
1753
|
proxiedRequestsHashmap.remove(key);
|
|
1179
1754
|
}
|
|
1180
1755
|
}
|
|
1756
|
+
|
|
1757
|
+
private void shareUrl() {
|
|
1758
|
+
Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
|
1759
|
+
shareIntent.setType("text/plain");
|
|
1760
|
+
shareIntent.putExtra(Intent.EXTRA_SUBJECT, _options.getShareSubject());
|
|
1761
|
+
shareIntent.putExtra(Intent.EXTRA_TEXT, _options.getUrl());
|
|
1762
|
+
_context.startActivity(Intent.createChooser(shareIntent, "Share"));
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
private boolean isDarkColor(int color) {
|
|
1766
|
+
int red = Color.red(color);
|
|
1767
|
+
int green = Color.green(color);
|
|
1768
|
+
int blue = Color.blue(color);
|
|
1769
|
+
double luminance = (0.299 * red + 0.587 * green + 0.114 * blue) / 255.0;
|
|
1770
|
+
return luminance < 0.5;
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
private boolean isDarkThemeEnabled() {
|
|
1774
|
+
// This method checks if dark theme is currently enabled without using Configuration class
|
|
1775
|
+
try {
|
|
1776
|
+
// On Android 10+, check via resources for night mode
|
|
1777
|
+
Resources.Theme theme = _context.getTheme();
|
|
1778
|
+
TypedValue typedValue = new TypedValue();
|
|
1779
|
+
|
|
1780
|
+
if (
|
|
1781
|
+
theme.resolveAttribute(android.R.attr.isLightTheme, typedValue, true)
|
|
1782
|
+
) {
|
|
1783
|
+
// isLightTheme exists - returns true if light, false if dark
|
|
1784
|
+
return typedValue.data != 1;
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
// Fallback method - check background color of window
|
|
1788
|
+
if (
|
|
1789
|
+
theme.resolveAttribute(
|
|
1790
|
+
android.R.attr.windowBackground,
|
|
1791
|
+
typedValue,
|
|
1792
|
+
true
|
|
1793
|
+
)
|
|
1794
|
+
) {
|
|
1795
|
+
int backgroundColor = typedValue.data;
|
|
1796
|
+
return isDarkColor(backgroundColor);
|
|
1797
|
+
}
|
|
1798
|
+
} catch (Exception e) {
|
|
1799
|
+
// Ignore and fallback to light theme
|
|
1800
|
+
}
|
|
1801
|
+
return false;
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
private void injectDatePickerFixes() {
|
|
1805
|
+
if (_webView == null || datePickerInjected) {
|
|
1806
|
+
return;
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
datePickerInjected = true;
|
|
1810
|
+
|
|
1811
|
+
// This script adds minimal fixes for date inputs to use Material Design
|
|
1812
|
+
String script =
|
|
1813
|
+
"(function() {\n" +
|
|
1814
|
+
" // Find all date inputs\n" +
|
|
1815
|
+
" const dateInputs = document.querySelectorAll('input[type=\"date\"]');\n" +
|
|
1816
|
+
" dateInputs.forEach(input => {\n" +
|
|
1817
|
+
" // Ensure change events propagate correctly\n" +
|
|
1818
|
+
" let lastValue = input.value;\n" +
|
|
1819
|
+
" input.addEventListener('change', () => {\n" +
|
|
1820
|
+
" if (input.value !== lastValue) {\n" +
|
|
1821
|
+
" lastValue = input.value;\n" +
|
|
1822
|
+
" // Dispatch an input event to ensure frameworks detect the change\n" +
|
|
1823
|
+
" input.dispatchEvent(new Event('input', { bubbles: true }));\n" +
|
|
1824
|
+
" }\n" +
|
|
1825
|
+
" });\n" +
|
|
1826
|
+
" });\n" +
|
|
1827
|
+
"})();";
|
|
1828
|
+
|
|
1829
|
+
// Execute the script in the WebView
|
|
1830
|
+
_webView.post(() -> _webView.evaluateJavascript(script, null));
|
|
1831
|
+
|
|
1832
|
+
Log.d("InAppBrowser", "Applied minimal date picker fixes");
|
|
1833
|
+
}
|
|
1181
1834
|
}
|