Skip to content

Commit

Permalink
Merge branch 'develop' into skip_permission_request_config
Browse files Browse the repository at this point in the history
  • Loading branch information
aleksandar-apostolov authored Sep 23, 2024
2 parents 8a198ad + 7a82213 commit d5bb86e
Show file tree
Hide file tree
Showing 9 changed files with 340 additions and 137 deletions.
7 changes: 5 additions & 2 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 (Ljava/lang/String;Lio/getstream/video/android/model/StreamCallId;)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 @@ -4191,7 +4192,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 (Ljava/lang/String;Lio/getstream/video/android/model/StreamCallId;)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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,47 @@ 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)
fun onMissedCall(callId: StreamCallId, callDisplayName: String)
fun onNotification(callId: StreamCallId, callDisplayName: String)
fun onLiveCall(callId: StreamCallId, callDisplayName: String)
fun getOngoingCallNotification(callDisplayName: String?, callId: StreamCallId): Notification?
fun getOngoingCallNotification(
callId: StreamCallId,
callDisplayName: String? = null,
isOutgoingCall: Boolean = false,
remoteParticipantCount: Int = 0,
): Notification?
fun getRingingCallNotification(
ringingState: RingingState,
callId: StreamCallId,
callDisplayName: String,
callDisplayName: String? = null,
shouldHaveContentIntent: Boolean = true,
): Notification?
fun getSettingUpCallNotification(): Notification?

/**
* Get subsequent updates to notifications.
* Initially, notifications are posted by one of the other methods, and then this method can be used to re-post them with updated content.
*
* @param coroutineScope Coroutine scope used for the updates.
* @param call The Stream call object.
* @param localUser The local Stream user.
* @param onUpdate Callback to be called when the notification is updated.
*/
fun getNotificationUpdates(
coroutineScope: CoroutineScope,
call: Call,
localUser: User,
onUpdate: (Notification) -> Unit,
)

