Skip to content

Commit

Permalink
Add getNotificationUpdates() in NotificationHandler and default imple…
Browse files Browse the repository at this point in the history
…mentation
  • Loading branch information
liviu-timar committed Sep 20, 2024
1 parent 2ab0928 commit f163c62
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 112 deletions.
8 changes: 5 additions & 3 deletions stream-video-android-core/api/stream-video-android-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -4139,7 +4139,8 @@ public class io/getstream/video/android/core/notifications/DefaultNotificationHa
public final fun getHideRingingNotificationInForeground ()Z
public final fun getNotificationIconRes ()I
protected final fun getNotificationManager ()Landroidx/core/app/NotificationManagerCompat;
public fun getOngoingCallNotification (Lio/getstream/video/android/model/StreamCallId;Ljava/lang/String;IZ)Landroid/app/Notification;
public fun getNotificationUpdates (Lkotlinx/coroutines/CoroutineScope;Lio/getstream/video/android/core/Call;Lio/getstream/video/android/model/User;Lkotlin/jvm/functions/Function1;)V
public fun getOngoingCallNotification (Lio/getstream/video/android/model/StreamCallId;Ljava/lang/String;ZI)Landroid/app/Notification;
public fun getRingingCallNotification (Lio/getstream/video/android/core/RingingState;Lio/getstream/video/android/model/StreamCallId;Ljava/lang/String;Z)Landroid/app/Notification;
public fun getSettingUpCallNotification ()Landroid/app/Notification;
public fun onLiveCall (Lio/getstream/video/android/model/StreamCallId;Ljava/lang/String;)V
Expand Down Expand Up @@ -4189,8 +4190,9 @@ public abstract interface class io/getstream/video/android/core/notifications/No
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;Ljava/lang/String;IZ)Landroid/app/Notification;
public static synthetic fun getOngoingCallNotification$default (Lio/getstream/video/android/core/notifications/NotificationHandler;Lio/getstream/video/android/model/StreamCallId;Ljava/lang/String;IZILjava/lang/Object;)Landroid/app/Notification;
public abstract fun getNotificationUpdates (Lkotlinx/coroutines/CoroutineScope;Lio/getstream/video/android/core/Call;Lio/getstream/video/android/model/User;Lkotlin/jvm/functions/Function1;)V
public abstract fun getOngoingCallNotification (Lio/getstream/video/android/model/StreamCallId;Ljava/lang/String;ZI)Landroid/app/Notification;
public static synthetic fun getOngoingCallNotification$default (Lio/getstream/video/android/core/notifications/NotificationHandler;Lio/getstream/video/android/model/StreamCallId;Ljava/lang/String;ZIILjava/lang/Object;)Landroid/app/Notification;
public abstract fun getRingingCallNotification (Lio/getstream/video/android/core/RingingState;Lio/getstream/video/android/model/StreamCallId;Ljava/lang/String;Z)Landroid/app/Notification;
public static synthetic fun getRingingCallNotification$default (Lio/getstream/video/android/core/notifications/NotificationHandler;Lio/getstream/video/android/core/RingingState;Lio/getstream/video/android/model/StreamCallId;Ljava/lang/String;ZILjava/lang/Object;)Landroid/app/Notification;
public abstract fun getSettingUpCallNotification ()Landroid/app/Notification;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import androidx.core.graphics.drawable.IconCompat
import io.getstream.android.push.permissions.DefaultNotificationPermissionHandler
import io.getstream.android.push.permissions.NotificationPermissionHandler
import io.getstream.log.taggedLogger
import io.getstream.video.android.core.Call
import io.getstream.video.android.core.R
import io.getstream.video.android.core.RingingState
import io.getstream.video.android.core.notifications.NotificationHandler.Companion.ACTION_LIVE_CALL
Expand All @@ -44,6 +45,13 @@ import io.getstream.video.android.core.notifications.NotificationHandler.Compani
import io.getstream.video.android.core.notifications.internal.DefaultStreamIntentResolver
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.CoroutineScope
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch

