@capgo/inappbrowser 7.3.0 → 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 +660 -102
  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
@@ -46,6 +46,10 @@ public class InAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
46
46
  var currentPluginCall: CAPPluginCall?
47
47
  var isPresentAfterPageLoad = false
48
48
  var webViewController: WKWebViewController?
49
+ private var closeModalTitle: String?
50
+ private var closeModalDescription: String?
51
+ private var closeModalOk: String?
52
+ private var closeModalCancel: String?
49
53
 
50
54
  private func setup() {
51
55
  self.isSetupDone = true
@@ -186,10 +190,94 @@ public class InAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
186
190
  }
187
191
 
188
192
  if iconType == "sf-symbol" {
189
- buttonNearDoneIcon = UIImage(systemName: icon)
193
+ buttonNearDoneIcon = UIImage(systemName: icon)?.withRenderingMode(.alwaysTemplate)
194
+ print("[DEBUG] Set buttonNearDone SF Symbol icon: \(icon)")
190
195
  } else {
191
- // UIImage(resource: ImageResource(name: "public/monkey.svg", bundle: Bundle.main))
192
- buttonNearDoneIcon = UIImage(named: icon, in: Bundle.main, with: nil)
196
+ // Look in app's web assets/public directory
197
+ guard let webDir = Bundle.main.resourceURL?.appendingPathComponent("public") else {
198
+ print("[DEBUG] Failed to locate web assets directory")
199
+ return
200
+ }
201
+
202
+ // Try several path combinations to find the asset
203
+ let paths = [
204
+ icon, // Just the icon name
205
+ "public/\(icon)", // With public/ prefix
206
+ icon.replacingOccurrences(of: "public/", with: "") // Without public/ prefix
207
+ ]
208
+
209
+ var foundImage = false
210
+
211
+ for path in paths {
212
+ // Try as a direct path from web assets dir
213
+ let assetPath = path.replacingOccurrences(of: "public/", with: "")
214
+ let fileURL = webDir.appendingPathComponent(assetPath)
215
+
216
+ print("[DEBUG] Trying to load from: \(fileURL.path)")
217
+
218
+ if FileManager.default.fileExists(atPath: fileURL.path),
219
+ let data = try? Data(contentsOf: fileURL),
220
+ let img = UIImage(data: data) {
221
+ buttonNearDoneIcon = img.withRenderingMode(.alwaysTemplate)
222
+ print("[DEBUG] Successfully loaded buttonNearDone from web assets: \(fileURL.path)")
223
+ foundImage = true
224
+ break
225
+ }
226
+
227
+ // Try with www directory as an alternative
228
+ if let wwwDir = Bundle.main.resourceURL?.appendingPathComponent("www") {
229
+ let wwwFileURL = wwwDir.appendingPathComponent(assetPath)
230
+
231
+ print("[DEBUG] Trying to load from www dir: \(wwwFileURL.path)")
232
+
233
+ if FileManager.default.fileExists(atPath: wwwFileURL.path),
234
+ let data = try? Data(contentsOf: wwwFileURL),
235
+ let img = UIImage(data: data) {
236
+ buttonNearDoneIcon = img.withRenderingMode(.alwaysTemplate)
237
+ print("[DEBUG] Successfully loaded buttonNearDone from www dir: \(wwwFileURL.path)")
238
+ foundImage = true
239
+ break
240
+ }
241
+ }
242
+
243
+ // Try looking in app bundle assets
244
+ if let iconImage = UIImage(named: path) {
245
+ buttonNearDoneIcon = iconImage.withRenderingMode(.alwaysTemplate)
246
+ print("[DEBUG] Successfully loaded buttonNearDone from app bundle: \(path)")
247
+ foundImage = true
248
+ break
249
+ }
250
+ }
251
+
252
+ if !foundImage {
253
+ print("[DEBUG] Failed to load buttonNearDone icon: \(icon)")
254
+
255
+ // Debug info
256
+ if let resourceURL = Bundle.main.resourceURL {
257
+ print("[DEBUG] Resource URL: \(resourceURL.path)")
258
+
259
+ // List directories to help debugging
260
+ do {
261
+ let contents = try FileManager.default.contentsOfDirectory(atPath: resourceURL.path)
262
+ print("[DEBUG] Root bundle contents: \(contents)")
263
+
264
+ // Check if public or www directories exist
265
+ if contents.contains("public") {
266
+ let publicContents = try FileManager.default.contentsOfDirectory(
267
+ atPath: resourceURL.appendingPathComponent("public").path)
268
+ print("[DEBUG] Public dir contents: \(publicContents)")
269
+ }
270
+
271
+ if contents.contains("www") {
272
+ let wwwContents = try FileManager.default.contentsOfDirectory(
273
+ atPath: resourceURL.appendingPathComponent("www").path)
274
+ print("[DEBUG] WWW dir contents: \(wwwContents)")
275
+ }
276
+ } catch {
277
+ print("[DEBUG] Error listing directories: \(error)")
278
+ }
279
+ }
280
+ }
193
281
  }
194
282
  }
195
283
 
@@ -203,12 +291,63 @@ public class InAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
203
291
  let preventDeeplink = call.getBool("preventDeeplink", false)
204
292
  let isAnimated = call.getBool("isAnimated", true)
205
293
 
206
- var disclaimerContent = call.getObject("shareDisclaimer")
294
+ // Validate preShowScript requires isPresentAfterPageLoad
295
+ if call.getString("preShowScript") != nil && !call.getBool("isPresentAfterPageLoad", false) {
296
+ call.reject("preShowScript requires isPresentAfterPageLoad to be true")
297
+ return
298
+ }
299
+
300
+ // Validate closeModal options
301
+ if closeModal {
302
+ if call.getString("closeModalTitle") != nil ||
303
+ call.getString("closeModalDescription") != nil ||
304
+ call.getString("closeModalOk") != nil ||
305
+ call.getString("closeModalCancel") != nil {
306
+ // Store the values to be set after proper initialization
307
+ self.closeModalTitle = closeModalTitle
308
+ self.closeModalDescription = closeModalDescription
309
+ self.closeModalOk = closeModalOk
310
+ self.closeModalCancel = closeModalCancel
311
+ }
312
+ } else {
313
+ // Reject if closeModal is false but closeModal options are provided
314
+ if call.getString("closeModalTitle") != nil ||
315
+ call.getString("closeModalDescription") != nil ||
316
+ call.getString("closeModalOk") != nil ||
317
+ call.getString("closeModalCancel") != nil {
318
+ call.reject("closeModal options require closeModal to be true")
319
+ return
320
+ }
321
+ }
322
+
323
+ // Validate shareDisclaimer requires shareSubject
324
+ if call.getString("shareSubject") == nil && call.getObject("shareDisclaimer") != nil {
325
+ call.reject("shareDisclaimer requires shareSubject to be provided")
326
+ return
327
+ }
328
+
329
+ // Validate buttonNearDone compatibility with toolbar type
330
+ if call.getString("buttonNearDone") != nil {
331
+ let toolbarType = call.getString("toolbarType", "")
332
+ if toolbarType == "activity" || toolbarType == "navigation" || toolbarType == "blank" {
333
+ call.reject("buttonNearDone is not compatible with toolbarType: " + toolbarType)
334
+ return
335
+ }
336
+ }
337
+
338
+ var disclaimerContent: JSObject?
339
+ if let shareDisclaimerRaw = call.getObject("shareDisclaimer"), !shareDisclaimerRaw.isEmpty {
340
+ disclaimerContent = shareDisclaimerRaw
341
+ }
342
+
207
343
  let toolbarType = call.getString("toolbarType", "")
208
344
  let backgroundColor = call.getString("backgroundColor", "black") == "white" ? UIColor.white : UIColor.black
209
- if toolbarType != "activity" {
210
- disclaimerContent = nil
211
- }
345
+
346
+ // Don't null out shareDisclaimer regardless of toolbarType
347
+ // if toolbarType != "activity" {
348
+ // disclaimerContent = nil
349
+ // }
350
+
212
351
  let ignoreUntrustedSSLError = call.getBool("ignoreUntrustedSSLError", false)
213
352
 
214
353
  self.isPresentAfterPageLoad = call.getBool("isPresentAfterPageLoad", false)
@@ -230,46 +369,209 @@ public class InAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
230
369
  }
231
370
 
232
371
  webViewController.source = .remote(url)
233
- webViewController.leftNavigationBarItemTypes = self.getToolbarItems(toolbarType: toolbarType) + [.reload]
372
+ webViewController.leftNavigationBarItemTypes = []
234
373
  webViewController.toolbarItemTypes = []
235
- webViewController.doneBarButtonItemPosition = .right
236
374
 
237
- webViewController.buttonNearDoneIcon = buttonNearDoneIcon
375
+ // Configure close button based on showArrow
376
+ let showArrow = call.getBool("showArrow", false)
377
+ if showArrow {
378
+ // When showArrow is true, put arrow on left
379
+ webViewController.doneBarButtonItemPosition = .left
380
+ webViewController.showArrowAsClose = true
381
+ } else {
382
+ // Default X on right
383
+ webViewController.doneBarButtonItemPosition = toolbarType == "activity" ? .none : .right
384
+ webViewController.showArrowAsClose = false
385
+ }
386
+
387
+ // Configure navigation buttons based on toolbarType
388
+ if toolbarType == "activity" {
389
+ // Activity mode should ONLY have:
390
+ // 1. Close button (if not hidden by doneBarButtonItemPosition)
391
+ // 2. Share button (if shareSubject is provided)
392
+ webViewController.leftNavigationBarItemTypes = [] // Clear any left items
393
+ webViewController.rightNavigaionBarItemTypes = [] // Clear any right items
394
+
395
+ // Only add share button if subject is provided
396
+ if call.getString("shareSubject") != nil {
397
+ // Add share button to right bar
398
+ webViewController.rightNavigaionBarItemTypes.append(.activity)
399
+ print("[DEBUG] Activity mode: Added share button, shareSubject: \(call.getString("shareSubject") ?? "nil")")
400
+ } else {
401
+ // In activity mode, always make the share button visible by setting a default shareSubject
402
+ webViewController.shareSubject = "Share"
403
+ webViewController.rightNavigaionBarItemTypes.append(.activity)
404
+ print("[DEBUG] Activity mode: Setting default shareSubject")
405
+ }
238
406
 
239
- if call.getBool("showArrow", false) {
240
- webViewController.stopBarButtonItemImage = UIImage(named: "Forward@3x", in: Bundle(for: InAppBrowserPlugin.self), compatibleWith: nil)
407
+ // Set done button position based on showArrow
408
+ if showArrow {
409
+ webViewController.doneBarButtonItemPosition = .left
410
+ } else {
411
+ // In activity mode, keep the done button visible even when showArrow is false
412
+ webViewController.doneBarButtonItemPosition = .right
413
+ }
414
+ } else if toolbarType == "navigation" {
415
+ // Navigation mode puts back/forward on the left
416
+ webViewController.leftNavigationBarItemTypes = [.back, .forward]
417
+ if showReloadButton {
418
+ webViewController.leftNavigationBarItemTypes.append(.reload)
419
+ }
420
+
421
+ // Only add share button if subject is provided
422
+ if call.getString("shareSubject") != nil {
423
+ // Add share button to right navigation bar
424
+ webViewController.rightNavigaionBarItemTypes.append(.activity)
425
+ }
426
+ } else {
427
+ // Other modes may have reload button
428
+ if showReloadButton {
429
+ webViewController.leftNavigationBarItemTypes.append(.reload)
430
+ }
431
+
432
+ // Only add share button if subject is provided
433
+ if call.getString("shareSubject") != nil {
434
+ // Add share button to right navigation bar
435
+ webViewController.rightNavigaionBarItemTypes.append(.activity)
436
+ }
437
+ }
438
+
439
+ // Set buttonNearDoneIcon if provided
440
+ if let buttonNearDoneIcon = buttonNearDoneIcon {
441
+ webViewController.buttonNearDoneIcon = buttonNearDoneIcon
442
+ print("[DEBUG] Button near done icon set: \(buttonNearDoneIcon)")
241
443
  }
242
444
 
243
445
  webViewController.capBrowserPlugin = self
244
446
  webViewController.title = call.getString("title", "New Window")
245
- webViewController.shareSubject = call.getString("shareSubject")
447
+ // Only set shareSubject if not already set for activity mode
448
+ if webViewController.shareSubject == nil {
449
+ webViewController.shareSubject = call.getString("shareSubject")
450
+ }
246
451
  webViewController.shareDisclaimer = disclaimerContent
452
+
453
+ // Debug shareDisclaimer
454
+ if let disclaimer = disclaimerContent {
455
+ print("[DEBUG] Share disclaimer set: \(disclaimer)")
456
+ } else {
457
+ print("[DEBUG] No share disclaimer set")
458
+ }
459
+
247
460
  webViewController.preShowScript = call.getString("preShowScript")
248
461
  webViewController.websiteTitleInNavigationBar = call.getBool("visibleTitle", true)
462
+ webViewController.ignoreUntrustedSSLError = ignoreUntrustedSSLError
463
+
464
+ // Set closeModal properties after proper initialization
249
465
  if closeModal {
250
466
  webViewController.closeModal = true
251
- webViewController.closeModalTitle = closeModalTitle
252
- webViewController.closeModalDescription = closeModalDescription
253
- webViewController.closeModalOk = closeModalOk
254
- webViewController.closeModalCancel = closeModalCancel
467
+ webViewController.closeModalTitle = self.closeModalTitle ?? closeModalTitle
468
+ webViewController.closeModalDescription = self.closeModalDescription ?? closeModalDescription
469
+ webViewController.closeModalOk = self.closeModalOk ?? closeModalOk
470
+ webViewController.closeModalCancel = self.closeModalCancel ?? closeModalCancel
255
471
  }
256
- webViewController.ignoreUntrustedSSLError = ignoreUntrustedSSLError
257
472
 
258
473
  self.navigationWebViewController = UINavigationController.init(rootViewController: webViewController)
259
474
  self.navigationWebViewController?.navigationBar.isTranslucent = false
260
475
  self.navigationWebViewController?.toolbar.isTranslucent = false
261
- self.navigationWebViewController?.navigationBar.backgroundColor = backgroundColor
262
- self.navigationWebViewController?.toolbar.backgroundColor = backgroundColor
263
- self.navigationWebViewController?.toolbar.tintColor = backgroundColor == UIColor.black ? UIColor.white : UIColor.black
476
+
477
+ // Ensure no lines or borders appear by default
478
+ self.navigationWebViewController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
479
+ self.navigationWebViewController?.navigationBar.shadowImage = UIImage()
480
+ self.navigationWebViewController?.navigationBar.setValue(true, forKey: "hidesShadow")
481
+ self.navigationWebViewController?.toolbar.setShadowImage(UIImage(), forToolbarPosition: .any)
482
+
483
+ // Handle toolbar color
484
+ if let toolbarColor = call.getString("toolbarColor"), self.isHexColorCode(toolbarColor) {
485
+ // If specific color provided, use it
486
+ let color = UIColor(hexString: toolbarColor)
487
+
488
+ // Apply to status bar and navigation bar area with a single colored view
489
+ webViewController.setupStatusBarBackground(color: color)
490
+
491
+ // Set status bar style based on toolbar color
492
+ let isDark = self.isDarkColor(color)
493
+ webViewController.statusBarStyle = isDark ? .lightContent : .darkContent
494
+ webViewController.updateStatusBarStyle()
495
+
496
+ // Apply text color
497
+ let textColor: UIColor
498
+ if let toolbarTextColor = call.getString("toolbarTextColor"), self.isHexColorCode(toolbarTextColor) {
499
+ textColor = UIColor(hexString: toolbarTextColor)
500
+ } else {
501
+ textColor = isDark ? UIColor.white : UIColor.black
502
+ }
503
+
504
+ // Apply tint color to all UI elements without changing background
505
+ self.navigationWebViewController?.navigationBar.tintColor = textColor
506
+ webViewController.tintColor = textColor
507
+ self.navigationWebViewController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: textColor]
508
+ } else {
509
+ // Use system appearance
510
+ let isDarkMode = UITraitCollection.current.userInterfaceStyle == .dark
511
+ let backgroundColor = isDarkMode ? UIColor.black : UIColor.white
512
+ let textColor: UIColor
513
+
514
+ if let toolbarTextColor = call.getString("toolbarTextColor"), self.isHexColorCode(toolbarTextColor) {
515
+ textColor = UIColor(hexString: toolbarTextColor)
516
+ } else {
517
+ textColor = isDarkMode ? UIColor.white : UIColor.black
518
+ }
519
+
520
+ // Apply colors
521
+ webViewController.setupStatusBarBackground(color: backgroundColor)
522
+ webViewController.tintColor = textColor
523
+ self.navigationWebViewController?.navigationBar.tintColor = textColor
524
+ self.navigationWebViewController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: textColor]
525
+ webViewController.statusBarStyle = isDarkMode ? .lightContent : .darkContent
526
+ webViewController.updateStatusBarStyle()
527
+
528
+ }
529
+
264
530
  self.navigationWebViewController?.modalPresentationStyle = .fullScreen
265
531
  if toolbarType == "blank" {
266
532
  self.navigationWebViewController?.navigationBar.isHidden = true
267
533
  webViewController.blankNavigationTab = true
534
+
535
+ // Even with hidden navigation bar, we need to set proper status bar appearance
536
+ // If toolbarColor is explicitly set, use that for status bar style
537
+ if let toolbarColor = call.getString("toolbarColor"), self.isHexColorCode(toolbarColor) {
538
+ let color = UIColor(hexString: toolbarColor)
539
+ let isDark = self.isDarkColor(color)
540
+ webViewController.statusBarStyle = isDark ? .lightContent : .darkContent
541
+ webViewController.updateStatusBarStyle()
542
+
543
+ // Apply status bar background color via the special view
544
+ webViewController.setupStatusBarBackground(color: color)
545
+
546
+ // Apply background color to whole view to ensure no gaps
547
+ webViewController.view.backgroundColor = color
548
+ self.navigationWebViewController?.view.backgroundColor = color
549
+
550
+ // Apply status bar background color
551
+ if let navController = self.navigationWebViewController {
552
+ navController.view.backgroundColor = color
553
+ }
554
+ } else {
555
+ // Follow system appearance if no specific color
556
+ let isDarkMode = UITraitCollection.current.userInterfaceStyle == .dark
557
+ let backgroundColor = isDarkMode ? UIColor.black : UIColor.white
558
+ webViewController.statusBarStyle = isDarkMode ? .lightContent : .darkContent
559
+ webViewController.updateStatusBarStyle()
560
+
561
+ // Apply status bar background color via the special view
562
+ webViewController.setupStatusBarBackground(color: backgroundColor)
563
+
564
+ // Set appropriate background color
565
+ if let navController = self.navigationWebViewController {
566
+ navController.view.backgroundColor = backgroundColor
567
+ }
568
+ }
569
+
268
570
  }
