Jitsi: handle switch to another conference, now possible since we support PiP

This commit is contained in:
Benoit Marty 2021-02-23 12:36:45 +01:00
parent d5f0a6d66a
commit 5e4219cf8e
6 changed files with 110 additions and 14 deletions

View File

@ -18,4 +18,12 @@ package im.vector.app.features.call.conference
import im.vector.app.core.platform.VectorViewModelAction
sealed class JitsiCallViewActions : VectorViewModelAction
sealed class JitsiCallViewActions : VectorViewModelAction {
data class SwitchTo(val args: VectorJitsiActivity.Args,
val withConfirmation: Boolean) : JitsiCallViewActions()
/**
* The ViewModel will either ask the View to finish, or to join another conf.
*/
object OnConferenceLeft: JitsiCallViewActions()
}

View File

@ -27,4 +27,11 @@ sealed class JitsiCallViewEvents : VectorViewEvents {
val confId: String,
val userInfo: JitsiMeetUserInfo
) : JitsiCallViewEvents()
data class ConfirmSwitchingConference(
val args: VectorJitsiActivity.Args
) : JitsiCallViewEvents()
object LeaveConference : JitsiCallViewEvents()
object Finish : JitsiCallViewEvents()
}

View File

@ -19,12 +19,15 @@ package im.vector.app.features.call.conference
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.themes.ThemeProvider
import io.reactivex.disposables.Disposable
import org.jitsi.meet.sdk.JitsiMeetUserInfo
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.Session
@ -36,7 +39,6 @@ import java.net.URL
class JitsiCallViewModel @AssistedInject constructor(
@Assisted initialState: JitsiCallViewState,
@Assisted private val args: VectorJitsiActivity.Args,
private val session: Session,
private val jitsiMeetPropertiesFactory: JitsiWidgetPropertiesFactory,
private val themeProvider: ThemeProvider
@ -44,15 +46,23 @@ class JitsiCallViewModel @AssistedInject constructor(
@AssistedFactory
interface Factory {
fun create(initialState: JitsiCallViewState, args: VectorJitsiActivity.Args): JitsiCallViewModel
fun create(initialState: JitsiCallViewState): JitsiCallViewModel
}
private var currentWidgetObserver: Disposable? = null
private val widgetService = session.widgetService()
private var confIsStarted = false
private var pendingArgs: VectorJitsiActivity.Args? = null
init {
widgetService.getRoomWidgetsLive(args.roomId, QueryStringValue.Equals(args.widgetId), WidgetType.Jitsi.values())
observeWidget(initialState.roomId, initialState.widgetId)
}
private fun observeWidget(roomId: String, widgetId: String) {
confIsStarted = false
currentWidgetObserver?.dispose()
currentWidgetObserver = widgetService.getRoomWidgetsLive(roomId, QueryStringValue.Equals(widgetId), WidgetType.Jitsi.values())
.asObservable()
.distinctUntilChanged()
.subscribe {
@ -77,19 +87,19 @@ class JitsiCallViewModel @AssistedInject constructor(
.disposeOnClear()
}
private fun startConference(jitsiWidget: Widget) {
val me = session.getRoomMember(session.myUserId, args.roomId)?.toMatrixItem()
private fun startConference(jitsiWidget: Widget) = withState { state ->
val me = session.getRoomMember(session.myUserId, state.roomId)?.toMatrixItem()
val userInfo = JitsiMeetUserInfo().apply {
displayName = me?.getBestName()
avatar = me?.avatarUrl?.let { session.contentUrlResolver().resolveFullSize(it) }?.let { URL(it) }
}
val roomName = session.getRoomSummary(args.roomId)?.displayName
val roomName = session.getRoomSummary(state.roomId)?.displayName
val ppt = widgetService.getWidgetComputedUrl(jitsiWidget, themeProvider.isLightTheme())
?.let { url -> jitsiMeetPropertiesFactory.create(url) }
_viewEvents.post(JitsiCallViewEvents.StartConference(
enableVideo = args.enableVideo,
enableVideo = state.enableVideo,
jitsiUrl = "https://${ppt?.domain}",
subject = roomName ?: "",
confId = ppt?.confId ?: "",
@ -98,6 +108,45 @@ class JitsiCallViewModel @AssistedInject constructor(
}
override fun handle(action: JitsiCallViewActions) {
when (action) {
is JitsiCallViewActions.SwitchTo -> handleSwitchTo(action)
JitsiCallViewActions.OnConferenceLeft -> handleOnConferenceLeft()
}.exhaustive
}
private fun handleSwitchTo(action: JitsiCallViewActions.SwitchTo) = withState { state ->
// Check if it is the same conf
if (action.args.roomId != state.roomId
|| action.args.widgetId != state.widgetId) {
if (action.withConfirmation) {
// Ask confirmation to switch
_viewEvents.post(JitsiCallViewEvents.ConfirmSwitchingConference(action.args))
} else {
// Ask the view to leave the conf, then the view will tell us when it's done, to join the new conf
pendingArgs = action.args
_viewEvents.post(JitsiCallViewEvents.LeaveConference)
}
}
}
private fun handleOnConferenceLeft() {
val safePendingArgs = pendingArgs
pendingArgs = null
if (safePendingArgs == null) {
// Quit
_viewEvents.post(JitsiCallViewEvents.Finish)
} else {
setState {
copy(
roomId = safePendingArgs.roomId,
widgetId = safePendingArgs.widgetId,
enableVideo = safePendingArgs.enableVideo,
widget = Uninitialized
)
}
observeWidget(safePendingArgs.roomId, safePendingArgs.widgetId)
}
}
companion object : MvRxViewModelFactory<JitsiCallViewModel, JitsiCallViewState> {
@ -107,8 +156,7 @@ class JitsiCallViewModel @AssistedInject constructor(
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: JitsiCallViewState): JitsiCallViewModel? {
val callActivity: VectorJitsiActivity = viewModelContext.activity()
val callArgs: VectorJitsiActivity.Args = viewModelContext.args()
return callActivity.viewModelFactory.create(state, callArgs)
return callActivity.viewModelFactory.create(state)
}
override fun initialState(viewModelContext: ViewModelContext): JitsiCallViewState? {
@ -116,7 +164,8 @@ class JitsiCallViewModel @AssistedInject constructor(
return JitsiCallViewState(
roomId = args.roomId,
widgetId = args.widgetId
widgetId = args.widgetId,
enableVideo = args.enableVideo
)
}
}

View File

@ -24,5 +24,6 @@ import org.matrix.android.sdk.api.session.widgets.model.Widget
data class JitsiCallViewState(
val roomId: String = "",
val widgetId: String = "",
val enableVideo: Boolean = false,
val widget: Async<Widget> = Uninitialized
) : MvRxState

View File

@ -25,6 +25,7 @@ import android.content.res.Configuration
import android.os.Bundle
import android.os.Parcelable
import android.widget.FrameLayout
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.airbnb.mvrx.Fail
@ -32,6 +33,7 @@ import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.viewModel
import com.facebook.react.modules.core.PermissionListener
import im.vector.app.R
import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorBaseActivity
@ -85,13 +87,31 @@ class VectorJitsiActivity : VectorBaseActivity<ActivityJitsiBinding>(), JitsiMee
jitsiViewModel.observeViewEvents {
when (it) {
is JitsiCallViewEvents.StartConference -> configureJitsiView(it)
is JitsiCallViewEvents.StartConference -> configureJitsiView(it)
is JitsiCallViewEvents.ConfirmSwitchingConference -> handleConfirmSwitching(it)
JitsiCallViewEvents.Finish -> finish()
JitsiCallViewEvents.LeaveConference -> handleLeaveConference()
}.exhaustive
}
registerForBroadcastMessages()
}
private fun handleLeaveConference() {
jitsiMeetView?.leave()
}
private fun handleConfirmSwitching(action: JitsiCallViewEvents.ConfirmSwitchingConference) {
AlertDialog.Builder(this)
.setTitle(R.string.dialog_title_warning)
.setMessage(R.string.jitsi_leave_conf_to_join_another_one_content)
.setPositiveButton(R.string.action_switch) { _, _ ->
jitsiViewModel.handle(JitsiCallViewActions.SwitchTo(action.args, false))
}
.setNegativeButton(R.string.cancel, null)
.show()
}
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean,
newConfig: Configuration) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
@ -170,6 +190,14 @@ class VectorJitsiActivity : VectorBaseActivity<ActivityJitsiBinding>(), JitsiMee
override fun onNewIntent(intent: Intent?) {
JitsiMeetActivityDelegate.onNewIntent(intent)
// Is it a switch to another conf?
intent?.takeIf { it.hasExtra(MvRx.KEY_ARG) }
?.let { intent.getParcelableExtra<Args>(MvRx.KEY_ARG) }
?.let {
jitsiViewModel.handle(JitsiCallViewActions.SwitchTo(it, true))
}
super.onNewIntent(intent)
}
@ -211,7 +239,7 @@ class VectorJitsiActivity : VectorBaseActivity<ActivityJitsiBinding>(), JitsiMee
Timber.v("JitsiMeetViewListener.onConferenceTerminated()")
// Do not finish if there is an error
if (data["error"] == null) {
finish()
jitsiViewModel.handle(JitsiCallViewActions.OnConferenceLeft)
}
}

View File

@ -133,6 +133,7 @@
<string name="action_close">Close</string>
<string name="action_copy">Copy</string>
<string name="action_add">Add</string>
<string name="action_switch">Switch</string>
<string name="action_unpublish">Unpublish</string>
<string name="copied_to_clipboard">Copied to clipboard</string>
<string name="disable">Disable</string>
@ -1318,6 +1319,8 @@
<string name="error_jitsi_not_supported_on_old_device">Sorry, conference calls with Jitsi are not supported on old devices (devices with Android OS below 6.0)</string>
<string name="jitsi_leave_conf_to_join_another_one_content">Leave the current conference and switch to the other one?</string>
<string name="room_widget_resource_permission_title">This widget wants to use the following resources:</string>
<string name="room_widget_resource_grant_permission">Allow</string>
<string name="room_widget_resource_decline_permission">Block All</string>