public open class DefaultNotificationHandler(
private val application: Application,
Expand Down Expand Up @@ -107,7 +115,7 @@ public open class DefaultNotificationHandler(
override fun getRingingCallNotification(
ringingState: RingingState,
callId: StreamCallId,
callDisplayName: String,
callDisplayName: String?,
shouldHaveContentIntent: Boolean,
): Notification? {
return if (ringingState is RingingState.Incoming) {
Expand Down Expand Up @@ -183,7 +191,7 @@ public open class DefaultNotificationHandler(
fullScreenPendingIntent: PendingIntent,
acceptCallPendingIntent: PendingIntent,
rejectCallPendingIntent: PendingIntent,
callerName: String,
callerName: String?,
shouldHaveContentIntent: Boolean,
): Notification {
// if the app is in foreground then don't interrupt the user with a high priority
Expand Down Expand Up @@ -233,7 +241,7 @@ public open class DefaultNotificationHandler(
priority = NotificationCompat.PRIORITY_HIGH
setContentTitle(callerName)
setContentText(
application.getString(R.string.stream_video_incoming_call_notification_title),
application.getString(R.string.stream_video_incoming_call_notification_description),
)
setChannelId(channelId)
setOngoing(true)
Expand Down Expand Up @@ -282,8 +290,8 @@ public open class DefaultNotificationHandler(
override fun getOngoingCallNotification(
callId: StreamCallId,
callDisplayName: String?,
remoteParticipantCount: Int,
isOutgoingCall: Boolean,
remoteParticipantCount: Int,
): Notification? {
val notificationId = callId.hashCode() // Notification ID

Expand Down Expand Up @@ -344,14 +352,97 @@ public open class DefaultNotificationHandler(
)
.setAutoCancel(false)
.setOngoing(true)
.addHangupAction(
.addHangUpAction(
hangUpIntent,
callDisplayName ?: callId.toString(),
callDisplayName ?: application.getString(
R.string.stream_video_ongoing_call_notification_title,
),
remoteParticipantCount,
)
.build()
}

override fun getNotificationUpdates(
coroutineScope: CoroutineScope,
call: Call,
localUser: User,
onUpdate: (Notification) -> Unit,
) {
coroutineScope.launch {
var latestRemoteParticipantCount = -1

// Monitor call state and remote participants
combine(
call.state.ringingState,
call.state.members,
call.state.remoteParticipants,
) { ringingState, members, remoteParticipants ->
Triple(ringingState, members, remoteParticipants)
}
.distinctUntilChanged()
.filter { it.first is RingingState.Active || it.first is RingingState.Outgoing }
.collectLatest { state ->
val ringingState = state.first
val members = state.second
val remoteParticipants = state.third

if (ringingState is RingingState.Outgoing) {
val remoteMembersCount = members.size - 1

val callDisplayName = if (remoteMembersCount != 1) {
application.getString(
R.string.stream_video_outgoing_call_notification_title,
)
} else {
members.firstOrNull { member ->
member.user.id != localUser.id
}?.user?.name ?: "Unknown"
}

getOngoingCallNotification(
callId = StreamCallId.fromCallCid(call.cid),
callDisplayName = callDisplayName,
isOutgoingCall = true,
remoteParticipantCount = remoteMembersCount,
)?.let {
onUpdate(it)
}
} else if (ringingState is RingingState.Active) {
// If number of remote participants increased or decreased
if (remoteParticipants.size != latestRemoteParticipantCount) {
latestRemoteParticipantCount = remoteParticipants.size

val callDisplayName = if (remoteParticipants.isEmpty()) {
// If no remote participants, get simple call notification title
application.getString(
R.string.stream_video_ongoing_call_notification_title,
)
} else {
if (remoteParticipants.size > 1) {
// If more than 1 remote participant, get group call notification title
application.getString(
R.string.stream_video_ongoing_group_call_notification_title,
)
} else {
// If 1 remote participant, get the name of the remote participant
remoteParticipants.firstOrNull()?.name?.value ?: "Unknown"
}
}

// Use latest call display name in notification
getOngoingCallNotification(
callId = StreamCallId.fromCallCid(call.cid),
callDisplayName = callDisplayName,
remoteParticipantCount = remoteParticipants.size,
)?.let {
onUpdate(it)
}
}
}
}
}
}

private fun maybeCreateChannel(
channelId: String,
context: Context,
Expand Down Expand Up @@ -427,7 +518,7 @@ public open class DefaultNotificationHandler(
.build()
}

private fun NotificationCompat.Builder.addHangupAction(
private fun NotificationCompat.Builder.addHangUpAction(
rejectCallPendingIntent: PendingIntent,
callDisplayName: String,
remoteParticipantCount: Int,
Expand Down Expand Up @@ -474,13 +565,23 @@ public open class DefaultNotificationHandler(
private fun NotificationCompat.Builder.addCallActions(
acceptCallPendingIntent: PendingIntent,
rejectCallPendingIntent: PendingIntent,
callDisplayName: String,
callDisplayName: String?,
): NotificationCompat.Builder = apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
setStyle(
CallStyle.forIncomingCall(
Person.Builder()
.setName(callDisplayName)
.setName(callDisplayName ?: "Unknown")
.apply {
if (callDisplayName == null) {
setIcon(
IconCompat.createWithResource(
application,
R.drawable.stream_video_ic_user,
),
)
}
}
.build(),
rejectCallPendingIntent,
acceptCallPendingIntent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ package io.getstream.video.android.core.notifications

import android.app.Notification
import io.getstream.android.push.permissions.NotificationPermissionHandler
import io.getstream.video.android.core.Call
import io.getstream.video.android.core.RingingState
import io.getstream.video.android.model.StreamCallId
import io.getstream.video.android.model.User
import kotlinx.coroutines.CoroutineScope

public interface NotificationHandler : NotificationPermissionHandler {
fun onRingingCall(callId: StreamCallId, callDisplayName: String)
Expand All @@ -29,16 +32,22 @@ public interface NotificationHandler : NotificationPermissionHandler {
fun getOngoingCallNotification(
callId: StreamCallId,
callDisplayName: String?,
remoteParticipantCount: Int = 0,
isOutgoingCall: Boolean = false,
remoteParticipantCount: Int = 0,
): Notification?
fun getRingingCallNotification(
ringingState: RingingState,
callId: StreamCallId,
callDisplayName: String,
callDisplayName: String?,
shouldHaveContentIntent: Boolean = true,
): Notification?
fun getSettingUpCallNotification(): Notification?
fun getNotificationUpdates(
coroutineScope: CoroutineScope,
call: Call,
localUser: User,
onUpdate: (Notification) -> Unit,
)

companion object {
const val ACTION_NOTIFICATION = "io.getstream.video.android.action.NOTIFICATION"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@
package io.getstream.video.android.core.notifications.internal

import android.app.Notification
import io.getstream.video.android.core.Call
import io.getstream.video.android.core.RingingState
import io.getstream.video.android.core.notifications.NotificationHandler
import io.getstream.video.android.model.StreamCallId
import io.getstream.video.android.model.User
import kotlinx.coroutines.CoroutineScope

internal object NoOpNotificationHandler : NotificationHandler {
override fun onRingingCall(callId: StreamCallId, callDisplayName: String) { /* NoOp */ }
Expand All @@ -29,16 +32,22 @@ internal object NoOpNotificationHandler : NotificationHandler {
override fun getOngoingCallNotification(
callId: StreamCallId,
callDisplayName: String?,
remoteParticipantCount: Int,
isOutgoingCall: Boolean,
remoteParticipantCount: Int,
): Notification? = null
override fun getRingingCallNotification(
ringingState: RingingState,
callId: StreamCallId,
callDisplayName: String,
callDisplayName: String?,
shouldHaveContentIntent: Boolean,
): Notification? = null
override fun getSettingUpCallNotification(): Notification? = null
override fun getNotificationUpdates(
coroutineScope: CoroutineScope,
call: Call,
localUser: User,
onUpdate: (Notification) -> Unit,
) { /* NoOp */ }

override fun onPermissionDenied() { /* NoOp */ }
override fun onPermissionGranted() { /* NoOp */ }
Expand Down
Loading

0 comments on commit f163c62

Please sign in to comment.