Element Call: remove top app bar and add it inside the webview instead (#4927)
Co-authored-by: ElementBot <android@element.io>
This commit is contained in:
parent
27a5787b13
commit
2f6ce3da33
16 changed files with 33 additions and 153 deletions
|
|
@ -7,16 +7,6 @@
|
|||
|
||||
package io.element.android.features.call.impl.pip
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
|
||||
open class PictureInPictureStateProvider : PreviewParameterProvider<PictureInPictureState> {
|
||||
override val values: Sequence<PictureInPictureState>
|
||||
get() = sequenceOf(
|
||||
aPictureInPictureState(supportPip = true),
|
||||
aPictureInPictureState(supportPip = true, isInPictureInPicture = true),
|
||||
)
|
||||
}
|
||||
|
||||
fun aPictureInPictureState(
|
||||
supportPip: Boolean = false,
|
||||
isInPictureInPicture: Boolean = false,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import android.annotation.SuppressLint
|
|||
import android.util.Log
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.ConsoleMessage
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.webkit.PermissionRequest
|
||||
import android.webkit.WebChromeClient
|
||||
import android.webkit.WebView
|
||||
|
|
@ -19,7 +20,6 @@ import androidx.compose.foundation.layout.Box
|
|||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
|
@ -32,11 +32,9 @@ import androidx.compose.ui.platform.LocalInspectionMode
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.features.call.impl.R
|
||||
import io.element.android.features.call.impl.pip.PictureInPictureEvents
|
||||
import io.element.android.features.call.impl.pip.PictureInPictureState
|
||||
import io.element.android.features.call.impl.pip.PictureInPictureStateProvider
|
||||
import io.element.android.features.call.impl.pip.aPictureInPictureState
|
||||
import io.element.android.features.call.impl.utils.InvalidAudioDeviceReason
|
||||
import io.element.android.features.call.impl.utils.WebViewAudioManager
|
||||
|
|
@ -44,13 +42,11 @@ import io.element.android.features.call.impl.utils.WebViewPipController
|
|||
import io.element.android.features.call.impl.utils.WebViewWidgetMessageInterceptor
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.designsystem.components.ProgressDialog
|
||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import timber.log.Timber
|
||||
|
||||
|
|
@ -60,7 +56,6 @@ interface CallScreenNavigator {
|
|||
fun close()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
internal fun CallScreenView(
|
||||
state: CallScreenState,
|
||||
|
|
@ -78,19 +73,6 @@ internal fun CallScreenView(
|
|||
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
topBar = {
|
||||
if (!pipState.isInPictureInPicture) {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.element_call)) },
|
||||
navigationIcon = {
|
||||
BackButton(
|
||||
imageVector = if (pipState.supportPip) CompoundIcons.ArrowLeft() else CompoundIcons.Close(),
|
||||
onClick = ::handleBack,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
) { padding ->
|
||||
BackHandler {
|
||||
handleBack()
|
||||
|
|
@ -127,9 +109,11 @@ internal fun CallScreenView(
|
|||
requestPermissions(androidPermissions.toTypedArray(), callback)
|
||||
},
|
||||
onCreateWebView = { webView ->
|
||||
webView.addBackHandler(onBackPressed = ::handleBack)
|
||||
val interceptor = WebViewWidgetMessageInterceptor(
|
||||
webView = webView,
|
||||
onUrlLoaded = { url ->
|
||||
webView.evaluateJavascript("controls.onBackButtonPressed = () => { backHandler.onBackPressed() }", null)
|
||||
if (webViewAudioManager?.isInCallMode?.get() == false) {
|
||||
Timber.d("URL $url is loaded, starting in-call audio mode")
|
||||
webViewAudioManager?.onCallStarted()
|
||||
|
|
@ -282,6 +266,17 @@ private fun WebView.setup(
|
|||
}
|
||||
}
|
||||
|
||||
private fun WebView.addBackHandler(onBackPressed: () -> Unit) {
|
||||
addJavascriptInterface(
|
||||
object {
|
||||
@Suppress("unused")
|
||||
@JavascriptInterface
|
||||
fun onBackPressed() = onBackPressed()
|
||||
},
|
||||
"backHandler"
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun CallScreenViewPreview(
|
||||
|
|
@ -294,18 +289,6 @@ internal fun CallScreenViewPreview(
|
|||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun CallScreenPipViewPreview(
|
||||
@PreviewParameter(PictureInPictureStateProvider::class) state: PictureInPictureState,
|
||||
) = ElementPreview {
|
||||
CallScreenView(
|
||||
state = aCallScreenState(),
|
||||
pipState = state,
|
||||
requestPermissions = { _, _ -> },
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun InvalidAudioDeviceDialogPreview() = ElementPreview {
|
||||
|
|
|
|||
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.call.impl.ui
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.features.call.impl.pip.PictureInPictureEvents
|
||||
import io.element.android.features.call.impl.pip.PictureInPictureState
|
||||
import io.element.android.features.call.impl.pip.aPictureInPictureState
|
||||
import io.element.android.tests.testutils.EventsRecorder
|
||||
import io.element.android.tests.testutils.pressBack
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class CallScreenViewTest {
|
||||
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
|
||||
|
||||
@Test
|
||||
fun `clicking on back when pip is not supported hangs up`() {
|
||||
val eventsRecorder = EventsRecorder<CallScreenEvents>()
|
||||
val pipEventsRecorder = EventsRecorder<PictureInPictureEvents>()
|
||||
rule.setCallScreenView(
|
||||
aCallScreenState(
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
aPictureInPictureState(
|
||||
supportPip = false,
|
||||
eventSink = pipEventsRecorder,
|
||||
),
|
||||
)
|
||||
rule.pressBack()
|
||||
eventsRecorder.assertSize(2)
|
||||
eventsRecorder.assertTrue(0) { it is CallScreenEvents.SetupMessageChannels }
|
||||
eventsRecorder.assertTrue(1) { it == CallScreenEvents.Hangup }
|
||||
pipEventsRecorder.assertSize(1)
|
||||
pipEventsRecorder.assertTrue(0) { it is PictureInPictureEvents.SetPipController }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on back when pip is supported enables PiP`() {
|
||||
val eventsRecorder = EventsRecorder<CallScreenEvents>()
|
||||
val pipEventsRecorder = EventsRecorder<PictureInPictureEvents>()
|
||||
rule.setCallScreenView(
|
||||
aCallScreenState(
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
aPictureInPictureState(
|
||||
supportPip = true,
|
||||
eventSink = pipEventsRecorder,
|
||||
),
|
||||
)
|
||||
rule.pressBack()
|
||||
eventsRecorder.assertSize(1)
|
||||
eventsRecorder.assertTrue(0) { it is CallScreenEvents.SetupMessageChannels }
|
||||
pipEventsRecorder.assertSize(2)
|
||||
pipEventsRecorder.assertTrue(0) { it is PictureInPictureEvents.SetPipController }
|
||||
pipEventsRecorder.assertTrue(1) { it == PictureInPictureEvents.EnterPictureInPicture }
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setCallScreenView(
|
||||
state: CallScreenState,
|
||||
pipState: PictureInPictureState,
|
||||
requestPermissions: (Array<String>, RequestPermissionCallback) -> Unit = { _, _ -> },
|
||||
) {
|
||||
setContent {
|
||||
CallScreenView(
|
||||
state = state,
|
||||
pipState = pipState,
|
||||
requestPermissions = requestPermissions,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ import io.element.android.services.analytics.api.AnalyticsService
|
|||
import kotlinx.coroutines.flow.first
|
||||
import org.matrix.rustcomponents.sdk.newVirtualElementCallWidget
|
||||
import uniffi.matrix_sdk.EncryptionSystem
|
||||
import uniffi.matrix_sdk.HeaderStyle
|
||||
import uniffi.matrix_sdk.VirtualElementCallWidgetOptions
|
||||
import javax.inject.Inject
|
||||
import uniffi.matrix_sdk.Intent as CallIntent
|
||||
|
|
@ -48,9 +49,10 @@ class DefaultCallWidgetSettingsProvider @Inject constructor(
|
|||
sentryDsn = callAnalyticsCredentialsProvider.sentryDsn.takeIf { isAnalyticsEnabled },
|
||||
sentryEnvironment = if (buildMeta.buildType == BuildType.RELEASE) "RELEASE" else "DEBUG",
|
||||
parentUrl = null,
|
||||
// For backwards compatibility, it'll be ignored in recent versions of Element Call
|
||||
hideHeader = true,
|
||||
controlledMediaDevices = true,
|
||||
header = null,
|
||||
header = HeaderStyle.APP_BAR,
|
||||
)
|
||||
val rustWidgetSettings = newVirtualElementCallWidget(options)
|
||||
return MatrixWidgetSettings.fromRustWidgetSettings(rustWidgetSettings)
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b61b391f6380189603939badc2ae5912f5cb072200a001ff946af2d52e81b95a
|
||||
size 12734
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7696eb0f5bf8698d001ea44be6eb2005f414057f9a65703e6d987e8eb75f7f94
|
||||
size 9441
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c3d14805d85b33ce1f0fdd26eec433db1973924542cd85a3bb0e4514cdcd857d
|
||||
size 12392
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:146fc2ce4d344c0ea947fe0370e6b8d94c2e724c69c01c2cc3476a756e1f09e4
|
||||
size 9315
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8cc555b1614f3d9e5181769ec203da84f0fb01bbec9b7edf1ad53944158aea7e
|
||||
size 12824
|
||||
oid sha256:7696eb0f5bf8698d001ea44be6eb2005f414057f9a65703e6d987e8eb75f7f94
|
||||
size 9441
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5480b1c7a3244e0c702f6623f736bfc76560843a8eba8c118a1560954414b4f4
|
||||
size 14560
|
||||
oid sha256:5d22e3789c7515c5fcb0ee0c72a7c238947f526b6959e1590a29008c8cfa9919
|
||||
size 11737
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8b42b1ad21ab76ffb7b555809e5d5d4495a6b0faceba181735369e0635ed0a56
|
||||
size 13847
|
||||
oid sha256:b4d802d233fdb12dbbb8136d8b127b10898ba7c5963ebbe2f8ce4d015016b984
|
||||
size 10892
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:73bbf4bfe808722a4d2444ab78bd1f6ab6978d0e427dfaed0d0b957d153a00e5
|
||||
size 19294
|
||||
oid sha256:6d885da406fe9866a5390a65bb6f8dda3e2242bdec4ac59e0ec654a16838bbf6
|
||||
size 16309
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:037a5e384eef63ade207a6a938409897f11efc83f1cbb1d382474bf21dbb54c2
|
||||
size 12524
|
||||
oid sha256:146fc2ce4d344c0ea947fe0370e6b8d94c2e724c69c01c2cc3476a756e1f09e4
|
||||
size 9315
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:548c1d91bfee3a9fe22e881d61c469369885f51f80e267ce9e16c6f4501948fe
|
||||
size 13372
|
||||
oid sha256:8f2d4611b2fc7156c855b13d8b390bc9be80b877407f521a8d196434e97650fe
|
||||
size 10519
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5cd1c4e6317f6372c3c341f7e681d82533038a15c2287f584603330b3cf62a40
|
||||
size 12316
|
||||
oid sha256:b58875c87cfdf54fcfd6990cc4c7637d74d85faed1c69bc4e5c671afb827ea4d
|
||||
size 9483
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6de376c3fa6098ba16522181a7b7835660c57c77bfd2c7d8ca75d372c4144676
|
||||
size 17437
|
||||
oid sha256:928fecaa9000e8c0309d4bbbd7742921ec83497cb65868ffda1a2faf0cba9b57
|
||||
size 14599
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue