@capgo/capacitor-stream-call 0.0.56 → 0.0.62

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.
@@ -75,8 +75,8 @@ dependencies {
75
75
  implementation "androidx.compose.material3:material3:1.3.2"
76
76
 
77
77
  // Stream dependencies
78
- implementation("io.getstream:stream-video-android-ui-compose:1.6.1")
79
- implementation("io.getstream:stream-video-android-core:1.6.1")
78
+ implementation("io.getstream:stream-video-android-ui-compose:1.6.3")
79
+ implementation("io.getstream:stream-video-android-core:1.6.3")
80
80
  implementation("io.getstream:stream-android-push:1.3.1")
81
81
  implementation("io.getstream:stream-android-push-firebase:1.3.1")
82
82
 
@@ -13,7 +13,8 @@ import io.getstream.video.android.core.RingingState
13
13
  import io.getstream.video.android.core.notifications.DefaultNotificationHandler
14
14
  import io.getstream.video.android.core.notifications.NotificationHandler
15
15
  import io.getstream.video.android.model.StreamCallId
16
-
16
+ import io.getstream.video.android.model.streamCallId
17
+
17
18
  // declare "incoming_calls_custom" as a constant
18
19
  const val INCOMING_CALLS_CUSTOM = "incoming_calls_custom"
19
20
 
@@ -76,7 +77,7 @@ class CustomNotificationHandler(
76
77
  }
77
78
  acceptCallIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
78
79
 
79
- Log.d("CustomNotificationHandler", "Constructed Accept Call Intent for PI: action=${acceptCallIntent.action}, cid=${acceptCallIntent.getStringExtra(NotificationHandler.INTENT_EXTRA_CALL_CID)}, package=${acceptCallIntent.getPackage()}, component=${acceptCallIntent.component?.flattenToString()}, flags=${acceptCallIntent.flags}")
80
+ Log.d("CustomNotificationHandler", "Constructed Accept Call Intent for PI: action=${acceptCallIntent.action}, cid=${acceptCallIntent.streamCallId(NotificationHandler.INTENT_EXTRA_CALL_CID)}, package=${acceptCallIntent.getPackage()}, component=${acceptCallIntent.component?.flattenToString()}, flags=${acceptCallIntent.flags}")
80
81
 
81
82
  // Create PendingIntent for Accept action using getActivity to launch the app
82
83
  val requestCodeAccept = callId.cid.hashCode() + 1 // Unique request code for the PendingIntent with offset to avoid collisions
@@ -150,7 +151,7 @@ class CustomNotificationHandler(
150
151
  )
151
152
  }
152
153
 
