@capgo/inappbrowser 6.11.1 → 6.12.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 (28) hide show
  1. package/README.md +48 -42
  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 +650 -93
  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 +171 -37
  11. package/dist/esm/definitions.d.ts +224 -31
  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 +337 -44
  19. package/ios/Plugin/WKWebViewController.swift +478 -45
  20. package/package.json +7 -8
  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/Forward.imageset/Contents.json +0 -26
  26. package/ios/Plugin/Assets.xcassets/Forward.imageset/Forward.png +0 -0
  27. package/ios/Plugin/Assets.xcassets/Forward.imageset/Forward@2x.png +0 -0
  28. 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,207 @@ 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
+ }
406
+
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
+ }
238
420
 
239
- if call.getBool("showArrow", false) {
240
- webViewController.stopBarButtonItemImage = UIImage(named: "Forward@3x", in: Bundle(for: InAppBrowserPlugin.self), compatibleWith: nil)
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
+
264
529
  self.navigationWebViewController?.modalPresentationStyle = .fullScreen
265
530
  if toolbarType == "blank" {
266
531
  self.navigationWebViewController?.navigationBar.isHidden = true
267
532
  webViewController.blankNavigationTab = true
533
+
534
+ // Even with hidden navigation bar, we need to set proper status bar appearance
535
+ // If toolbarColor is explicitly set, use that for status bar style
536
+ if let toolbarColor = call.getString("toolbarColor"), self.isHexColorCode(toolbarColor) {
537
+ let color = UIColor(hexString: toolbarColor)
538
+ let isDark = self.isDarkColor(color)
539
+ webViewController.statusBarStyle = isDark ? .lightContent : .darkContent
540
+ webViewController.updateStatusBarStyle()
541
+
542
+ // Apply status bar background color via the special view
543
+ webViewController.setupStatusBarBackground(color: color)
544
+
545
+ // Apply background color to whole view to ensure no gaps
546
+ webViewController.view.backgroundColor = color
547
+ self.navigationWebViewController?.view.backgroundColor = color
548
+
549
+ // Apply status bar background color
550
+ if let navController = self.navigationWebViewController {
551
+ navController.view.backgroundColor = color
552
+ }
553
+ } else {
554
+ // Follow system appearance if no specific color
555
+ let isDarkMode = UITraitCollection.current.userInterfaceStyle == .dark
556
+ let backgroundColor = isDarkMode ? UIColor.black : UIColor.white
557
+ webViewController.statusBarStyle = isDarkMode ? .lightContent : .darkContent
558
+ webViewController.updateStatusBarStyle()
559
+
560
+ // Apply status bar background color via the special view
561
+ webViewController.setupStatusBarBackground(color: backgroundColor)
562
+
563
+ // Set appropriate background color
564
+ if let navController = self.navigationWebViewController {
565
+ navController.view.backgroundColor = backgroundColor
566
+ }
567
+ }
268
568
  }
269
- if showReloadButton {
270
- let toolbarItems = self.getToolbarItems(toolbarType: toolbarType)
271
- webViewController.leftNavigationBarItemTypes = toolbarItems + [.reload]
272
- }
569
+
570
+ // We don't use the toolbar anymore, always hide it
571
+ self.navigationWebViewController?.setToolbarHidden(true, animated: false)
572
+
273
573
  if !self.isPresentAfterPageLoad {
274
574
  self.presentView(isAnimated: isAnimated)
275
575
  }
@@ -277,17 +577,6 @@ public class InAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
277
577
  }
278
578
  }
279
579
 
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
580
  @objc func reload(_ call: CAPPluginCall) {
292
581
  self.webViewController?.reload()
293
582
  call.resolve()
@@ -397,14 +686,9 @@ public class InAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
397
686
  self.navigationWebViewController?.navigationBar.isTranslucent = false
398
687
  self.navigationWebViewController?.toolbar.isTranslucent = false
399
688
  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
689
+ self.navigationWebViewController?.toolbar.backgroundColor = .white
690
+ self.navigationWebViewController?.toolbar.tintColor = .black
691
+
408
692
  self.navigationWebViewController?.modalPresentationStyle = .fullScreen
409
693
  if !self.isPresentAfterPageLoad {
410
694
  self.presentView()
@@ -459,4 +743,13 @@ public class InAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
459
743
  }
460
744
  return credentials
461
745
  }
746
+
747
+ private func isDarkColor(_ color: UIColor) -> Bool {
748
+ let components = color.cgColor.components ?? []
749
+ let red = components[0]
750
+ let green = components[1]
751
+ let blue = components[2]
752
+ let brightness = (red * 299 + green * 587 + blue * 114) / 1000
753
+ return brightness < 0.5
754
+ }
462
755
  }