companion object {
const val ACTION_NOTIFICATION = "io.getstream.video.android.action.NOTIFICATION"
const val ACTION_MISSED_CALL = "io.getstream.video.android.action.MISSED_CALL"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,37 @@
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 */ }
override fun onMissedCall(callId: StreamCallId, callDisplayName: String) { /* NoOp */ }
override fun onNotification(callId: StreamCallId, callDisplayName: String) { /* NoOp */ }
override fun onLiveCall(callId: StreamCallId, callDisplayName: String) { /* NoOp */ }
override fun getOngoingCallNotification(
callDisplayName: String?,
callId: StreamCallId,
callDisplayName: String?,
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
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,8 @@ internal open class CallService : Service() {
val notificationData: Pair<Notification?, Int> = when (trigger) {
TRIGGER_ONGOING_CALL -> Pair(
first = streamVideo.getOngoingCallNotification(
callDisplayName = intentCallDisplayName,
callId = intentCallId,
callDisplayName = intentCallDisplayName,
),
second = intentCallId.hashCode(),
)
Expand All @@ -258,7 +258,7 @@ internal open class CallService : Service() {
first = streamVideo.getRingingCallNotification(
ringingState = RingingState.Incoming(),
callId = intentCallId,
callDisplayName = intentCallDisplayName!!,
callDisplayName = intentCallDisplayName,
shouldHaveContentIntent = streamVideo.state.activeCall.value == null,
),
second = INCOMING_CALL_NOTIFICATION_ID,
Expand All @@ -269,7 +269,7 @@ internal open class CallService : Service() {
ringingState = RingingState.Outgoing(),
callId = intentCallId,
callDisplayName = getString(
R.string.stream_video_ongoing_call_notification_description,
R.string.stream_video_outgoing_call_notification_title,
),
),
second = INCOMING_CALL_NOTIFICATION_ID, // Same for incoming and outgoing
Expand Down Expand Up @@ -331,7 +331,7 @@ internal open class CallService : Service() {
} else if (trigger == TRIGGER_OUTGOING_CALL) {
if (mediaPlayer == null) mediaPlayer = MediaPlayer()
}
observeCallState(intentCallId, streamVideo)
observeCall(intentCallId, streamVideo)
registerToggleCameraBroadcastReceiver()
return START_NOT_STICKY
}
Expand Down Expand Up @@ -414,8 +414,13 @@ internal open class CallService : Service() {
}
}

private fun observeCallState(callId: StreamCallId, streamVideo: StreamVideoImpl) {
// Ringing state
private fun observeCall(callId: StreamCallId, streamVideo: StreamVideoImpl) {
observeRingingState(callId, streamVideo)
observeCallEvents(callId, streamVideo)
observeNotificationUpdates(callId, streamVideo)
}

private fun observeRingingState(callId: StreamCallId, streamVideo: StreamVideoImpl) {
serviceScope.launch {
val call = streamVideo.call(callId.type, callId.id)
call.state.ringingState.collect {
Expand Down Expand Up @@ -457,37 +462,6 @@ internal open class CallService : Service() {
}
}
}

// Call state
serviceScope.launch {
val call = streamVideo.call(callId.type, callId.id)
call.subscribe { event ->
logger.i { "Received event in service: $event" }
when (event) {
is CallAcceptedEvent -> {
handleIncomingCallAcceptedByMeOnAnotherDevice(
acceptedByUserId = event.user.id,
myUserId = streamVideo.userId,
callRingingState = call.state.ringingState.value,
)
}

is CallRejectedEvent -> {
handleIncomingCallRejectedByMeOrCaller(
rejectedByUserId = event.user.id,
myUserId = streamVideo.userId,
createdByUserId = call.state.createdBy.value?.id,
activeCallExists = streamVideo.state.activeCall.value != null,
)
}

is CallEndedEvent -> {
// When call ends for any reason
stopService()
}
}
}
}
}

private fun playCallSound(@RawRes sound: Int?) {
Expand Down Expand Up @@ -524,6 +498,38 @@ internal open class CallService : Service() {
}
}

private fun observeCallEvents(callId: StreamCallId, streamVideo: StreamVideoImpl) {
serviceScope.launch {
val call = streamVideo.call(callId.type, callId.id)
call.subscribe { event ->
logger.i { "Received event in service: $event" }
when (event) {
is CallAcceptedEvent -> {
handleIncomingCallAcceptedByMeOnAnotherDevice(
acceptedByUserId = event.user.id,
myUserId = streamVideo.userId,
callRingingState = call.state.ringingState.value,
)
}

is CallRejectedEvent -> {
handleIncomingCallRejectedByMeOrCaller(
rejectedByUserId = event.user.id,
myUserId = streamVideo.userId,
createdByUserId = call.state.createdBy.value?.id,
activeCallExists = streamVideo.state.activeCall.value != null,
)
}

is CallEndedEvent -> {
// When call ends for any reason
stopService()
}
}
}
}
}

private fun handleIncomingCallAcceptedByMeOnAnotherDevice(acceptedByUserId: String, myUserId: String, callRingingState: RingingState) {
// If accepted event was received, with event user being me, but current device is still ringing, it means the call was accepted on another device
if (acceptedByUserId == myUserId && callRingingState is RingingState.Incoming) {
Expand All @@ -543,6 +549,21 @@ internal open class CallService : Service() {
}
}

private fun observeNotificationUpdates(callId: StreamCallId, streamVideo: StreamVideoImpl) {
streamVideo.getNotificationUpdates(
serviceScope,
streamVideo.call(callId.type, callId.id),
streamVideo.user,
) { notification ->
startForegroundWithServiceType(
callId.hashCode(),
notification,
TRIGGER_ONGOING_CALL,
serviceType,
)
}
}

private fun registerToggleCameraBroadcastReceiver() {
serviceScope.launch {
if (!isToggleCameraBroadcastReceiverRegistered) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,4 @@ public data class StreamCallId constructor(
public fun Intent.streamCallId(key: String): StreamCallId? =
IntentCompat.getParcelableExtra(this, key, StreamCallId::class.java)

public fun Intent.streamCallDisplayName(key: String): String = this.getStringExtra(key) ?: "."
public fun Intent.streamCallDisplayName(key: String): String? = this.getStringExtra(key)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2024 Stream.io Inc. All rights reserved.
Licensed under the Stream License;
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://github.com/GetStream/stream-video-android/blob/main/LICENSE
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M480,0a480,480 0 1,0 0,960a480,480 0 1,0 0,-960"
android:fillColor="#CCC"/>
<path
android:pathData="M480,480q-66,0 -113,-47t-47,-113q0,-66 47,-113t113,-47q66,0 113,47t47,113q0,66 -47,113t-113,47ZM160,720v-32q0,-34 17.5,-62.5T224,582q62,-31 126,-46.5T480,520q66,0 130,15.5T736,582q29,15 46.5,43.5T800,688v32q0,33 -23.5,56.5T720,800L240,800q-33,0 -56.5,-23.5T160,720Z"
android:fillColor="#434343"/>
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2024 Stream.io Inc. All rights reserved.
Licensed under the Stream License;
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://github.com/GetStream/stream-video-android/blob/main/LICENSE
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M480,0a480,480 0 1,0 0,960a480,480 0 1,0 0,-960"
android:fillColor="#CCC"/>
<group
android:scaleX="0.9"
android:scaleY="0.9"
android:translateX="52"
android:translateY="52">
<path
android:pathData="M84,696q-15,0 -25.5,-10.5T48,660v-21q0,-39 39,-63t105,-24q14,0 26,1t23,3q-12,18 -18.5,39.11Q216,616.23 216,638v58L84,696ZM300,696q-15,0 -25.5,-10.5T264,660v-22q0,-28 14.5,-50t43.5,-39q29,-17 69,-25t89.5,-8q49.5,0 89,8t68.5,25q29,16 43.5,38.69Q696,610.38 696,638v22q0,15 -10.5,25.5T660,696L300,696ZM744,696v-58q0,-22 -6.5,-42.5T719,556q9,-2 20.5,-3t28.5,-1q66,0 105,24t39,63v21q0,15 -10.5,25.5T876,696L744,696ZM192,504q-30,0 -51,-21t-21,-51q0,-30 21,-51t51,-21q30,0 51,21t21,51q0,30 -21,51t-51,21ZM768,504q-30,0 -51,-21t-21,-51q0,-30 21,-51t51,-21q30,0 51,21t21,51q0,30 -21,51t-51,21ZM480,468q-45,0 -76.5,-31.52T372,359.93q0,-44.93 31.52,-76.43 31.52,-31.5 76.55,-31.5 44.93,0 76.43,31.55Q588,315.1 588,360q0,45 -31.55,76.5T480,468Z"
android:fillColor="#434343" />
</group>
</vector>
9 changes: 5 additions & 4 deletions stream-video-android-core/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,18 @@
<string name="stream_video_incoming_call_low_priority_notification_channel_id" translatable="false">incoming_calls_low_priority</string>
<string name="stream_video_incoming_call_low_priority_notification_channel_description">Incoming audio and video call alerts</string>
<string name="stream_video_incoming_call_notification_channel_description">Incoming audio and video call alerts</string>
<string name="stream_video_incoming_call_notification_title">Incoming call</string>
<string name="stream_video_incoming_call_notification_description">Incoming call</string>
<string name="stream_video_outgoing_call_notification_channel_id" translatable="false">outgoing_calls</string>
<string name="stream_video_outgoing_call_notification_channel_title">Outgoing Calls</string>
<string name="stream_video_outgoing_call_notification_channel_description">Outgoing call notifications</string>
<string name="stream_video_outgoing_call_notification_title" tools:ignore="TypographyEllipsis">Calling...</string>
<string name="stream_video_outgoing_call_notification_description">There is a call in progress, tap to go back to the call</string>
<string name="stream_video_outgoing_call_notification_description">Tap to go back to the call</string>
<string name="stream_video_ongoing_call_notification_channel_id" translatable="false">ongoing_calls</string>
<string name="stream_video_ongoing_call_notification_channel_title">Ongoing Calls</string>
<string name="stream_video_ongoing_call_notification_channel_description">Ongoing call notifications</string>
<string name="stream_video_ongoing_call_notification_title">Call in progress</string>
<string name="stream_video_ongoing_call_notification_description">There is a call in progress, tap to go back to the call</string>
<string name="stream_video_ongoing_call_notification_title">Call in Progress</string>
<string name="stream_video_ongoing_group_call_notification_title">Group Call in Progress</string>
<string name="stream_video_ongoing_call_notification_description">Tap to go back to the call</string>
<string name="stream_video_call_setup_notification_channel_id" translatable="false">call_setup</string>
<string name="stream_video_call_setup_notification_channel_title">Call Setup</string>
<string name="stream_video_call_setup_notification_channel_description">Temporary notifications used while setting up calls</string>
Expand Down

0 comments on commit d5bb86e

Please sign in to comment.