Skip to content

Commit

Permalink
Incoming calls handling (#956)
Browse files Browse the repository at this point in the history
Improve handling of incoming calls
  • Loading branch information
aleksandar-apostolov authored Dec 12, 2023
1 parent ba09119 commit 65850a1
Show file tree
Hide file tree
Showing 15 changed files with 496 additions and 159 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import io.getstream.result.Result
import io.getstream.video.android.compose.theme.VideoTheme
import io.getstream.video.android.compose.ui.components.call.activecall.CallContent
import io.getstream.video.android.compose.ui.components.call.ringing.RingingCallContent
import io.getstream.video.android.core.Call
import io.getstream.video.android.core.StreamVideo
import io.getstream.video.android.core.call.state.AcceptCall
import io.getstream.video.android.core.call.state.CallAction
Expand All @@ -45,7 +46,10 @@ import io.getstream.video.android.datastore.delegate.StreamUserDataStore
import io.getstream.video.android.model.mapper.isValidCallId
import io.getstream.video.android.model.mapper.toTypeAndId
import io.getstream.video.android.util.StreamVideoInitHelper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.openapitools.client.models.CallRejectedEvent
import java.util.UUID
import javax.inject.Inject

Expand All @@ -54,6 +58,7 @@ class DirectCallActivity : ComponentActivity() {

@Inject
lateinit var dataStore: StreamUserDataStore
private lateinit var call: Call

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -73,7 +78,7 @@ class DirectCallActivity : ComponentActivity() {
}

// Create call object
val call = StreamVideo.instance().call(type, id)
call = StreamVideo.instance().call(type, id)

// Get list of members
val members: List<String> = intent.getStringArrayExtra(EXTRA_MEMBERS_ARRAY)?.asList() ?: emptyList()
Expand All @@ -84,6 +89,18 @@ class DirectCallActivity : ComponentActivity() {
// Ring the members
val result = call.create(ring = true, memberIds = membersWithMe)

// Update the call
call.get()

call.subscribe {
when (it) {
// Finish this activity if ever a call.reject is received
is CallRejectedEvent -> {
finish()
}
}
}

if (result is Result.Failure) {
// Failed to recover the current state of the call
// TODO: Automaticly call this in the SDK?
Expand All @@ -101,20 +118,19 @@ class DirectCallActivity : ComponentActivity() {
val onCallAction: (CallAction) -> Unit = { callAction ->
when (callAction) {
is ToggleCamera -> call.camera.setEnabled(callAction.isEnabled)
is ToggleMicrophone -> call.microphone.setEnabled(callAction.isEnabled)
is ToggleMicrophone -> call.microphone.setEnabled(
callAction.isEnabled,
)
is ToggleSpeakerphone -> call.speaker.setEnabled(callAction.isEnabled)
is LeaveCall -> {
call.leave()
finish()
}
is DeclineCall -> {
// Not needed. this activity is only used for outgoing calls.
reject(call)
}
is CancelCall -> {
lifecycleScope.launch {
call.leave()
finish()
}
reject(call)
}
is AcceptCall -> {
lifecycleScope.launch {
Expand All @@ -131,8 +147,7 @@ class DirectCallActivity : ComponentActivity() {
modifier = Modifier.background(color = VideoTheme.colors.appBackground),
call = call,
onBackPressed = {
call.leave()
finish()
reject(call)
},
onAcceptedContent = {
CallContent(
Expand All @@ -142,8 +157,7 @@ class DirectCallActivity : ComponentActivity() {
)
},
onRejectedContent = {
call.leave()
finish()
reject(call)
},
onCallAction = onCallAction,
)
Expand All @@ -152,6 +166,22 @@ class DirectCallActivity : ComponentActivity() {
}
}

override fun onStop() {
super.onStop()
if (::call.isInitialized) {
reject(call)
}
}

private fun reject(call: Call) {
lifecycleScope.launch(Dispatchers.IO) {
call.reject()
withContext(Dispatchers.Main) {
finish()
}
}
}

companion object {
const val EXTRA_CID: String = "EXTRA_CID"
const val EXTRA_MEMBERS_ARRAY: String = "EXTRA_MEMBERS_ARRAY"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
Expand Down Expand Up @@ -135,8 +136,10 @@ class IncomingCallActivity : ComponentActivity() {
)
},
onRejectedContent = {
call.leave()
finish()
LaunchedEffect(key1 = call) {
call.reject()
finish()
}
},
onCallAction = onCallAction,
)
Expand Down
6 changes: 6 additions & 0 deletions stream-video-android-core/api/stream-video-android-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,7 @@ public abstract interface class io/getstream/video/android/core/StreamVideo : io
public static synthetic fun call$default (Lio/getstream/video/android/core/StreamVideo;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lio/getstream/video/android/core/Call;
public abstract fun cleanup ()V
public abstract fun connectAsync (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun connectIfNotAlreadyConnected (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun createDevice (Lio/getstream/android/push/PushDevice;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun deleteDevice (Lio/getstream/video/android/model/Device;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun getContext ()Landroid/content/Context;
Expand Down Expand Up @@ -4036,6 +4037,7 @@ public class io/getstream/video/android/core/notifications/DefaultNotificationHa
public fun getChannelId ()Ljava/lang/String;
public fun getChannelName ()Ljava/lang/String;
public fun getOngoingCallNotification (Lio/getstream/video/android/model/StreamCallId;)Landroid/app/Notification;
public fun getRingingCallNotification (Lio/getstream/video/android/model/StreamCallId;Ljava/lang/String;)Landroid/app/Notification;
public fun onLiveCall (Lio/getstream/video/android/model/StreamCallId;Ljava/lang/String;)V
public fun onNotification (Lio/getstream/video/android/model/StreamCallId;Ljava/lang/String;)V
public fun onPermissionDenied ()V
Expand Down Expand Up @@ -4076,8 +4078,10 @@ public abstract interface class io/getstream/video/android/core/notifications/No
public static final field Companion Lio/getstream/video/android/core/notifications/NotificationHandler$Companion;
public static final field INCOMING_CALL_NOTIFICATION_ID I
public static final field INTENT_EXTRA_CALL_CID Ljava/lang/String;
public static final field INTENT_EXTRA_CALL_DISPLAY_NAME Ljava/lang/String;
public static final field INTENT_EXTRA_NOTIFICATION_ID Ljava/lang/String;
public abstract fun getOngoingCallNotification (Lio/getstream/video/android/model/StreamCallId;)Landroid/app/Notification;
public abstract fun getRingingCallNotification (Lio/getstream/video/android/model/StreamCallId;Ljava/lang/String;)Landroid/app/Notification;
public abstract fun onLiveCall (Lio/getstream/video/android/model/StreamCallId;Ljava/lang/String;)V
public abstract fun onNotification (Lio/getstream/video/android/model/StreamCallId;Ljava/lang/String;)V
public abstract fun onRingingCall (Lio/getstream/video/android/model/StreamCallId;Ljava/lang/String;)V
Expand All @@ -4093,6 +4097,7 @@ public final class io/getstream/video/android/core/notifications/NotificationHan
public static final field ACTION_REJECT_CALL Ljava/lang/String;
public static final field INCOMING_CALL_NOTIFICATION_ID I
public static final field INTENT_EXTRA_CALL_CID Ljava/lang/String;
public static final field INTENT_EXTRA_CALL_DISPLAY_NAME Ljava/lang/String;
public static final field INTENT_EXTRA_NOTIFICATION_ID Ljava/lang/String;
}

Expand Down Expand Up @@ -5786,6 +5791,7 @@ public final class io/getstream/video/android/model/StreamCallId$Creator : andro
}

public final class io/getstream/video/android/model/StreamCallIdKt {
public static final fun streamCallDisplayName (Landroid/content/Intent;Ljava/lang/String;)Ljava/lang/String;
public static final fun streamCallId (Landroid/content/Intent;Ljava/lang/String;)Lio/getstream/video/android/model/StreamCallId;
}

Expand Down
4 changes: 2 additions & 2 deletions stream-video-android-core/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@
</receiver>


<service android:name=".notifications.internal.service.OngoingCallService"
android:foregroundServiceType="microphone"/>
<service android:name=".notifications.internal.service.CallService"
android:foregroundServiceType="shortService|microphone"/>

<service android:name=".screenshare.StreamScreenShareService"
android:foregroundServiceType="mediaProjection"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@
package io.getstream.video.android.core

import android.content.Context
import android.content.Intent
import androidx.core.content.ContextCompat
import io.getstream.video.android.core.notifications.NotificationHandler
import io.getstream.video.android.core.notifications.internal.service.OngoingCallService
import io.getstream.video.android.core.notifications.internal.service.CallService
import io.getstream.video.android.model.StreamCallId
import io.getstream.video.android.model.User
import kotlinx.coroutines.flow.MutableStateFlow
Expand Down Expand Up @@ -125,10 +123,10 @@ class ClientState(client: StreamVideo) {
private fun maybeStartForegroundService(call: Call) {
if (clientImpl.runForeGroundService) {
val context = clientImpl.context
val serviceIntent = Intent(context, OngoingCallService::class.java)
serviceIntent.putExtra(
NotificationHandler.INTENT_EXTRA_CALL_CID,
val serviceIntent = CallService.buildStartIntent(
context,
StreamCallId.fromCallCid(call.cid),
CallService.TRIGGER_ONGOING_CALL,
)
ContextCompat.startForegroundService(context, serviceIntent)
}
Expand All @@ -137,7 +135,7 @@ class ClientState(client: StreamVideo) {
private fun maybeStopForegroundService() {
if (clientImpl.runForeGroundService) {
val context = clientImpl.context
val serviceIntent = Intent(context, OngoingCallService::class.java)
val serviceIntent = CallService.buildStopIntent(context)
context.stopService(serviceIntent)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ public interface StreamVideo : NotificationHandler {
}

public fun cleanup()
suspend fun connectIfNotAlreadyConnected()
}

private const val DEFAULT_QUERY_CALLS_SORT = "cid"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,14 @@ internal class StreamVideoImpl internal constructor(
return sub
}

override suspend fun connectIfNotAlreadyConnected() {
if (connectionModule.coordinatorSocket.connectionState.value != SocketState.NotConnected &&
connectionModule.coordinatorSocket.connectionState.value != SocketState.Connecting
) {
connectionModule.coordinatorSocket.connect()
}
}

/**
* Observes the app lifecycle and attempts to reconnect/release the socket connection.
*/
Expand Down
Loading

0 comments on commit 65850a1

Please sign in to comment.