@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.
- package/CapgoCapacitorStreamCall.podspec +2 -2
- package/Package.swift +1 -1
- package/android/build.gradle +2 -2
- package/android/src/main/java/ee/forgr/capacitor/streamcall/StreamCallPlugin.kt +208 -28
- package/ios/Sources/StreamCallPlugin/StreamCallPlugin.swift +53 -58
- package/ios/Sources/StreamCallPlugin/TouchInterceptView.swift +12 -26
- package/package.json +1 -1
|
@@ -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.
|
|
17
|
-
s.dependency 'StreamVideoSwiftUI', '1.29.
|
|
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.
|
|
14
|
+
.package(url: "https://github.com/GetStream/stream-video-swift.git", exact: "1.29.1")
|
|
15
15
|
],
|
|
16
16
|
targets: [
|
|
17
17
|
.target(
|
package/android/build.gradle
CHANGED
|
@@ -77,8 +77,8 @@ dependencies {
|
|
|
77
77
|
|
|
78
78
|
|
|
79
79
|
// Stream dependencies
|
|
80
|
-
implementation("io.getstream:stream-video-android-ui-compose:1.
|
|
81
|
-
implementation("io.getstream:stream-video-android-core:1.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
//
|
|
938
|
-
parent.
|
|
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.
|
|
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
|
-
//
|
|
1008
|
-
|
|
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
|
|
29
|
+
// Check if there's an active call
|
|
43
30
|
let hasActiveCall = hasActiveCallCheck?() ?? false
|
|
44
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
164
|
+
print("TouchInterceptView: hitTest - WebView fallback result \(String(describing: result)) at \(webPoint)")
|
|
179
165
|
return result
|
|
180
166
|
}
|
|
181
|
-
|
|
167
|
+
print("TouchInterceptView: hitTest - No view found for \(point)")
|
|
182
168
|
return nil
|
|
183
169
|
}
|
|
184
170
|
|
package/package.json
CHANGED