Skip to content

Commit 9ea0a11

Browse files
authored
Merge pull request #3815 from element-hq/feature/bma/hideJoinCallButton
Hide join call button when the user is already in the call
2 parents 4620bb8 + db4b4d3 commit 9ea0a11

File tree

46 files changed

+742
-157
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+742
-157
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2024 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only
5+
* Please see LICENSE in the repository root for full details.
6+
*/
7+
8+
package io.element.android.features.call.api
9+
10+
import io.element.android.libraries.matrix.api.core.RoomId
11+
12+
/**
13+
* Value for the local current call.
14+
*/
15+
sealed interface CurrentCall {
16+
data object None : CurrentCall
17+
18+
data class RoomCall(
19+
val roomId: RoomId,
20+
) : CurrentCall
21+
22+
data class ExternalUrl(
23+
val url: String,
24+
) : CurrentCall
25+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Copyright 2024 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only
5+
* Please see LICENSE in the repository root for full details.
6+
*/
7+
8+
package io.element.android.features.call.api
9+
10+
import kotlinx.coroutines.flow.StateFlow
11+
12+
interface CurrentCallService {
13+
/**
14+
* The current call state flow, which will be updated when the active call changes.
15+
* This value reflect the local state of the call. It is not updated if the user answers
16+
* a call from another session.
17+
*/
18+
val currentCall: StateFlow<CurrentCall>
19+
}

features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import androidx.core.app.NotificationManagerCompat
1212
import com.squareup.anvil.annotations.ContributesBinding
1313
import io.element.android.appconfig.ElementCallConfig
1414
import io.element.android.features.call.api.CallType
15+
import io.element.android.features.call.api.CurrentCall
1516
import io.element.android.features.call.impl.notifications.CallNotificationData
1617
import io.element.android.features.call.impl.notifications.RingingCallNotificationCreator
1718
import io.element.android.libraries.di.AppScope
@@ -82,13 +83,15 @@ class DefaultActiveCallManager @Inject constructor(
8283
private val ringingCallNotificationCreator: RingingCallNotificationCreator,
8384
private val notificationManagerCompat: NotificationManagerCompat,
8485
private val matrixClientProvider: MatrixClientProvider,
86+
private val defaultCurrentCallService: DefaultCurrentCallService,
8587
) : ActiveCallManager {
8688
private var timedOutCallJob: Job? = null
8789

8890
override val activeCall = MutableStateFlow<ActiveCall?>(null)
8991

9092
init {
9193
observeRingingCall()
94+
observeCurrentCall()
9295
}
9396

9497
override fun registerIncomingCall(notificationData: CallNotificationData) {
@@ -209,6 +212,28 @@ class DefaultActiveCallManager @Inject constructor(
209212
}
210213
.launchIn(coroutineScope)
211214
}
215+
216+
private fun observeCurrentCall() {
217+
activeCall
218+
.onEach { value ->
219+
if (value == null) {
220+
defaultCurrentCallService.onCallEnded()
221+
} else {
222+
when (value.callState) {
223+
is CallState.Ringing -> {
224+
// Nothing to do
225+
}
226+
is CallState.InCall -> {
227+
when (val callType = value.callType) {
228+
is CallType.ExternalUrl -> defaultCurrentCallService.onCallStarted(CurrentCall.ExternalUrl(callType.url))
229+
is CallType.RoomCall -> defaultCurrentCallService.onCallStarted(CurrentCall.RoomCall(callType.roomId))
230+
}
231+
}
232+
}
233+
}
234+
}
235+
.launchIn(coroutineScope)
236+
}
212237
}
213238

214239
/**
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright 2024 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only
5+
* Please see LICENSE in the repository root for full details.
6+
*/
7+
8+
package io.element.android.features.call.impl.utils
9+
10+
import com.squareup.anvil.annotations.ContributesBinding
11+
import io.element.android.features.call.api.CurrentCall
12+
import io.element.android.features.call.api.CurrentCallService
13+
import io.element.android.libraries.di.AppScope
14+
import io.element.android.libraries.di.SingleIn
15+
import kotlinx.coroutines.flow.MutableStateFlow
16+
import javax.inject.Inject
17+
18+
@SingleIn(AppScope::class)
19+
@ContributesBinding(AppScope::class)
20+
class DefaultCurrentCallService @Inject constructor() : CurrentCallService {
21+
override val currentCall = MutableStateFlow<CurrentCall>(CurrentCall.None)
22+
23+
fun onCallStarted(call: CurrentCall) {
24+
currentCall.value = call
25+
}
26+
27+
fun onCallEnded() {
28+
currentCall.value = CurrentCall.None
29+
}
30+
}

features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import io.element.android.features.call.impl.notifications.RingingCallNotificati
1515
import io.element.android.features.call.impl.utils.ActiveCall
1616
import io.element.android.features.call.impl.utils.CallState
1717
import io.element.android.features.call.impl.utils.DefaultActiveCallManager
18+
import io.element.android.features.call.impl.utils.DefaultCurrentCallService
1819
import io.element.android.features.call.test.aCallNotificationData
1920
import io.element.android.libraries.matrix.api.core.EventId
2021
import io.element.android.libraries.matrix.api.core.RoomId
@@ -299,5 +300,6 @@ class DefaultActiveCallManagerTest {
299300
),
300301
notificationManagerCompat = notificationManagerCompat,
301302
matrixClientProvider = matrixClientProvider,
303+
defaultCurrentCallService = DefaultCurrentCallService(),
302304
)
303305
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright 2024 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only
5+
* Please see LICENSE in the repository root for full details.
6+
*/
7+
8+
package io.element.android.features.call.test
9+
10+
import io.element.android.features.call.api.CurrentCall
11+
import io.element.android.features.call.api.CurrentCallService
12+
import kotlinx.coroutines.flow.MutableStateFlow
13+
14+
class FakeCurrentCallService(
15+
override val currentCall: MutableStateFlow<CurrentCall> = MutableStateFlow(CurrentCall.None),
16+
) : CurrentCallService

