Skip to content

Commit 8eaa2f8

Browse files
authored
Merge pull request #5811 from vector-im/feature/ons/voip_screen_sharing_permission
VoIP Screen Sharing Permission
2 parents c21ec98 + 56b3e8a commit 8eaa2f8

File tree

19 files changed

+263
-10
lines changed

19 files changed

+263
-10
lines changed

changelog.d/5811.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
VoIP Screen Sharing Permission

vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ class DebugVectorFeatures(
6060
override fun isLiveLocationEnabled(): Boolean = read(DebugFeatureKeys.liveLocationSharing)
6161
?: vectorFeatures.isLiveLocationEnabled()
6262

63+
override fun isScreenSharingEnabled(): Boolean = read(DebugFeatureKeys.screenSharing)
64+
?: vectorFeatures.isScreenSharingEnabled()
65+
6366
fun <T> override(value: T?, key: Preferences.Key<T>) = updatePreferences {
6467
if (value == null) {
6568
it.remove(key)
@@ -114,4 +117,5 @@ object DebugFeatureKeys {
114117
val onboardingPersonalize = booleanPreferencesKey("onboarding-personalize")
115118
val onboardingCombinedRegister = booleanPreferencesKey("onboarding-combined-register")
116119
val liveLocationSharing = booleanPreferencesKey("live-location-sharing")
120+
val screenSharing = booleanPreferencesKey("screen-sharing")
117121
}

vector/src/main/AndroidManifest.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,12 @@
375375
android:exported="false"
376376
android:foregroundServiceType="location" />
377377

378+
<service
379+
android:name=".features.call.webrtc.ScreenCaptureService"
380+
android:exported="false"
381+
android:foregroundServiceType="mediaProjection"
382+
tools:targetApi="Q" />
383+
378384
<!-- Receivers -->
379385

380386
<receiver

vector/src/main/java/im/vector/app/features/VectorFeatures.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ interface VectorFeatures {
2727
fun isOnboardingPersonalizeEnabled(): Boolean
2828
fun isOnboardingCombinedRegisterEnabled(): Boolean
2929
fun isLiveLocationEnabled(): Boolean
30+
fun isScreenSharingEnabled(): Boolean
3031

3132
enum class OnboardingVariant {
3233
LEGACY,
@@ -43,4 +44,5 @@ class DefaultVectorFeatures : VectorFeatures {
4344
override fun isOnboardingPersonalizeEnabled() = false
4445
override fun isOnboardingCombinedRegisterEnabled() = false
4546
override fun isLiveLocationEnabled(): Boolean = false
47+
override fun isScreenSharingEnabled(): Boolean = false
4648
}

vector/src/main/java/im/vector/app/features/call/CallControlsBottomSheet.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,17 @@ import dagger.hilt.android.AndroidEntryPoint
2727
import im.vector.app.R
2828
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
2929
import im.vector.app.databinding.BottomSheetCallControlsBinding
30+
import im.vector.app.features.VectorFeatures
31+
import javax.inject.Inject
3032

3133
@AndroidEntryPoint
3234
class CallControlsBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetCallControlsBinding>() {
3335
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetCallControlsBinding {
3436
return BottomSheetCallControlsBinding.inflate(inflater, container, false)
3537
}
3638

39+
@Inject lateinit var vectorFeatures: VectorFeatures
40+
3741
private val callViewModel: VectorCallViewModel by activityViewModel()
3842

3943
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -66,6 +70,12 @@ class CallControlsBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetC
6670
callViewModel.handle(VectorCallViewActions.InitiateCallTransfer)
6771
dismiss()
6872
}
73+
74+
views.callControlsShareScreen.isVisible = vectorFeatures.isScreenSharingEnabled()
75+
views.callControlsShareScreen.views.bottomSheetActionClickableZone.debouncedClicks {
76+
callViewModel.handle(VectorCallViewActions.ToggleScreenSharing)
77+
dismiss()
78+
}
6979
}
7080

7181
private fun renderState(state: VectorCallViewState) {
@@ -95,5 +105,6 @@ class CallControlsBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetC
95105
views.callControlsToggleHoldResume.leftIcon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_call_hold_action)
96106
}
97107
views.callControlsTransfer.isVisible = state.canOpponentBeTransferred
108+
views.callControlsShareScreen.title = getString(if (state.isSharingScreen) R.string.call_stop_screen_sharing else R.string.call_start_screen_sharing)
98109
}
99110
}

vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import android.content.Intent
2424
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
2525
import android.content.res.Configuration
2626
import android.graphics.Color
27+
import android.media.projection.MediaProjectionManager
2728
import android.os.Build
2829
import android.os.Bundle
2930
import android.os.Parcelable
@@ -56,6 +57,8 @@ import im.vector.app.features.call.dialpad.CallDialPadBottomSheet
5657
import im.vector.app.features.call.dialpad.DialPadFragment
5758
import im.vector.app.features.call.transfer.CallTransferActivity
5859
import im.vector.app.features.call.utils.EglUtils
60+
import im.vector.app.features.call.webrtc.ScreenCaptureService
61+
import im.vector.app.features.call.webrtc.ScreenCaptureServiceConnection
5962
import im.vector.app.features.call.webrtc.WebRtcCall
6063
import im.vector.app.features.call.webrtc.WebRtcCallManager
6164
import im.vector.app.features.displayname.getBestName
@@ -94,6 +97,7 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
9497

9598
@Inject lateinit var callManager: WebRtcCallManager
9699
@Inject lateinit var avatarRenderer: AvatarRenderer
100+
@Inject lateinit var screenCaptureServiceConnection: ScreenCaptureServiceConnection
97101

98102
private val callViewModel: VectorCallViewModel by viewModel()
99103

@@ -512,20 +516,22 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
512516
private fun handleViewEvents(event: VectorCallViewEvents?) {
513517
Timber.tag(loggerTag.value).v("handleViewEvents $event")
514518
when (event) {
515-
is VectorCallViewEvents.ConnectionTimeout -> {
519+
is VectorCallViewEvents.ConnectionTimeout -> {
516520
onErrorTimoutConnect(event.turn)
517521
}
518-
is VectorCallViewEvents.ShowDialPad -> {
522+
is VectorCallViewEvents.ShowDialPad -> {
519523
CallDialPadBottomSheet.newInstance(false).apply {
520524
callback = dialPadCallback
521525
}.show(supportFragmentManager, FRAGMENT_DIAL_PAD_TAG)
522526
}
523-
is VectorCallViewEvents.ShowCallTransferScreen -> {
527+
is VectorCallViewEvents.ShowCallTransferScreen -> {
524528
val callId = withState(callViewModel) { it.callId }
525529
navigator.openCallTransfer(this, callTransferActivityResultLauncher, callId)
526530
}
527-
is VectorCallViewEvents.FailToTransfer -> showSnackbar(getString(R.string.call_transfer_failure))
528-
else -> Unit
531+
is VectorCallViewEvents.FailToTransfer -> showSnackbar(getString(R.string.call_transfer_failure))
532+
is VectorCallViewEvents.ShowScreenSharingPermissionDialog -> handleShowScreenSharingPermissionDialog()
533+
is VectorCallViewEvents.StopScreenSharingService -> handleStopScreenSharingService()
534+
else -> Unit
529535
}
530536
}
531537

@@ -628,6 +634,32 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
628634
}
629635
}
630636

637+
private val screenSharingPermissionActivityResultLauncher = registerStartForActivityResult { activityResult ->
638+
if (activityResult.resultCode == Activity.RESULT_OK) {
639+
callViewModel.handle(VectorCallViewActions.StartScreenSharing)
640+
// We need to start a foreground service with a sticky notification during screen sharing
641+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
642+
ContextCompat.startForegroundService(
643+
this,
644+
Intent(this, ScreenCaptureService::class.java)
645+
)
646+
screenCaptureServiceConnection.bind()
647+
}
648+
}
649+
}
650+
651+
private fun handleShowScreenSharingPermissionDialog() {
652+
getSystemService<MediaProjectionManager>()?.let {
653+
navigator.openScreenSharingPermissionDialog(it.createScreenCaptureIntent(), screenSharingPermissionActivityResultLauncher)
654+
}
655+
}
656+
657+
private fun handleStopScreenSharingService() {
658+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
659+
screenCaptureServiceConnection.stopScreenCapturing()
660+
}
661+
}
662+
631663
companion object {
632664
private const val EXTRA_MODE = "EXTRA_MODE"
633665
private const val FRAGMENT_DIAL_PAD_TAG = "FRAGMENT_DIAL_PAD_TAG"

vector/src/main/java/im/vector/app/features/call/VectorCallViewActions.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,6 @@ sealed class VectorCallViewActions : VectorViewModelAction {
4040
object CallTransferSelectionCancelled : VectorCallViewActions()
4141
data class CallTransferSelectionResult(val callTransferResult: CallTransferResult) : VectorCallViewActions()
4242
object TransferCall : VectorCallViewActions()
43+
object ToggleScreenSharing : VectorCallViewActions()
44+
object StartScreenSharing : VectorCallViewActions()
4345
}

vector/src/main/java/im/vector/app/features/call/VectorCallViewEvents.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ sealed class VectorCallViewEvents : VectorViewEvents {
3131
object ShowDialPad : VectorCallViewEvents()
3232
object ShowCallTransferScreen : VectorCallViewEvents()
3333
object FailToTransfer : VectorCallViewEvents()
34-
// data class CallAnswered(val content: CallAnswerContent) : VectorCallViewEvents()
35-
// data class CallHangup(val content: CallHangupContent) : VectorCallViewEvents()
36-
// object CallAccepted : VectorCallViewEvents()
34+
object ShowScreenSharingPermissionDialog : VectorCallViewEvents()
35+
object StopScreenSharingService : VectorCallViewEvents()
3736
}

vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,10 @@ class VectorCallViewModel @AssistedInject constructor(
256256

257257
override fun handle(action: VectorCallViewActions) = withState { state ->
258258
when (action) {
259-
VectorCallViewActions.EndCall -> call?.endCall()
259+
VectorCallViewActions.EndCall -> {
260+
call?.endCall()
261+
_viewEvents.post(VectorCallViewEvents.StopScreenSharingService)
262+
}
260263
VectorCallViewActions.AcceptCall -> {
261264
setState {
262265
copy(callState = Loading())
@@ -341,6 +344,31 @@ class VectorCallViewModel @AssistedInject constructor(
341344
setState { VectorCallViewState(action.callArgs) }
342345
setupCallWithCurrentState()
343346
}
347+
is VectorCallViewActions.ToggleScreenSharing -> {
348+
handleToggleScreenSharing(state.isSharingScreen)
349+
}
350+
is VectorCallViewActions.StartScreenSharing -> {
351+
call?.startSharingScreen()
352+
setState {
353+
copy(isSharingScreen = true)
354+
}
355+
}
356+
}
357+
}
358+
359+
private fun handleToggleScreenSharing(isSharingScreen: Boolean) {
360+
if (isSharingScreen) {
361+
call?.stopSharingScreen()
362+
setState {
363+
copy(isSharingScreen = false)
364+
}
365+
_viewEvents.post(
366+
VectorCallViewEvents.StopScreenSharingService
367+
)
368+
} else {
369+
_viewEvents.post(
370+
VectorCallViewEvents.ShowScreenSharingPermissionDialog
371+
)
344372
}
345373
}
346374

vector/src/main/java/im/vector/app/features/call/VectorCallViewState.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ data class VectorCallViewState(
4242
val callInfo: CallInfo? = null,
4343
val formattedDuration: String = "",
4444
val canOpponentBeTransferred: Boolean = false,
45-
val transferee: TransfereeState = TransfereeState.NoTransferee
45+
val transferee: TransfereeState = TransfereeState.NoTransferee,
46+
val isSharingScreen: Boolean = false
4647
) : MavericksState {
4748

4849
sealed class TransfereeState {

0 commit comments

Comments
 (0)