153
- fun buildNotification(
154
+ private fun buildNotification(
154
155
  fullScreenPendingIntent: PendingIntent,
155
156
  acceptCallPendingIntent: PendingIntent,
156
157
  rejectCallPendingIntent: PendingIntent,
@@ -31,26 +31,26 @@ class RingtonePlayer(
31
31
 
32
32
  fun pauseRinging() {
33
33
  Log.d("RingtonePlayer", "Pause ringing")
34
- try {
35
- if (!isStopped) {
36
- mediaPlayer?.pause()
37
- isPaused = true
38
- }
39
- } catch (e: Exception) {
40
- Log.e("RingtonePlayer", "Error pausing ringtone: ${e.message}")
41
- }
34
+ // try {
35
+ // if (!isStopped) {
36
+ // mediaPlayer?.pause()
37
+ // isPaused = true
38
+ // }
39
+ // } catch (e: Exception) {
40
+ // Log.e("RingtonePlayer", "Error pausing ringtone: ${e.message}")
41
+ // }
42
42
  }
43
43
 
44
44
  fun resumeRinging() {
45
45
  Log.d("RingtonePlayer", "Resume ringing")
46
- try {
47
- if (!isStopped && isPaused) {
48
- mediaPlayer?.start()
49
- isPaused = false
50
- }
51
- } catch (e: Exception) {
52
- Log.e("RingtonePlayer", "Error resuming ringtone: ${e.message}")
53
- }
46
+ // try {
47
+ // if (!isStopped && isPaused) {
48
+ // mediaPlayer?.start()
49
+ // isPaused = false
50
+ // }
51
+ // } catch (e: Exception) {
52
+ // Log.e("RingtonePlayer", "Error resuming ringtone: ${e.message}")
53
+ // }
54
54
  }
55
55
 
56
56
  fun isPaused(): Boolean {
@@ -59,103 +59,103 @@ class RingtonePlayer(
59
59
 
60
60
  fun startRinging() {
61
61
  Log.d("RingtonePlayer", "Start ringing")
62
- try {
63
- isStopped = false
64
- isPaused = false
65
- if (mediaPlayer == null) {
66
- val uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
67
- mediaPlayer = MediaPlayer().apply {
68
- setDataSource(application, uri)
69
- isLooping = true
70
- setAudioAttributes(
71
- AudioAttributes.Builder()
72
- .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
73
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
74
- .build()
75
- )
76
- prepare()
77
- }
78
- }
79
-
80
- val notificationManager = application.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
81
- val notifs = notificationManager.activeNotifications.toList()
82
- var notificationTime = 0L
83
-
84
- for (notification in notifs) {
85
- // First check if it's our notification
86
- val isOurs = isOurNotification(notification)
87
-
88
- // Only proceed with ringtone if it's our notification
89
- if (!isOurs) {
90
- Log.d("RingtonePlayer", "Skipping notification as it's not our incoming call notification")
91
- continue
92
- }
93
-
94
- // Cancel our notification
95
- try {
96
- Log.d("RingtonePlayer", "Canceling notification/service with id: ${notification.id}")
97
- this.cancelIncomingCallService()
98
- } catch (e: Exception) {
99
- Log.e("RingtonePlayer", "Error cancelling notification: ${e.message}")
100
- }
101
- notificationTime = notification.postTime
102
- }
103
-
104
- if (notificationTime > 0) {
105
- val currentTime = System.currentTimeMillis()
106
- val elapsedTime = currentTime - notificationTime
107
-
108
- // Only start playing if we're within the ringtone duration
109
- if (elapsedTime < DEFAULT_RINGTONE_DURATION) {
110
- // Get the ringtone duration
111
- val ringtoneDuration = mediaPlayer?.duration?.toLong() ?: DEFAULT_RINGTONE_DURATION
112
-
113
- // Calculate the position to seek to
114
- val seekPosition = (elapsedTime % ringtoneDuration).toInt()
115
- Log.d("RingtonePlayer", "Seeking to position: $seekPosition ms in ringtone")
116
-
117
- mediaPlayer?.seekTo(seekPosition)
118
- mediaPlayer?.start()
119
-
120
- // Schedule stop at the remaining duration
121
- val remainingDuration = DEFAULT_RINGTONE_DURATION - elapsedTime
122
- stopRingtoneRunnable = Runnable { stopRinging() }
123
- handler.postDelayed(stopRingtoneRunnable!!, remainingDuration)
124
-
125
- Log.d("RingtonePlayer", "Starting ringtone with offset: $elapsedTime ms, will play for $remainingDuration ms")
126
- } else {
127
- Log.d("RingtonePlayer", "Not starting ringtone as elapsed time ($elapsedTime ms) exceeds duration")
128
- }
129
- } else {
130
- // If no notification time found, just play normally
131
- mediaPlayer?.start()
132
-
133
- // Schedule stop at the default duration
134
- stopRingtoneRunnable = Runnable { stopRinging() }
135
- handler.postDelayed(stopRingtoneRunnable!!, DEFAULT_RINGTONE_DURATION)
136
-
137
- Log.d("RingtonePlayer", "Starting ringtone with default duration")
138
- }
139
- } catch (e: Exception) {
140
- Log.e("RingtonePlayer", "Error playing ringtone: ${e.message}")
141
- }
62
+ // try {
63
+ // isStopped = false
64
+ // isPaused = false
65
+ // if (mediaPlayer == null) {
66
+ // val uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
67
+ // mediaPlayer = MediaPlayer().apply {
68
+ // setDataSource(application, uri)
69
+ // isLooping = true
70
+ // setAudioAttributes(
71
+ // AudioAttributes.Builder()
72
+ // .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
73
+ // .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
74
+ // .build()
75
+ // )
76
+ // prepare()
77
+ // }
78
+ // }
79
+ //
80
+ // val notificationManager = application.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
81
+ // val notifs = notificationManager.activeNotifications.toList()
82
+ // var notificationTime = 0L
83
+ //
84
+ // for (notification in notifs) {
85
+ // // First check if it's our notification
86
+ // val isOurs = isOurNotification(notification)
87
+ //
88
+ // // Only proceed with ringtone if it's our notification
89
+ // if (!isOurs) {
90
+ // Log.d("RingtonePlayer", "Skipping notification as it's not our incoming call notification")
91
+ // continue
92
+ // }
93
+ //
94
+ // // Cancel our notification
95
+ // try {
96
+ // Log.d("RingtonePlayer", "Canceling notification/service with id: ${notification.id}")
97
+ // this.cancelIncomingCallService()
98
+ // } catch (e: Exception) {
99
+ // Log.e("RingtonePlayer", "Error cancelling notification: ${e.message}")
100
+ // }
101
+ // notificationTime = notification.postTime
102
+ // }
103
+ //
104
+ // if (notificationTime > 0) {
105
+ // val currentTime = System.currentTimeMillis()
106
+ // val elapsedTime = currentTime - notificationTime
107
+ //
108
+ // // Only start playing if we're within the ringtone duration
109
+ // if (elapsedTime < DEFAULT_RINGTONE_DURATION) {
110
+ // // Get the ringtone duration
111
+ // val ringtoneDuration = mediaPlayer?.duration?.toLong() ?: DEFAULT_RINGTONE_DURATION
112
+ //
113
+ // // Calculate the position to seek to
114
+ // val seekPosition = (elapsedTime % ringtoneDuration).toInt()
115
+ // Log.d("RingtonePlayer", "Seeking to position: $seekPosition ms in ringtone")
116
+ //
117
+ // mediaPlayer?.seekTo(seekPosition)
118
+ // mediaPlayer?.start()
119
+ //
120
+ // // Schedule stop at the remaining duration
121
+ // val remainingDuration = DEFAULT_RINGTONE_DURATION - elapsedTime
122
+ // stopRingtoneRunnable = Runnable { stopRinging() }
123
+ // handler.postDelayed(stopRingtoneRunnable!!, remainingDuration)
124
+ //
125
+ // Log.d("RingtonePlayer", "Starting ringtone with offset: $elapsedTime ms, will play for $remainingDuration ms")
126
+ // } else {
127
+ // Log.d("RingtonePlayer", "Not starting ringtone as elapsed time ($elapsedTime ms) exceeds duration")
128
+ // }
129
+ // } else {
130
+ // // If no notification time found, just play normally
131
+ // mediaPlayer?.start()
132
+ //
133
+ // // Schedule stop at the default duration
134
+ // stopRingtoneRunnable = Runnable { stopRinging() }
135
+ // handler.postDelayed(stopRingtoneRunnable!!, DEFAULT_RINGTONE_DURATION)
136
+ //
137
+ // Log.d("RingtonePlayer", "Starting ringtone with default duration")
138
+ // }
139
+ // } catch (e: Exception) {
140
+ // Log.e("RingtonePlayer", "Error playing ringtone: ${e.message}")
141
+ // }
142
142
  }
143
143
 
144
144
  fun stopRinging() {
145
145
  Log.d("RingtonePlayer", "Stop ringing")
146
- try {
147
- isStopped = true
148
- isPaused = false
149
- stopRingtoneRunnable?.let { handler.removeCallbacks(it) }
150
- stopRingtoneRunnable = null
151
-
152
- mediaPlayer?.stop()
153
- mediaPlayer?.reset()
154
- mediaPlayer?.release()
155
- mediaPlayer = null
156
- } catch (e: Exception) {
157
- Log.e("RingtonePlayer", "Error stopping ringtone: ${e.message}")
158
- }
146
+ // try {
147
+ // isStopped = true
148
+ // isPaused = false
149
+ // stopRingtoneRunnable?.let { handler.removeCallbacks(it) }
150
+ // stopRingtoneRunnable = null
151
+ //
152
+ // mediaPlayer?.stop()
153
+ // mediaPlayer?.reset()
154
+ // mediaPlayer?.release()
155
+ // mediaPlayer = null
156
+ // } catch (e: Exception) {
157
+ // Log.e("RingtonePlayer", "Error stopping ringtone: ${e.message}")
158
+ // }
159
159
  }
160
160
 
161
161
 
@@ -1,45 +1,42 @@
1
1
  package ee.forgr.capacitor.streamcall
2
2
 
3
3
  import TouchInterceptWrapper
4
+ import android.Manifest
4
5
  import android.app.Activity
5
6
  import android.app.Application
6
7
  import android.app.KeyguardManager
8
+ import android.content.BroadcastReceiver
7
9
  import android.content.Context
10
+ import android.content.Intent
11
+ import android.content.IntentFilter
12
+ import android.content.pm.PackageManager
8
13
  import android.graphics.Color
9
14
  import android.media.RingtoneManager
10
15
  import android.net.Uri
16
+ import android.os.Build
11
17
  import android.os.Bundle
12
18
  import android.os.Handler
13
19
  import android.os.Looper
14
20
  import android.view.View
15
21
  import android.view.ViewGroup
22
+ import android.view.WindowManager
16
23
  import android.widget.FrameLayout
24
+ import androidx.compose.runtime.collectAsState
17
25
  import androidx.compose.ui.platform.ComposeView
26
+ import androidx.core.app.ActivityCompat
27
+ import androidx.core.content.ContextCompat
18
28
  import androidx.core.view.isVisible
19
29
  import com.getcapacitor.BridgeActivity
30
+ import com.getcapacitor.JSArray
20
31
  import com.getcapacitor.JSObject
21
32
  import com.getcapacitor.Plugin
22
33
  import com.getcapacitor.PluginCall
23
34
  import com.getcapacitor.PluginMethod
24
35
  import com.getcapacitor.annotation.CapacitorPlugin
25
- import io.getstream.android.push.permissions.ActivityLifecycleCallbacks
26
- import io.getstream.video.android.core.Call
27
- import io.getstream.video.android.core.GEO
28
- import io.getstream.video.android.core.StreamVideo
29
- import io.getstream.video.android.core.StreamVideoBuilder
30
- import io.getstream.video.android.core.notifications.NotificationConfig
31
- import io.getstream.video.android.core.notifications.NotificationHandler
32
- import io.getstream.video.android.core.sounds.toSounds
33
- import io.getstream.video.android.model.StreamCallId
34
- import io.getstream.video.android.model.User
35
- import io.getstream.video.android.model.streamCallId
36
- import kotlinx.coroutines.DelicateCoroutinesApi
37
- import kotlinx.coroutines.launch
38
- import io.getstream.video.android.model.Device
39
- import kotlinx.coroutines.tasks.await
40
36
  import com.google.firebase.messaging.FirebaseMessaging
41
37
  import io.getstream.android.push.PushProvider
42
38
  import io.getstream.android.push.firebase.FirebasePushDeviceGenerator
39
+ import io.getstream.android.push.permissions.ActivityLifecycleCallbacks
43
40
  import io.getstream.android.video.generated.models.CallAcceptedEvent
44
41
  import io.getstream.android.video.generated.models.CallCreatedEvent
45
42
  import io.getstream.android.video.generated.models.CallEndedEvent
@@ -47,26 +44,36 @@ import io.getstream.android.video.generated.models.CallMissedEvent
47
44
  import io.getstream.android.video.generated.models.CallRejectedEvent
48
45
  import io.getstream.android.video.generated.models.CallRingEvent
49
46
  import io.getstream.android.video.generated.models.CallSessionEndedEvent
47
+ import io.getstream.android.video.generated.models.CallSessionParticipantLeftEvent
50
48
  import io.getstream.android.video.generated.models.CallSessionStartedEvent
51
- import io.getstream.video.android.core.sounds.RingingConfig
52
- import kotlinx.coroutines.CoroutineScope
53
- import kotlinx.coroutines.Dispatchers
54
- import android.Manifest
55
- import android.content.pm.PackageManager
56
- import androidx.core.app.ActivityCompat
57
- import androidx.core.content.ContextCompat
58
49
  import io.getstream.android.video.generated.models.VideoEvent
50
+ import io.getstream.log.Priority
59
51
  import io.getstream.video.android.compose.theme.VideoTheme
60
52
  import io.getstream.video.android.compose.ui.components.call.activecall.CallContent
61
- import androidx.compose.runtime.collectAsState
53
+ import io.getstream.video.android.core.Call
62
54
  import io.getstream.video.android.core.CameraDirection
63
- import android.content.BroadcastReceiver
64
- import android.content.Intent
65
- import android.content.IntentFilter
66
- import com.getcapacitor.JSArray
67
- import io.getstream.android.video.generated.models.CallSessionParticipantLeftEvent
55
+ import io.getstream.video.android.core.GEO
68
56
  import io.getstream.video.android.core.RealtimeConnection
57
+ import io.getstream.video.android.core.StreamVideo
58
+ import io.getstream.video.android.core.StreamVideoBuilder
59
+ import io.getstream.video.android.core.call.CallType
69
60
  import io.getstream.video.android.core.events.ParticipantLeftEvent
61
+ import io.getstream.video.android.core.logging.LoggingLevel
62
+ import io.getstream.video.android.core.notifications.NotificationConfig
63
+ import io.getstream.video.android.core.notifications.NotificationHandler
64
+ import io.getstream.video.android.core.notifications.internal.service.CallServiceConfigRegistry
65
+ import io.getstream.video.android.core.notifications.internal.service.DefaultCallConfigurations
66
+ import io.getstream.video.android.core.sounds.RingingConfig
67
+ import io.getstream.video.android.core.sounds.toSounds
68
+ import io.getstream.video.android.model.Device
69
+ import io.getstream.video.android.model.StreamCallId
70
+ import io.getstream.video.android.model.User
71
+ import io.getstream.video.android.model.streamCallId
72
+ import kotlinx.coroutines.CoroutineScope
73
+ import kotlinx.coroutines.DelicateCoroutinesApi
74
+ import kotlinx.coroutines.Dispatchers
75
+ import kotlinx.coroutines.launch
76
+ import kotlinx.coroutines.tasks.await
70
77
 
71
78
  // I am not a religious pearson, but at this point, I am not sure even god himself would understand this code
72
79
  // It's a spaghetti-like, tangled, unreadable mess and frankly, I am deeply sorry for the code crimes commited in the Android impl
@@ -76,7 +83,6 @@ public class StreamCallPlugin : Plugin() {
76
83
  private var state: State = State.NOT_INITIALIZED
77
84
  private var overlayView: ComposeView? = null
78
85
  private var barrierView: View? = null
79
- private var ringtonePlayer: RingtonePlayer? = null
80
86
  private val mainHandler = Handler(Looper.getMainLooper())
81
87
  private var savedContext: Context? = null
82
88
  private var bootedToHandleCall: Boolean = false
@@ -112,62 +118,15 @@ public class StreamCallPlugin : Plugin() {
112
118
  }
113
119
 
114
120
  override fun handleOnPause() {
115
- this.ringtonePlayer.let { it?.pauseRinging() }
116
121
  super.handleOnPause()
117
122
  }
118
123
 
119
124
  override fun handleOnResume() {
120
- this.ringtonePlayer.let { it?.resumeRinging() }
121
125
  super.handleOnResume()
122
126
  }
123
127
 
124
128
  override fun load() {
125
129
  // general init
126
- ringtonePlayer = RingtonePlayer(
127
- this.activity.application,
128
- cancelIncomingCallService = {
129
- val streamVideoClient = this.streamVideoClient
130
- if (streamVideoClient == null) {
131
- android.util.Log.d("StreamCallPlugin", "StreamVideo SDK client is null, no incoming call notification can be constructed")
132
- return@RingtonePlayer
133
- }
134
-
135
- try {
136
- val callServiceClass = Class.forName("io.getstream.video.android.core.notifications.internal.service.CallService")
137
- val companionClass = callServiceClass.declaredClasses.first { it.simpleName == "Companion" }
138
- // Instead of getting INSTANCE, we'll get the companion object through the enclosing class
139
- val companionField = callServiceClass.getDeclaredField("Companion")
140
- companionField.isAccessible = true
141
- val companionInstance = companionField.get(null)
142
-
143
- val removeIncomingCallMethod = companionClass.getDeclaredMethod(
144
- "removeIncomingCall",
145
- Context::class.java,
146
- Class.forName("io.getstream.video.android.model.StreamCallId"),
147
- Class.forName("io.getstream.video.android.core.notifications.internal.service.CallServiceConfig")
148
- )
149
- removeIncomingCallMethod.isAccessible = true
150
-
151
- // Get the default config using reflection
152
- val defaultConfigClass = Class.forName("io.getstream.video.android.core.notifications.internal.service.DefaultCallConfigurations")
153
- val defaultField = defaultConfigClass.getDeclaredField("INSTANCE")
154
- val defaultInstance = defaultField.get(null)
155
- val defaultMethod = defaultConfigClass.getDeclaredMethod("getDefault")
156
- val defaultConfig = defaultMethod.invoke(defaultInstance)
157
-
158
- val app = this.activity.application
159
- val cId = streamVideoClient.state.ringingCall.value?.cid?.let { StreamCallId.fromCallCid(it) }
160
- if (app == null || cId == null || defaultConfig == null) {
161
- android.util.Log.e("StreamCallPlugin", "Some required parameters are null - app: ${app == null}, cId: ${cId == null}, defaultConfig: ${defaultConfig == null}")
162
- }
163
-
164
- // Call the method
165
- removeIncomingCallMethod.invoke(companionInstance, app, cId, defaultConfig)
166
- } catch (e : Throwable) {
167
- android.util.Log.e("StreamCallPlugin", "Reflecting streamNotificationManager and the config DID NOT work", e);
168
- }
169
- }
170
- )
171
130
  initializeStreamVideo()
172
131
  setupViews()
173
132
  super.load()
@@ -206,6 +165,8 @@ public class StreamCallPlugin : Plugin() {
206
165
 
207
166
  if (action === "io.getstream.video.android.action.INCOMING_CALL") {
208
167
  android.util.Log.d("StreamCallPlugin", "handleOnNewIntent: Matched INCOMING_CALL action")
168
+ // We need to make sure the activity is visible on locked screen in such case
169
+ changeActivityAsVisibleOnLockScreen(this@StreamCallPlugin.activity, true)
209
170
  activity?.runOnUiThread {
210
171
  val cid = intent.streamCallId(NotificationHandler.INTENT_EXTRA_CALL_CID)
211
172
  android.util.Log.d("StreamCallPlugin", "handleOnNewIntent: INCOMING_CALL - Extracted cid: $cid")
@@ -213,8 +174,6 @@ public class StreamCallPlugin : Plugin() {
213
174
  android.util.Log.d("StreamCallPlugin", "handleOnNewIntent: INCOMING_CALL - cid is not null, processing.")
214
175
  val call = streamVideoClient?.call(id = cid.id, type = cid.type)
215
176
  android.util.Log.d("StreamCallPlugin", "handleOnNewIntent: INCOMING_CALL - Got call object: ${call?.id}")
216
- // Start ringtone only; UI handled in web layer
217
- ringtonePlayer?.startRinging()
218
177
 
219
178
  // Try to get caller information from the call
220
179
  kotlinx.coroutines.GlobalScope.launch {
@@ -296,9 +255,7 @@ public class StreamCallPlugin : Plugin() {
296
255
  kotlinx.coroutines.GlobalScope.launch {
297
256
  try {
298
257
  call.reject()
299
-
300
- // Stop ringtone
301
- ringtonePlayer?.stopRinging()
258
+ changeActivityAsVisibleOnLockScreen(this@StreamCallPlugin.activity, false)
302
259
 
303
260
  // Notify that call has ended using our helper
304
261
  updateCallStatusAndNotify(call.id, "rejected")
@@ -547,6 +504,7 @@ public class StreamCallPlugin : Plugin() {
547
504
  )
548
505
 
549
506
  val soundsConfig = incomingOnlyRingingConfig()
507
+
550
508
  // Initialize StreamVideo client
551
509
  streamVideoClient = StreamVideoBuilder(
552
510
  context = contextToUse,
@@ -555,8 +513,8 @@ public class StreamCallPlugin : Plugin() {
555
513
  user = savedCredentials.user,
556
514
  token = savedCredentials.tokenValue,
557
515
  notificationConfig = notificationConfig,
558
- sounds = soundsConfig.toSounds()
559
- //, loggingLevel = LoggingLevel(priority = Priority.VERBOSE)
516
+ sounds = soundsConfig.toSounds(),
517
+ // loggingLevel = LoggingLevel(priority = Priority.DEBUG)
560
518
  ).build()
561
519
 
562
520
  // don't do event handler registration when activity may be null
@@ -758,7 +716,6 @@ public class StreamCallPlugin : Plugin() {
758
716
  val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
759
717
  if (keyguardManager.isKeyguardLocked) {
760
718
  android.util.Log.d("StreamCallPlugin", "Stop ringing and move to background")
761
- this.ringtonePlayer?.stopRinging()
762
719
  moveAllActivitiesToBackgroundOrKill(context)
763
720
  }
764
721
 
@@ -865,9 +822,12 @@ public class StreamCallPlugin : Plugin() {
865
822
  // Notify that a call has started or state updated (e.g., participants changed but still active)
866
823
  // The actual check for "last participant" is now handled by CallSessionParticipantLeftEvent
867
824
  updateCallStatusAndNotify(call.cid, "joined")
825
+ // Make sure activity is visible on lock screen
826
+ changeActivityAsVisibleOnLockScreen(this@StreamCallPlugin.activity, true)
868
827
  } ?: run {
869
828
  // Notify that call has ended using our helper
870
829
  updateCallStatusAndNotify("", "left")
830
+ changeActivityAsVisibleOnLockScreen(this@StreamCallPlugin.activity, false)
871
831
  }
872
832
  }
873
833
  }
@@ -967,9 +927,6 @@ public class StreamCallPlugin : Plugin() {
967
927
  kotlinx.coroutines.GlobalScope.launch {
968
928
  try {
969
929
  android.util.Log.d("StreamCallPlugin", "internalAcceptCall: Coroutine started for call ${call.id}")
970
- // Stop ringtone
971
- ringtonePlayer?.stopRinging()
972
- android.util.Log.d("StreamCallPlugin", "internalAcceptCall: Ringtone player stopped for call ${call.id}")
973
930
 
974
931
  // Hide incoming call view first
975
932
  runOnMainThread {
@@ -1274,6 +1231,12 @@ public class StreamCallPlugin : Plugin() {
1274
1231
  android.util.Log.d("StreamCallPlugin", "Leaving call $callId (not creator, >2 participants)")
1275
1232
  call.leave()
1276
1233
  }
1234
+
1235
+ // Here, we'll also mark the activity as not-visible on lock screen
1236
+ this@StreamCallPlugin.savedActivity?.let {
1237
+ changeActivityAsVisibleOnLockScreen(it, false)
1238
+ }
1239
+
1277
1240
  } catch (e: Exception) {
1278
1241
  android.util.Log.e("StreamCallPlugin", "Error getting call info for $callId, defaulting to leave()", e)
1279
1242
  // Fallback to leave if we can't determine the call info
@@ -1328,7 +1291,6 @@ public class StreamCallPlugin : Plugin() {
1328
1291
  }
1329
1292
  overlayView?.isVisible = false
1330
1293
  bridge?.webView?.setBackgroundColor(Color.WHITE) // Restore webview opacity
1331
- this@StreamCallPlugin.ringtonePlayer?.stopRinging()
1332
1294
 
1333
1295
  // Also hide incoming call view if visible
1334
1296
  android.util.Log.d("StreamCallPlugin", "Hiding incoming call view for call $callId")
@@ -1339,6 +1301,29 @@ public class StreamCallPlugin : Plugin() {
1339
1301
  updateCallStatusAndNotify(callId, "left")
1340
1302
  }
1341
1303
 
1304
+ private fun changeActivityAsVisibleOnLockScreen(activity: Activity, visible: Boolean) {
1305
+ if (visible) {
1306
+ // Ensure the activity is visible over the lock screen when launched via full-screen intent
1307
+ android.util.Log.d("StreamCallPlugin", "Mark the mainActivity as visible on the lockscreen")
1308
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
1309
+ activity.setShowWhenLocked(true)
1310
+ activity.setTurnScreenOn(true)
1311
+ } else {
1312
+ activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
1313
+ }
1314
+ } else {
1315
+ // Ensure the activity is NOT visible over the lock screen when launched via full-screen intent
1316
+ android.util.Log.d("StreamCallPlugin", "Clear the flag for the mainActivity for visible on the lockscreen")
1317
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
1318
+ activity.setShowWhenLocked(false)
1319
+ activity.setTurnScreenOn(false)
1320
+ } else {
1321
+ activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
1322
+ }
1323
+ }
1324
+
1325
+ }
1326
+
1342
1327
  @OptIn(DelicateCoroutinesApi::class)
1343
1328
  private fun transEndCallRaw(call: Call) {
1344
1329
  val callId = call.id
@@ -1595,8 +1580,8 @@ public class StreamCallPlugin : Plugin() {
1595
1580
  runOnMainThread {
1596
1581
  android.util.Log.d("StreamCallPlugin", "Hiding UI elements for call $callCid (one-time cleanup)")
1597
1582
  overlayView?.isVisible = false
1598
- ringtonePlayer?.stopRinging()
1599
- // No dedicated incoming-call native view anymore; UI handled by web layer
1583
+ // here we will also make sure we don't show on lock screen
1584
+ changeActivityAsVisibleOnLockScreen(this.activity, false)
1600
1585
  }
1601
1586
 
1602
1587
  android.util.Log.d("StreamCallPlugin", "Cleaned up resources for ended call: $callCid")
package/dist/esm/web.js CHANGED
@@ -21,18 +21,20 @@ export class StreamCallWeb extends WebPlugin {
21
21
  role: event.call.created_by.role,
22
22
  };
23
23
  }
24
- if (!this.currentCall) {
25
- console.log('Creating new call', event.call.id);
26
- this.currentCall = (_b = this.client) === null || _b === void 0 ? void 0 : _b.call(event.call.type, event.call.id);
27
- // this.currentActiveCallId = this.currentCall?.cid;
24
+ // if (!this.currentCall) {
25
+ console.log('Creating new call', event.call.id);
26
+ this.currentCall = (_b = this.client) === null || _b === void 0 ? void 0 : _b.call(event.call.type, event.call.id);
27
+ // this.currentActiveCallId = this.currentCall?.cid;
28
+ setTimeout(() => {
28
29
  this.notifyListeners('callEvent', {
29
30
  callId: event.call.id,
30
31
  state: CallingState.RINGING,
31
32
  caller,
32
33
  });
33
- // Clear previous responses when a new call starts
34
- this.participantResponses.clear();
35
- }
34
+ }, 100);
35
+ // Clear previous responses when a new call starts
36
+ this.participantResponses.clear();
37
+ // }
36
38
  if (this.currentCall) {
37
39
  console.log('Call found', this.currentCall.id);
38
40
  this.callStateSubscription = (_c = this.currentCall) === null || _c === void 0 ? void 0 : _c.state.callingState$.subscribe((s) => {