features/messages/impl/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ dependencies {
2929
implementation(projects.features.call.api)
3030
implementation(projects.features.location.api)
3131
implementation(projects.features.poll.api)
32+
implementation(projects.features.roomcall.api)
3233
implementation(projects.libraries.androidutils)
3334
implementation(projects.libraries.core)
3435
implementation(projects.libraries.architecture)

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import io.element.android.features.messages.impl.timeline.protection.TimelinePro
5050
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState
5151
import io.element.android.features.networkmonitor.api.NetworkMonitor
5252
import io.element.android.features.networkmonitor.api.NetworkStatus
53+
import io.element.android.features.roomcall.api.RoomCallState
5354
import io.element.android.libraries.androidutils.clipboard.ClipboardHelper
5455
import io.element.android.libraries.architecture.AsyncData
5556
import io.element.android.libraries.architecture.Presenter
@@ -75,7 +76,6 @@ import io.element.android.libraries.matrix.api.room.powerlevels.canSendMessage
7576
import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId
7677
import io.element.android.libraries.matrix.ui.messages.reply.map
7778
import io.element.android.libraries.matrix.ui.model.getAvatarData
78-
import io.element.android.libraries.matrix.ui.room.canCall
7979
import io.element.android.libraries.textcomposer.model.MessageComposerMode
8080
import io.element.android.libraries.ui.strings.CommonStrings
8181
import io.element.android.services.analytics.api.AnalyticsService
@@ -98,6 +98,7 @@ class MessagesPresenter @AssistedInject constructor(
9898
private val reactionSummaryPresenter: Presenter<ReactionSummaryState>,
9999
private val readReceiptBottomSheetPresenter: Presenter<ReadReceiptBottomSheetState>,
100100
private val pinnedMessagesBannerPresenter: Presenter<PinnedMessagesBannerState>,
101+
private val roomCallStatePresenter: Presenter<RoomCallState>,
101102
private val networkMonitor: NetworkMonitor,
102103
private val snackbarDispatcher: SnackbarDispatcher,
103104
private val dispatchers: CoroutineDispatchers,
@@ -133,6 +134,7 @@ class MessagesPresenter @AssistedInject constructor(
133134
val reactionSummaryState = reactionSummaryPresenter.present()
134135
val readReceiptBottomSheetState = readReceiptBottomSheetPresenter.present()
135136
val pinnedMessagesBannerState = pinnedMessagesBannerPresenter.present()
137+
val roomCallState = roomCallStatePresenter.present()
136138

137139
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
138140

@@ -152,8 +154,6 @@ class MessagesPresenter @AssistedInject constructor(
152154
mutableStateOf(false)
153155
}
154156

155-
val canJoinCall by room.canCall(updateKey = syncUpdateFlow.value)
156-
157157
LaunchedEffect(Unit) {
158158
// Remove the unread flag on entering but don't send read receipts
159159
// as those will be handled by the timeline.
@@ -204,12 +204,6 @@ class MessagesPresenter @AssistedInject constructor(
204204
}
205205
}
206206

207-
val callState = when {
208-
!canJoinCall -> RoomCallState.DISABLED
209-
roomInfo?.hasRoomCall == true -> RoomCallState.ONGOING
210-
else -> RoomCallState.ENABLED
211-
}
212-
213207
return MessagesState(
214208
roomId = room.roomId,
215209
roomName = roomName,
@@ -232,7 +226,7 @@ class MessagesPresenter @AssistedInject constructor(
232226
enableTextFormatting = MessageComposerConfig.ENABLE_RICH_TEXT_EDITING,
233227
enableVoiceMessages = enableVoiceMessages,
234228
appName = buildMeta.applicationName,
235-
callState = callState,
229+
roomCallState = roomCallState,
236230
pinnedMessagesBannerState = pinnedMessagesBannerState,
237231
eventSink = { handleEvents(it) }
238232
)

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import io.element.android.features.messages.impl.timeline.components.reactionsum
1818
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState
1919
import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState
2020
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState
21+
import io.element.android.features.roomcall.api.RoomCallState
2122
import io.element.android.libraries.architecture.AsyncData
2223
import io.element.android.libraries.designsystem.components.avatar.AvatarData
2324
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
@@ -46,14 +47,8 @@ data class MessagesState(
4647
val showReinvitePrompt: Boolean,
4748
val enableTextFormatting: Boolean,
4849
val enableVoiceMessages: Boolean,
49-
val callState: RoomCallState,
50+
val roomCallState: RoomCallState,
5051
val appName: String,
5152
val pinnedMessagesBannerState: PinnedMessagesBannerState,
5253
val eventSink: (MessagesEvents) -> Unit
5354
)
54-
55-
enum class RoomCallState {
56-
ENABLED,
57-
ONGOING,
58-
DISABLED
59-
}

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ import io.element.android.features.messages.impl.timeline.protection.aTimelinePr
3333
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState
3434
import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessageComposerState
3535
import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessagePreviewState
36+
import io.element.android.features.roomcall.api.RoomCallState
37+
import io.element.android.features.roomcall.api.aStandByCallState
38+
import io.element.android.features.roomcall.api.anOngoingCallState
3639
import io.element.android.libraries.architecture.AsyncData
3740
import io.element.android.libraries.designsystem.components.avatar.AvatarData
3841
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
@@ -70,7 +73,7 @@ open class MessagesStateProvider : PreviewParameterProvider<MessagesState> {
7073
),
7174
),
7275
aMessagesState(
73-
callState = RoomCallState.ONGOING,
76+
roomCallState = anOngoingCallState(),
7477
),
7578
aMessagesState(
7679
enableVoiceMessages = true,
@@ -80,7 +83,7 @@ open class MessagesStateProvider : PreviewParameterProvider<MessagesState> {
8083
),
8184
),
8285
aMessagesState(
83-
callState = RoomCallState.DISABLED,
86+
roomCallState = aStandByCallState(canStartCall = false),
8487
),
8588
aMessagesState(
8689
pinnedMessagesBannerState = aLoadedPinnedMessagesBannerState(
@@ -115,7 +118,7 @@ fun aMessagesState(
115118
hasNetworkConnection: Boolean = true,
116119
showReinvitePrompt: Boolean = false,
117120
enableVoiceMessages: Boolean = true,
118-
callState: RoomCallState = RoomCallState.ENABLED,
121+
roomCallState: RoomCallState = aStandByCallState(),
119122
pinnedMessagesBannerState: PinnedMessagesBannerState = aLoadedPinnedMessagesBannerState(),
120123
eventSink: (MessagesEvents) -> Unit = {},
121124
) = MessagesState(
@@ -139,7 +142,7 @@ fun aMessagesState(
139142
showReinvitePrompt = showReinvitePrompt,
140143
enableTextFormatting = true,
141144
enableVoiceMessages = enableVoiceMessages,
142-
callState = callState,
145+
roomCallState = roomCallState,
143146
appName = "Element",
144147
pinnedMessagesBannerState = pinnedMessagesBannerState,
145148
eventSink = eventSink,

0 commit comments

Comments
 (0)