Jitsi call: fix PiP

This commit is contained in:
ganfra 2021-07-05 15:44:36 +02:00
parent 6b6fb03dce
commit 7f74278a73

View file

@ -16,22 +16,22 @@
package im.vector.app.features.call.conference package im.vector.app.features.call.conference
import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.Toast import android.widget.Toast
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.lifecycle.Lifecycle
import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.viewModel
import com.facebook.react.bridge.JavaOnlyMap
import com.facebook.react.modules.core.PermissionListener import com.facebook.react.modules.core.PermissionListener
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import im.vector.app.R import im.vector.app.R
@ -40,7 +40,9 @@ import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivityJitsiBinding import im.vector.app.databinding.ActivityJitsiBinding
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.jitsi.meet.sdk.BroadcastEmitter
import org.jitsi.meet.sdk.BroadcastEvent import org.jitsi.meet.sdk.BroadcastEvent
import org.jitsi.meet.sdk.JitsiMeet
import org.jitsi.meet.sdk.JitsiMeetActivityDelegate import org.jitsi.meet.sdk.JitsiMeetActivityDelegate
import org.jitsi.meet.sdk.JitsiMeetActivityInterface import org.jitsi.meet.sdk.JitsiMeetActivityInterface
import org.jitsi.meet.sdk.JitsiMeetConferenceOptions import org.jitsi.meet.sdk.JitsiMeetConferenceOptions
@ -71,13 +73,6 @@ class VectorJitsiActivity : VectorBaseActivity<ActivityJitsiBinding>(), JitsiMee
injector.inject(this) injector.inject(this)
} }
// See https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-android-sdk#listening-for-broadcasted-events
private val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
intent?.let { onBroadcastReceived(it) }
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -94,8 +89,48 @@ class VectorJitsiActivity : VectorBaseActivity<ActivityJitsiBinding>(), JitsiMee
JitsiCallViewEvents.LeaveConference -> handleLeaveConference() JitsiCallViewEvents.LeaveConference -> handleLeaveConference()
}.exhaustive }.exhaustive
} }
lifecycle.addObserver(JitsiBroadcastEventObserver(this, this::onBroadcastEvent))
}
registerForBroadcastMessages() override fun onResume() {
super.onResume()
JitsiMeetActivityDelegate.onHostResume(this)
}
override fun initUiAndData() {
super.initUiAndData()
jitsiMeetView = JitsiMeetView(this)
val params = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
views.jitsiLayout.addView(jitsiMeetView, params)
}
override fun onStop() {
JitsiMeetActivityDelegate.onHostPause(this)
super.onStop()
}
override fun onDestroy() {
val currentConf = JitsiMeet.getCurrentConference()
jitsiMeetView?.leave()
jitsiMeetView?.dispose()
// Fake emitting CONFERENCE_TERMINATED event when currentConf is not null (probably when closing the PiP screen).
if (currentConf != null) {
val broadcastEventData = JavaOnlyMap.of("url", currentConf)
BroadcastEmitter(this).sendBroadcast(BroadcastEvent.Type.CONFERENCE_TERMINATED.name, broadcastEventData)
}
JitsiMeetActivityDelegate.onHostDestroy(this)
super.onDestroy()
}
override fun onBackPressed() {
JitsiMeetActivityDelegate.onBackPressed()
}
override fun onUserLeaveHint() {
super.onUserLeaveHint()
if (packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) {
jitsiMeetView?.enterPictureInPicture()
}
} }
private fun handleLeaveConference() { private fun handleLeaveConference() {
@ -116,14 +151,16 @@ class VectorJitsiActivity : VectorBaseActivity<ActivityJitsiBinding>(), JitsiMee
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean,
newConfig: Configuration) { newConfig: Configuration) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig) super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
checkIfActivityShouldBeFinished()
Timber.w("onPictureInPictureModeChanged($isInPictureInPictureMode)") Timber.w("onPictureInPictureModeChanged($isInPictureInPictureMode)")
} }
override fun initUiAndData() { private fun checkIfActivityShouldBeFinished() {
super.initUiAndData() // OnStop is called when PiP mode is closed directly from the ui
jitsiMeetView = JitsiMeetView(this) // If stopped is called and PiP mode is not active, we should finish the activity and remove the task as Android creates a new one for PiP.
val params = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED) && !isInPictureInPictureMode) {
views.jitsiLayout.addView(jitsiMeetView, params) finishAndRemoveTask()
}
} }
private fun renderState(viewState: JitsiCallViewState) { private fun renderState(viewState: JitsiCallViewState) {
@ -167,34 +204,6 @@ class VectorJitsiActivity : VectorBaseActivity<ActivityJitsiBinding>(), JitsiMee
jitsiMeetView?.join(jitsiMeetConferenceOptions) jitsiMeetView?.join(jitsiMeetConferenceOptions)
} }
override fun onStop() {
JitsiMeetActivityDelegate.onHostPause(this)
super.onStop()
}
override fun onResume() {
JitsiMeetActivityDelegate.onHostResume(this)
super.onResume()
}
override fun onBackPressed() {
JitsiMeetActivityDelegate.onBackPressed()
super.onBackPressed()
}
override fun onDestroy() {
JitsiMeetActivityDelegate.onHostDestroy(this)
unregisterForBroadcastMessages()
super.onDestroy()
}
override fun onUserLeaveHint() {
super.onUserLeaveHint()
if (packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) {
jitsiMeetView?.enterPictureInPicture()
}
}
override fun onNewIntent(intent: Intent?) { override fun onNewIntent(intent: Intent?) {
JitsiMeetActivityDelegate.onNewIntent(intent) JitsiMeetActivityDelegate.onNewIntent(intent)
@ -217,24 +226,7 @@ class VectorJitsiActivity : VectorBaseActivity<ActivityJitsiBinding>(), JitsiMee
JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults) JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults)
} }
private fun registerForBroadcastMessages() { private fun onBroadcastEvent(event: BroadcastEvent) {
val intentFilter = IntentFilter()
for (type in BroadcastEvent.Type.values()) {
intentFilter.addAction(type.action)
}
tryOrNull("Unable to register receiver") {
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, intentFilter)
}
}
private fun unregisterForBroadcastMessages() {
tryOrNull("Unable to unregister receiver") {
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver)
}
}
private fun onBroadcastReceived(intent: Intent) {
val event = BroadcastEvent(intent)
Timber.v("Broadcast received: ${event.type}") Timber.v("Broadcast received: ${event.type}")
when (event.type) { when (event.type) {
BroadcastEvent.Type.CONFERENCE_TERMINATED -> onConferenceTerminated(event.data) BroadcastEvent.Type.CONFERENCE_TERMINATED -> onConferenceTerminated(event.data)