1 /*
2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.core.telecom.extensions
18 
19 import android.net.Uri
20 import android.telecom.Call
21 import androidx.core.telecom.util.ExperimentalAppActions
22 
23 /**
24  * Provides a scope where extensions can be first initialized and next managed for a [Call] once
25  * [onConnected] is called.
26  *
27  * The following extension is supported on a call:
28  * - [addParticipantExtension] - Show the user more information about the [Participant]s in the
29  *   call.
30  *
31  * ```
32  * class InCallServiceImpl : InCallServiceCompat() {
33  * ...
34  *   override fun onCallAdded(call: Call) {
35  *     lifecycleScope.launch {
36  *       connectExtensions(context, call) {
37  *         // Initialize extensions
38  *         onConnected { call ->
39  *           // change call states & listen for extension updates/send extension actions
40  *         }
41  *       }
42  *       // Once the call is destroyed, control flow will resume again
43  *     }
44  *   }
45  *  ...
46  * }
47  * ```
48  */
49 @ExperimentalAppActions
50 public interface CallExtensionScope {
51 
52     /**
53      * Called when the [Call] extensions have been successfully set up and are ready to be used.
54      *
55      * @param block Called when the [Call] and initialized extensions are ready to be used.
56      */
onConnectednull57     public fun onConnected(block: suspend (Call) -> Unit)
58 
59     /**
60      * Add support for this remote surface to display information related to the [Participant]s in
61      * this call.
62      *
63      * ```
64      * connectExtensions(call) {
65      *     val participantExtension = addParticipantExtension(
66      *         // consume participant changed events
67      *     )
68      *     onConnected {
69      *         // extensions have been negotiated and actions are ready to be used
70      *     }
71      * }
72      * ```
73      *
74      * @param onActiveParticipantChanged Called with the active [Participant] in the call has
75      *   changed. If this method is called with a `null` [Participant], there is no active
76      *   [Participant]. The active [Participant] in the call is the [Participant] that should take
77      *   focus and be either more prominent on the screen or otherwise featured as active in UI. For
78      *   example, this could be the [Participant] that is actively talking or presenting.
79      * @param onParticipantsUpdated Called when the [Participant]s in the [Call] have changed and
80      *   the UI should be updated.
81      * @return The interface that is used to set up additional actions for this extension.
82      */
83     public fun addParticipantExtension(
84         onActiveParticipantChanged: suspend (Participant?) -> Unit,
85         onParticipantsUpdated: suspend (Set<Participant>) -> Unit
86     ): ParticipantExtensionRemote
87 
88     /**
89      * Add support for this remote surface to display meeting summary information for this call.
90      *
91      * This function establishes a connection with a remote service that provides meeting summary
92      * information, such as the current speaker and the number of participants. The extension will
93      * provide updates via the provided callbacks:
94      *
95      * @param onCurrentSpeakerChanged A suspend function that is called whenever the current speaker
96      *   in the meeting changes. The function receives a [CharSequence] representing the new
97      *   speaker's identifier (e.g., name or ID) or null if there is no current speaker.
98      * @param onParticipantCountChanged A suspend function that is called whenever the number of
99      *   participants in the meeting changes. It receives the new participant count as an [Int],
100      *   which is always 0 or greater.
101      * @return A [MeetingSummaryRemote] object with an `isSupported` property of this object will
102      *   indicate whether the meeting summary extension is supported by the calling application.
103      *
104      * Example Usage:
105      * ```kotlin
106      * connectExtensions(call) {
107      *     val meetingSummaryRemote =  addMeetingSummaryExtension(
108      *          onCurrentSpeakerChanged = { speaker ->
109      *              // Update UI with the new speaker
110      *              Log.d(TAG, "Current speaker: $speaker")
111      *         },
112      *         onParticipantCountChanged = { count ->
113      *             // Update UI with the new participant count
114      *             Log.d(TAG, "Participant count: $count")
115      *         }
116      *     )
117      *    onConnected {
118      *       if (meetingSummaryRemote.isSupported) {
119      *          // The extension is ready to use
120      *       } else {
121      *          // Handle the case where the extension is not supported.
122      *       }
123      *    }
124      * }
125      *  ```
126      */
127     public fun addMeetingSummaryExtension(
128         onCurrentSpeakerChanged: suspend (CharSequence?) -> Unit,
129         onParticipantCountChanged: suspend (Int) -> Unit
130     ): MeetingSummaryRemote
131 
132     /**
133      * Add support for this remote surface to display information related to the local call silence
134      * state for this call.
135      *
136      * ```
137      * connectExtensions(call) {
138      *     val localCallSilenceExtension = addLocalCallSilenceExtension(
139      *         // consume local call silence state changes
140      *     )
141      *     onConnected {
142      *         // At this point, support for the local call silence extension will be known
143      *     }
144      * }
145      * ```
146      *
147      * @param onIsLocallySilencedUpdated Called when the local call silence state has changed and
148      *   the UI should be updated.
149      * @return The interface that is used to interact with the local call silence extension methods.
150      */
151     public fun addLocalCallSilenceExtension(
152         onIsLocallySilencedUpdated: suspend (Boolean) -> Unit
153     ): LocalCallSilenceExtensionRemote
154 
155     /**
156      * Add support for call icon updates and provides a callback to receive those updates. This
157      * remote surface should implement a [android.database.ContentObserver] to observe changes to
158      * the icon's content URI. This is necessary to ensure the displayed icon reflects any updates
159      * made by the application if the URI remains the same.
160      *
161      * ```
162      * connectExtensions(call) {
163      *     val callIconExtension = addCallIconSupport(
164      *         // consume call icon state changes
165      *     )
166      *     onConnected {
167      *         // At this point, support for call icon extension will be known
168      *     }
169      * }
170      * ```
171      *
172      * @param onCallIconChanged A suspend function that will be invoked with the [Uri] of the new
173      *   call icon whenever it changes. This callback will only be called if the calling application
174      *   supports the call icon extension (i.e., `isSupported` returns `true`).
175      * @return A [CallIconExtensionRemote] instance that allows the remote to check if the calling
176      *   application supports the call icon extension. The remote *must* use this instance to check
177      *   support before expecting icon updates.
178      */
179     public fun addCallIconSupport(onCallIconChanged: suspend (Uri) -> Unit): CallIconExtensionRemote
180 }
181