@capgo/capacitor-stream-call 7.0.1 → 7.0.3

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.
@@ -13,7 +13,7 @@ Pod::Spec.new do |s|
13
13
  s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
14
14
  s.ios.deployment_target = '14.0'
15
15
  s.dependency 'Capacitor'
16
- s.dependency 'StreamVideo', '1.29.0'
17
- s.dependency 'StreamVideoSwiftUI', '1.29.0'
16
+ s.dependency 'StreamVideo', '1.29.1'
17
+ s.dependency 'StreamVideoSwiftUI', '1.29.1'
18
18
  s.swift_version = '5.1'
19
19
  end
package/Package.swift CHANGED
@@ -11,7 +11,7 @@ let package = Package(
11
11
  ],
12
12
  dependencies: [
13
13
  .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "7.0.0"),
14
- .package(url: "https://github.com/GetStream/stream-video-swift.git", exact: "1.29.0")
14
+ .package(url: "https://github.com/GetStream/stream-video-swift.git", exact: "1.29.1")
15
15
  ],
16
16
  targets: [
17
17
  .target(
@@ -77,8 +77,8 @@ dependencies {
77
77
 
78
78
 
79
79
  // Stream dependencies
80
- implementation("io.getstream:stream-video-android-ui-compose:1.9.2")
81
- implementation("io.getstream:stream-video-android-core:1.9.2")
80
+ implementation("io.getstream:stream-video-android-ui-compose:1.10.0")
81
+ implementation("io.getstream:stream-video-android-core:1.10.0")
82
82
  implementation("io.getstream:stream-android-push:1.3.2")
83
83
  implementation("io.getstream:stream-android-push-firebase:1.3.2")
84
84
 
@@ -370,19 +370,6 @@ class StreamCallPlugin : Plugin() {
370
370
  val context = context
371
371
  val originalParent = bridge?.webView?.parent as? ViewGroup ?: return
372
372
 
373
- // Wrap original parent with TouchInterceptWrapper to allow touch passthrough
374
- val rootParent = originalParent.parent as? ViewGroup
375
- val indexInRoot = rootParent?.indexOfChild(originalParent) ?: -1
376
- if (rootParent != null && indexInRoot >= 0) {
377
- rootParent.removeViewAt(indexInRoot)
378
- touchInterceptWrapper = TouchInterceptWrapper(originalParent).apply {
379
- setBackgroundColor(Color.TRANSPARENT)
380
- }
381
- rootParent.addView(touchInterceptWrapper, indexInRoot)
382
- }
383
-
384
- val parent: ViewGroup = touchInterceptWrapper ?: originalParent
385
-
386
373
  // Make WebView initially visible and opaque
387
374
  bridge?.webView?.setBackgroundColor(Color.WHITE) // Or whatever background color suits your app
388
375
 
@@ -394,7 +381,7 @@ class StreamCallPlugin : Plugin() {
394
381
  ViewGroup.LayoutParams.MATCH_PARENT
395
382
  )
396
383
  }
397
- parent.addView(overlayView, 0) // Add at index 0 to ensure it's below WebView
384
+ originalParent.addView(overlayView, 0) // Add at index 0 to ensure it's below WebView
398
385
 
399
386
  // Initialize with active call content
400
387
  setOverlayContent()
@@ -408,7 +395,160 @@ class StreamCallPlugin : Plugin() {
408
395
  )
409
396
  setBackgroundColor("#1a242c".toColorInt())
410
397
  }
411
- parent.addView(barrierView, parent.indexOfChild(bridge?.webView) + 1) // Add above WebView
398
+ originalParent.addView(barrierView, originalParent.indexOfChild(bridge?.webView) + 1) // Add above WebView
399
+ }
400
+
401
+ private fun addTouchInterceptor() {
402
+ val webView = bridge?.webView
403
+ if (webView == null) {
404
+ Log.e("StreamCallPlugin", "addTouchInterceptor: WebView is null, cannot add touch interceptor")
405
+ return
406
+ }
407
+
408
+ val originalParent = webView.parent as? ViewGroup
409
+ if (originalParent == null) {
410
+ Log.e("StreamCallPlugin", "addTouchInterceptor: WebView parent is null or not a ViewGroup")
411
+ return
412
+ }
413
+
414
+ // Check if touch interceptor already exists
415
+ if (touchInterceptWrapper != null) {
416
+ Log.d("StreamCallPlugin", "addTouchInterceptor: Touch interceptor already exists, skipping creation")
417
+ return
418
+ }
419
+
420
+ Log.d("StreamCallPlugin", "addTouchInterceptor: Starting setup")
421
+ Log.d("StreamCallPlugin", "addTouchInterceptor: Original parent type: ${originalParent.javaClass.simpleName}")
422
+ Log.d("StreamCallPlugin", "addTouchInterceptor: Original parent child count: ${originalParent.childCount}")
423
+
424
+ // Wrap original parent with TouchInterceptWrapper to allow touch passthrough
425
+ val rootParent = originalParent.parent as? ViewGroup
426
+ val indexInRoot = rootParent?.indexOfChild(originalParent) ?: -1
427
+
428
+ if (rootParent != null && indexInRoot >= 0) {
429
+ Log.d("StreamCallPlugin", "addTouchInterceptor: Root parent type: ${rootParent.javaClass.simpleName}")
430
+
431
+ rootParent.removeViewAt(indexInRoot)
432
+ touchInterceptWrapper = TouchInterceptWrapper(originalParent).apply {
433
+ setBackgroundColor(Color.TRANSPARENT)
434
+ }
435
+ rootParent.addView(touchInterceptWrapper, indexInRoot)
436
+
437
+ // Move views to touch interceptor
438
+ val parent = touchInterceptWrapper!!
439
+ if (overlayView?.parent != parent) {
440
+ (overlayView?.parent as? ViewGroup)?.removeView(overlayView)
441
+ parent.addView(overlayView, 0)
442
+ }
443
+ if (barrierView?.parent != parent) {
444
+ (barrierView?.parent as? ViewGroup)?.removeView(barrierView)
445
+ parent.addView(barrierView, parent.indexOfChild(webView) + 1)
446
+ }
447
+
448
+ Log.d("StreamCallPlugin", "addTouchInterceptor: Touch interceptor added successfully")
449
+ Log.d("StreamCallPlugin", "addTouchInterceptor: TouchWrapper child count: ${parent.childCount}")
450
+
451
+ // Log children of touch wrapper
452
+ for (i in 0 until parent.childCount) {
453
+ val child = parent.getChildAt(i)
454
+ Log.d("StreamCallPlugin", "addTouchInterceptor: TouchWrapper child $i: ${child.javaClass.simpleName}")
455
+ }
456
+ } else {
457
+ Log.e("StreamCallPlugin", "addTouchInterceptor: Could not add touch interceptor - rootParent=$rootParent, indexInRoot=$indexInRoot")
458
+ }
459
+ }
460
+
461
+ private fun removeTouchInterceptor() {
462
+ val touchWrapper = touchInterceptWrapper ?: return
463
+ val rootParent = touchWrapper.parent as? ViewGroup ?: return
464
+ val indexInRoot = rootParent.indexOfChild(touchWrapper)
465
+
466
+ Log.d("StreamCallPlugin", "removeTouchInterceptor: Starting removal process")
467
+ Log.d("StreamCallPlugin", "removeTouchInterceptor: TouchWrapper has ${touchWrapper.childCount} children")
468
+ Log.d("StreamCallPlugin", "removeTouchInterceptor: RootParent type: ${rootParent.javaClass.simpleName}")
469
+
470
+ // Log all children of touchWrapper
471
+ for (i in 0 until touchWrapper.childCount) {
472
+ val child = touchWrapper.getChildAt(i)
473
+ Log.d("StreamCallPlugin", "removeTouchInterceptor: Child $i: ${child.javaClass.simpleName}")
474
+ }
475
+
476
+ // Store references to all children before removing
477
+ val childrenToRestore = mutableListOf<View>()
478
+ for (i in 0 until touchWrapper.childCount) {
479
+ childrenToRestore.add(touchWrapper.getChildAt(i))
480
+ }
481
+
482
+ // Find which child should be the new container (usually the one that was the original parent)
483
+ // This is typically a FrameLayout or similar ViewGroup, but NOT ComposeView
484
+ var newParent: ViewGroup? = null
485
+
486
+ // Look for a ViewGroup that is not a ComposeView to be the new parent
487
+ for (child in childrenToRestore) {
488
+ if (child is ViewGroup && child !is ComposeView) {
489
+ // Check if this ViewGroup originally contained the WebView or other views
490
+ newParent = child
491
+ Log.d("StreamCallPlugin", "removeTouchInterceptor: Found potential parent: ${child.javaClass.simpleName}")
492
+ break
493
+ }
494
+ }
495
+
496
+ // If we didn't find a suitable parent, create a new FrameLayout
497
+ if (newParent == null) {
498
+ Log.d("StreamCallPlugin", "removeTouchInterceptor: No suitable parent found, creating new FrameLayout")
499
+ newParent = FrameLayout(touchWrapper.context).apply {
500
+ layoutParams = touchWrapper.layoutParams
501
+ }
502
+
503
+ // Move all children from touchWrapper to the new parent
504
+ touchWrapper.removeAllViews()
505
+ for (child in childrenToRestore) {
506
+ newParent.addView(child)
507
+ }
508
+ } else {
509
+ // If we found an existing parent, move other children to it
510
+ touchWrapper.removeAllViews()
511
+
512
+ // First add the parent back
513
+ rootParent.addView(newParent, indexInRoot)
514
+
515
+ // Then add other children to this parent if they're not already there
516
+ for (child in childrenToRestore) {
517
+ if (child != newParent && child.parent == null) {
518
+ when (child) {
519
+ is ComposeView -> newParent.addView(child, 0) // Add ComposeView at bottom
520
+ bridge?.webView -> newParent.addView(child) // Add WebView on top
521
+ else -> newParent.addView(child) // Add other views
522
+ }
523
+ }
524
+ }
525
+ }
526
+
527
+ // Remove the touch wrapper from root if not done yet
528
+ if (touchWrapper.parent != null) {
529
+ rootParent.removeView(touchWrapper)
530
+ if (newParent.parent == null) {
531
+ rootParent.addView(newParent, indexInRoot)
532
+ }
533
+ }
534
+
535
+ // Ensure WebView is still visible and has correct background
536
+ bridge?.webView?.let { webView ->
537
+ webView.visibility = View.VISIBLE
538
+ webView.setBackgroundColor(Color.WHITE)
539
+ webView.bringToFront()
540
+ Log.d("StreamCallPlugin", "removeTouchInterceptor: WebView visibility set to VISIBLE, background set to WHITE")
541
+ }
542
+
543
+ touchInterceptWrapper = null
544
+ Log.d("StreamCallPlugin", "Touch interceptor removed successfully")
545
+
546
+ // Log final state
547
+ Log.d("StreamCallPlugin", "removeTouchInterceptor: Final rootParent child count: ${rootParent.childCount}")
548
+ for (i in 0 until rootParent.childCount) {
549
+ val child = rootParent.getChildAt(i)
550
+ Log.d("StreamCallPlugin", "removeTouchInterceptor: Final child $i: ${child.javaClass.simpleName}")
551
+ }
412
552
  }
413
553
 
414
554
  /**
@@ -1101,6 +1241,10 @@ class StreamCallPlugin : Plugin() {
1101
1241
  // Show overlay view with the active call and make webview transparent
1102
1242
  runOnMainThread {
1103
1243
  Log.d("StreamCallPlugin", "internalAcceptCall: Updating UI for active call ${call.id} - setting overlay visible.")
1244
+
1245
+ // Add touch interceptor for the call
1246
+ addTouchInterceptor()
1247
+
1104
1248
  bridge?.webView?.setBackgroundColor(Color.TRANSPARENT) // Make webview transparent
1105
1249
  Log.d("StreamCallPlugin", "internalAcceptCall: WebView background set to transparent for call ${call.id}")
1106
1250
  bridge?.webView?.bringToFront() // Ensure WebView is on top and transparent
@@ -1474,6 +1618,9 @@ class StreamCallPlugin : Plugin() {
1474
1618
  streamCall?.microphone?.setEnabled(true)
1475
1619
  streamCall?.camera?.setEnabled(!isAudioOnly)
1476
1620
 
1621
+ // Add touch interceptor for the call
1622
+ addTouchInterceptor()
1623
+
1477
1624
  bridge?.webView?.setBackgroundColor(Color.TRANSPARENT) // Make webview transparent
1478
1625
  bridge?.webView?.bringToFront() // Ensure WebView is on top and transparent
1479
1626
  setOverlayContent(streamCall)
@@ -1896,26 +2043,39 @@ class StreamCallPlugin : Plugin() {
1896
2043
  }
1897
2044
  }
1898
2045
 
2046
+ // Always clean up the UI regardless of savedCapacitorActivity state
2047
+ Log.d("StreamCallPlugin", "endCallRaw: Cleaning up UI for call $callId")
2048
+ Log.d("StreamCallPlugin", "endCallRaw: WebView visible before: ${bridge?.webView?.visibility}")
2049
+ Log.d("StreamCallPlugin", "endCallRaw: TouchInterceptWrapper exists: ${touchInterceptWrapper != null}")
2050
+
2051
+ setOverlayContent(call)
2052
+ overlayView?.isVisible = false
2053
+ bridge?.webView?.setBackgroundColor(Color.WHITE) // Restore webview opacity
2054
+ bridge?.webView?.visibility = View.VISIBLE // Ensure WebView is visible
2055
+
2056
+ // Remove touch interceptor
2057
+ if (touchInterceptWrapper != null) {
2058
+ Log.d("StreamCallPlugin", "endCallRaw: Removing touch interceptor")
2059
+ removeTouchInterceptor()
2060
+ } else {
2061
+ Log.d("StreamCallPlugin", "endCallRaw: No touch interceptor to remove")
2062
+ }
2063
+
2064
+ // Also hide incoming call view if visible
2065
+ Log.d("StreamCallPlugin", "endCallRaw: Hiding incoming call view for call $callId")
2066
+ // No dedicated incoming-call native view anymore; UI handled by web layer
2067
+
2068
+ Log.d("StreamCallPlugin", "endCallRaw: WebView visible after: ${bridge?.webView?.visibility}")
2069
+
1899
2070
  val savedCapacitorActivity = savedActivity
1900
2071
  if (savedCapacitorActivity != null) {
1901
-
1902
2072
  if (savedActivityPaused) {
1903
- Log.d("StreamCallPlugin", "Activity is paused. Adding call ${call.id} to savedCallsToEndOnResume")
2073
+ Log.d("StreamCallPlugin", "endCallRaw: Activity is paused. Adding call ${call.id} to savedCallsToEndOnResume")
1904
2074
  savedCallsToEndOnResume.add(call)
1905
2075
  } else {
1906
2076
  transEndCallRaw(call)
1907
2077
  }
1908
-
1909
- return@runOnMainThread
1910
2078
  }
1911
-
1912
- setOverlayContent(call)
1913
- overlayView?.isVisible = false
1914
- bridge?.webView?.setBackgroundColor(Color.WHITE) // Restore webview opacity
1915
-
1916
- // Also hide incoming call view if visible
1917
- Log.d("StreamCallPlugin", "Hiding incoming call view for call $callId")
1918
- // No dedicated incoming-call native view anymore; UI handled by web layer
1919
2079
  }
1920
2080
 
1921
2081
  // Notify that call has ended using helper
@@ -2176,10 +2336,30 @@ class StreamCallPlugin : Plugin() {
2176
2336
 
2177
2337
  // Hide UI elements directly without setting content
2178
2338
  runOnMainThread {
2179
- Log.d("StreamCallPlugin", "Hiding UI elements for call $callCid (one-time cleanup)")
2339
+ Log.d("StreamCallPlugin", "cleanupCall: Hiding UI elements for call $callCid")
2340
+
2341
+ // Log current state before cleanup
2342
+ Log.d("StreamCallPlugin", "cleanupCall: OverlayView visible: ${overlayView?.isVisible}")
2343
+ Log.d("StreamCallPlugin", "cleanupCall: WebView visible: ${bridge?.webView?.visibility}")
2344
+ Log.d("StreamCallPlugin", "cleanupCall: TouchInterceptWrapper exists: ${touchInterceptWrapper != null}")
2345
+
2180
2346
  overlayView?.isVisible = false
2347
+ bridge?.webView?.setBackgroundColor(Color.WHITE) // Restore webview opacity
2348
+ bridge?.webView?.visibility = View.VISIBLE // Ensure WebView is visible
2349
+
2350
+ // Remove touch interceptor if it exists
2351
+ if (touchInterceptWrapper != null) {
2352
+ Log.d("StreamCallPlugin", "cleanupCall: Removing touch interceptor")
2353
+ removeTouchInterceptor()
2354
+ } else {
2355
+ Log.d("StreamCallPlugin", "cleanupCall: No touch interceptor to remove")
2356
+ }
2357
+
2181
2358
  // here we will also make sure we don't show on lock screen
2182
2359
  changeActivityAsVisibleOnLockScreen(this.activity, false)
2360
+
2361
+ // Log final state after cleanup
2362
+ Log.d("StreamCallPlugin", "cleanupCall: Cleanup complete. WebView visible: ${bridge?.webView?.visibility}")
2183
2363
  }
2184
2364
 
2185
2365
  Log.d("StreamCallPlugin", "Cleaned up resources for ended call: $callCid")
@@ -245,9 +245,6 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
245
245
  print("- In call state detected")
246
246
  print("- All participants: \(String(describing: viewModel.participants))")
247
247
 
248
- // Enable touch interceptor when call becomes active
249
- self.touchInterceptView?.setCallActive(true)
250
-
251
248
  // Create/update overlay and make visible when there's an active call
252
249
  self.createCallOverlayView()
253
250
 
@@ -303,9 +300,6 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
303
300
  } else if newState == .idle {
304
301
  print("Call state changed to idle. CurrentCallId: \(self.currentCallId), ActiveCall: \(String(describing: self.streamVideo?.state.activeCall?.cId))")
305
302
 
306
- // Disable touch interceptor when call becomes inactive
307
- self.touchInterceptView?.setCallActive(false)
308
-
309
303
  // Only notify about call ending if we have a valid stored call ID and there's truly no active call
310
304
  // This prevents false "left" events during normal state transitions
311
305
  if !self.currentCallId.isEmpty && self.streamVideo?.state.activeCall == nil {
@@ -433,9 +427,11 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
433
427
  Task { @MainActor in
434
428
  // self.overlayViewModel?.updateCall(nil)
435
429
  // self.overlayViewModel?.updateStreamVideo(nil)
436
- self.touchInterceptView?.setCallActive(false)
437
430
  self.overlayView?.isHidden = true
438
431
  self.webView?.isOpaque = true
432
+
433
+ // Remove touch interceptor if it exists
434
+ self.removeTouchInterceptor()
439
435
  }
440
436
 
441
437
  call.resolve([
@@ -604,6 +600,9 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
604
600
 
605
601
  // Update UI on main thread
606
602
  await MainActor.run {
603
+ // Add touch interceptor for the call
604
+ self.addTouchInterceptor()
605
+
607
606
  // self.overlayViewModel?.updateCall(streamCall)
608
607
  self.overlayView?.isHidden = false
609
608
  self.webView?.isOpaque = false
@@ -671,9 +670,11 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
671
670
  }
672
671
 
673
672
  await MainActor.run {
674
- self.touchInterceptView?.setCallActive(false)
675
673
  self.overlayView?.isHidden = true
676
674
  self.webView?.isOpaque = true
675
+
676
+ // Remove touch interceptor
677
+ self.removeTouchInterceptor()
677
678
  }
678
679
 
679
680
  call.resolve([
@@ -700,9 +701,11 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
700
701
  await callViewModel?.hangUp()
701
702
 
702
703
  await MainActor.run {
703
- self.touchInterceptView?.setCallActive(false)
704
704
  self.overlayView?.isHidden = true
705
705
  self.webView?.isOpaque = true
706
+
707
+ // Remove touch interceptor
708
+ self.removeTouchInterceptor()
706
709
  }
707
710
 
708
711
  call.resolve([
@@ -809,6 +812,9 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
809
812
 
810
813
  // Update the CallOverlayView with the active call
811
814
  await MainActor.run {
815
+ // Add touch interceptor for the call
816
+ self.addTouchInterceptor()
817
+
812
818
  // self.overlayViewModel?.updateCall(streamCall)
813
819
  self.overlayView?.isHidden = false
814
820
  self.webView?.isOpaque = false
@@ -900,12 +906,6 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
900
906
  private func setupViews() {
901
907
  guard let webView = self.webView, let parent = webView.superview else { return }
902
908
 
903
- // Create the touch intercept view as an overlay for touch passthrough
904
- let touchInterceptView = TouchInterceptView(frame: parent.bounds)
905
- touchInterceptView.translatesAutoresizingMaskIntoConstraints = false
906
- touchInterceptView.backgroundColor = .clear
907
- touchInterceptView.isOpaque = false
908
-
909
909
  // Create SwiftUI view with view model if not already created
910
910
  if self.overlayView == nil, let callViewModel = self.callViewModel {
911
911
  let hostingController = UIHostingController(rootView: CallOverlayView(viewModel: callViewModel))
@@ -930,16 +930,34 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
930
930
  ])
931
931
  }
932
932
  }
933
+ }
934
+
935
+ private func addTouchInterceptor() {
936
+ guard let webView = self.webView, let parent = webView.superview else { return }
937
+
938
+ // Check if touch interceptor already exists
939
+ if self.touchInterceptView != nil {
940
+ print("Touch interceptor already exists, skipping creation")
941
+ return
942
+ }
943
+
944
+ // Create the touch intercept view as an overlay for touch passthrough
945
+ let touchInterceptView = TouchInterceptView(frame: parent.bounds)
946
+ touchInterceptView.translatesAutoresizingMaskIntoConstraints = false
947
+ touchInterceptView.backgroundColor = .clear
948
+ touchInterceptView.isOpaque = false
933
949
 
934
950
  // Setup touch intercept view with references to webview and overlay
935
951
  if let overlayView = self.overlayView {
936
952
  touchInterceptView.setupWithWebView(webView, overlayView: overlayView)
937
- // Insert touchInterceptView above webView
938
- parent.insertSubview(touchInterceptView, aboveSubview: webView)
953
+ // Add touchInterceptView as the topmost view
954
+ parent.addSubview(touchInterceptView)
955
+ parent.bringSubviewToFront(touchInterceptView)
939
956
  } else {
940
957
  // If overlayView is not present, just add on top of webView
941
958
  touchInterceptView.setupWithWebView(webView, overlayView: webView)
942
- parent.insertSubview(touchInterceptView, aboveSubview: webView)
959
+ parent.addSubview(touchInterceptView)
960
+ parent.bringSubviewToFront(touchInterceptView)
943
961
  }
944
962
 
945
963
  // Set up active call check function
@@ -957,6 +975,18 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
957
975
  touchInterceptView.leadingAnchor.constraint(equalTo: parent.leadingAnchor),
958
976
  touchInterceptView.trailingAnchor.constraint(equalTo: parent.trailingAnchor)
959
977
  ])
978
+
979
+ print("Touch interceptor added for active call - view hierarchy: \(parent.subviews.map { type(of: $0) })")
980
+ }
981
+
982
+ private func removeTouchInterceptor() {
983
+ guard let touchInterceptView = self.touchInterceptView else { return }
984
+
985
+ // Remove touch interceptor from view hierarchy
986
+ touchInterceptView.removeFromSuperview()
987
+ self.touchInterceptView = nil
988
+
989
+ print("Touch interceptor removed after call ended")
960
990
  }
961
991
 
962
992
  private func createCallOverlayView() {
@@ -1004,43 +1034,8 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
1004
1034
  self.hostingController = overlayView
1005
1035
  self.overlayView = overlayView.view
1006
1036
 
1007
- // Ensure touch intercept view is on top
1008
- if let touchInterceptView = parent.subviews.first(where: { $0 is TouchInterceptView }) {
1009
- parent.bringSubviewToFront(touchInterceptView)
1010
- // Update reference and set call active
1011
- self.touchInterceptView = touchInterceptView as? TouchInterceptView
1012
-
1013
- // Set up active call check function
1014
- self.touchInterceptView?.setActiveCallCheck { [weak self] in
1015
- return self?.streamVideo?.state.activeCall != nil
1016
- }
1017
-
1018
- self.touchInterceptView?.setCallActive(true)
1019
- } else {
1020
- // Create touch intercept view if not already created
1021
- let touchInterceptView = TouchInterceptView(frame: parent.bounds)
1022
- touchInterceptView.translatesAutoresizingMaskIntoConstraints = false
1023
- touchInterceptView.backgroundColor = .clear
1024
- touchInterceptView.isOpaque = false
1025
- touchInterceptView.setupWithWebView(webView, overlayView: overlayView.view)
1026
- parent.addSubview(touchInterceptView)
1027
-
1028
- // Set up active call check function
1029
- touchInterceptView.setActiveCallCheck { [weak self] in
1030
- return self?.streamVideo?.state.activeCall != nil
1031
- }
1032
-
1033
- // Store reference and set call active
1034
- self.touchInterceptView = touchInterceptView
1035
- self.touchInterceptView?.setCallActive(true)
1036
-
1037
- NSLayoutConstraint.activate([
1038
- touchInterceptView.topAnchor.constraint(equalTo: parent.topAnchor),
1039
- touchInterceptView.bottomAnchor.constraint(equalTo: parent.bottomAnchor),
1040
- touchInterceptView.leadingAnchor.constraint(equalTo: parent.leadingAnchor),
1041
- touchInterceptView.trailingAnchor.constraint(equalTo: parent.trailingAnchor)
1042
- ])
1043
- }
1037
+ // Add touch interceptor if not already present
1038
+ self.addTouchInterceptor()
1044
1039
  }
1045
1040
 
1046
1041
  // MARK: - Dynamic API Key Management
@@ -1066,9 +1061,6 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
1066
1061
  }
1067
1062
 
1068
1063
  func ensureViewRemoved() {
1069
- // Disable touch interceptor when overlay is removed
1070
- self.touchInterceptView?.setCallActive(false)
1071
-
1072
1064
  // Check if we have an overlay view
1073
1065
  if let existingOverlayView = self.overlayView {
1074
1066
  print("Hiding call overlay view")
@@ -1083,6 +1075,9 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
1083
1075
  } else {
1084
1076
  print("No call overlay view to hide")
1085
1077
  }
1078
+
1079
+ // Remove touch interceptor
1080
+ self.removeTouchInterceptor()
1086
1081
  }
1087
1082
 
1088
1083
  @objc func getCallStatus(_ call: CAPPluginCall) {
@@ -9,7 +9,6 @@ class TouchInterceptView: UIView {
9
9
  private var lastTouchPoint: CGPoint?
10
10
  private let touchThreshold: CGFloat = 5.0 // pixels
11
11
  private let timerDelay: TimeInterval = 0.1 // seconds
12
- private var isCallActive: Bool = false
13
12
  private var hasActiveCallCheck: (() -> Bool)?
14
13
 
15
14
  func setupWithWebView(_ webView: UIView, overlayView: UIView) {
@@ -26,28 +25,10 @@ class TouchInterceptView: UIView {
26
25
  self.hasActiveCallCheck = check
27
26
  }
28
27
 
29
- func setCallActive(_ active: Bool) {
30
- self.isCallActive = active
31
- // os_log(.debug, "TouchInterceptView: setCallActive - %{public}s", String(describing: active))
32
-
33
- // Cancel any pending timer when call becomes inactive
34
- if !active {
35
- forwardTimer?.invalidate()
36
- forwardTimer = nil
37
- lastTouchPoint = nil
38
- }
39
- }
40
-
41
28
  private func shouldInterceptTouches() -> Bool {
42
- // Check both our flag and actual call state
29
+ // Check if there's an active call
43
30
  let hasActiveCall = hasActiveCallCheck?() ?? false
44
- let shouldIntercept = isCallActive && hasActiveCall
45
-
46
- if isCallActive != hasActiveCall {
47
- // os_log(.debug, "TouchInterceptView: State mismatch - isCallActive: %{public}s, hasActiveCall: %{public}s", String(describing: isCallActive), String(describing: hasActiveCall))
48
- }
49
-
50
- return shouldIntercept
31
+ return hasActiveCall
51
32
  }
52
33
 
53
34
  private func isInteractive(_ view: UIView) -> Bool {
@@ -130,16 +111,21 @@ class TouchInterceptView: UIView {
130
111
 
131
112
  // Check if we should intercept touches
132
113
  if !shouldInterceptTouches() {
114
+ // Cancel any pending timer when not in a call
115
+ forwardTimer?.invalidate()
116
+ forwardTimer = nil
117
+ lastTouchPoint = nil
118
+
133
119
  if let webView = self.webView {
134
120
  let webPoint = self.convert(point, to: webView)
135
121
  let result = webView.hitTest(webPoint, with: event)
136
- // os_log(.debug, "TouchInterceptView: hitTest - Not intercepting, direct WebView result %{public}s at %{public}s", String(describing: result), String(describing: webPoint))
122
+ print("TouchInterceptView: hitTest - Not intercepting (no active call), direct WebView result \(String(describing: result)) at \(webPoint)")
137
123
  return result
138
124
  }
139
125
  return nil
140
126
  }
141
127
 
142
- // os_log(.debug, "TouchInterceptView: hitTest entry at %{public}s, callActive: %{public}s", String(describing: point), String(describing: isCallActive))
128
+ print("TouchInterceptView: hitTest intercepting touch at \(point)")
143
129
 
144
130
  // Check if this is same touch location continuing
145
131
  if let lastPoint = lastTouchPoint {
@@ -167,7 +153,7 @@ class TouchInterceptView: UIView {
167
153
  if let overlayView = self.overlayView, !overlayView.isHidden {
168
154
  let overlayPoint = self.convert(point, to: overlayView)
169
155
  if let overlayHit = nonGreedyInteractiveHitTest(in: overlayView, point: overlayPoint, with: event) {
170
- // os_log(.debug, "TouchInterceptView: hitTest - Overlay view %{public}s at %{public}s", String(describing: overlayHit), String(describing: overlayPoint))
156
+ print("TouchInterceptView: hitTest - Hit overlay view \(String(describing: overlayHit)) at \(overlayPoint)")
171
157
  return overlayHit
172
158
  }
173
159
  }
@@ -175,10 +161,10 @@ class TouchInterceptView: UIView {
175
161
  if let webView = self.webView {
176
162
  let webPoint = self.convert(point, to: webView)
177
163
  let result = webView.hitTest(webPoint, with: event)
178
- // os_log(.debug, "TouchInterceptView: hitTest - WebView result %{public}s at %{public}s", String(describing: result), String(describing: webPoint))
164
+ print("TouchInterceptView: hitTest - WebView fallback result \(String(describing: result)) at \(webPoint)")
179
165
  return result
180
166
  }
181
- // os_log(.debug, "TouchInterceptView: hitTest - No view found for %{public}s", String(describing: point))
167
+ print("TouchInterceptView: hitTest - No view found for \(point)")
182
168
  return nil
183
169
  }
184
170
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-stream-call",
3
- "version": "7.0.1",
3
+ "version": "7.0.3",
4
4
  "description": "Uses the https://getstream.io/ SDK to implement calling in Capacitor",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",