269
- if showReloadButton {
270
- let toolbarItems = self.getToolbarItems(toolbarType: toolbarType)
271
- webViewController.leftNavigationBarItemTypes = toolbarItems + [.reload]
272
- }
571
+
572
+ // We don't use the toolbar anymore, always hide it
573
+ self.navigationWebViewController?.setToolbarHidden(true, animated: false)
574
+
273
575
  if !self.isPresentAfterPageLoad {
274
576
  self.presentView(isAnimated: isAnimated)
275
577
  }
@@ -277,17 +579,6 @@ public class InAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
277
579
  }
278
580
  }
279
581
 
280
- func getToolbarItems(toolbarType: String) -> [BarButtonItemType] {
281
- var result: [BarButtonItemType] = []
282
- if toolbarType == "activity" {
283
- result.append(.activity)
284
- } else if toolbarType == "navigation" {
285
- result.append(.back)
286
- result.append(.forward)
287
- }
288
- return result
289
- }
290
-
291
582
  @objc func reload(_ call: CAPPluginCall) {
292
583
  self.webViewController?.reload()
293
584
  call.resolve()
@@ -397,14 +688,9 @@ public class InAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
397
688
  self.navigationWebViewController?.navigationBar.isTranslucent = false
398
689
  self.navigationWebViewController?.toolbar.isTranslucent = false
399
690
  self.navigationWebViewController?.navigationBar.backgroundColor = .white
400
- let inputString: String = call.getString("toolbarColor", "#ffffff")
401
- var color: UIColor = UIColor(hexString: "#ffffff")
402
- if self.isHexColorCode(inputString) {
403
- color = UIColor(hexString: inputString)
404
- } else {
405
- print("\(inputString) is not a valid hex color code.")
406
- }
407
- self.navigationWebViewController?.toolbar.backgroundColor = color
691
+ self.navigationWebViewController?.toolbar.backgroundColor = .white
692
+ self.navigationWebViewController?.toolbar.tintColor = .black
693
+
408
694
  self.navigationWebViewController?.modalPresentationStyle = .fullScreen
409
695
  if !self.isPresentAfterPageLoad {
410
696
  self.presentView()
@@ -459,4 +745,13 @@ public class InAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
459
745
  }
460
746
  return credentials
461
747
  }
748
+
749
+ private func isDarkColor(_ color: UIColor) -> Bool {
750
+ let components = color.cgColor.components ?? []
751
+ let red = components[0]
752
+ let green = components[1]
753
+ let blue = components[2]
754
+ let brightness = (red * 299 + green * 587 + blue * 114) / 1000
755
+ return brightness < 0.5
756
+ }
462
757
  }