mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-02 12:07:28 +00:00
Merge develop into the PreviewParam rename
The 174 commits develop gained since this branch was cut touch preview data in three ways, and each is resolved so the flipped konsist rule still holds across the merged tree. Two screens the branch had renamed were deleted upstream by "Remove the account provider search screen and homeserver resolver": the changeaccountprovider and searchaccountprovider packages. Their deletion wins, so ChangeAccountProviderStatePreviewParam, SearchAccountProviderStatePreviewParam and the two views that used them go with them rather than coming back under the new name. Two preview classes develop added still carried the old suffix and are renamed here, along with the `@PreviewParameter` reference in each view: GlobalSearchStateProvider -> GlobalSearchStatePreviewParam and SendFailureDialogStateProvider -> SendFailureDialogStatePreviewParam. Everything else merged textually. No production class extending PreviewParameterProvider is left with a `Provider` name, and no reference to any of the 194 renamed names survives outside the CHANGES.md entry that records a past pull request title.
This commit is contained in:
+2
@@ -11,7 +11,9 @@ package io.element.android.features.call.impl.pip
|
||||
import io.element.android.features.call.impl.utils.PipController
|
||||
|
||||
sealed interface PictureInPictureEvent {
|
||||
data object OnCallStarted : PictureInPictureEvent
|
||||
data class SetPipController(val pipController: PipController) : PictureInPictureEvent
|
||||
data object EnterPictureInPicture : PictureInPictureEvent
|
||||
data class OnPictureInPictureModeChanged(val isInPip: Boolean) : PictureInPictureEvent
|
||||
data class SetPipOrientation(val orientation: Int?) : PictureInPictureEvent
|
||||
}
|
||||
|
||||
+7
@@ -38,6 +38,9 @@ class PictureInPicturePresenter(
|
||||
|
||||
fun handleEvent(event: PictureInPictureEvent) {
|
||||
when (event) {
|
||||
is PictureInPictureEvent.OnCallStarted -> {
|
||||
pipController?.setupPipCallbacks()
|
||||
}
|
||||
is PictureInPictureEvent.SetPipController -> {
|
||||
pipController = event.pipController
|
||||
}
|
||||
@@ -55,6 +58,10 @@ class PictureInPicturePresenter(
|
||||
pipController?.exitPip()
|
||||
}
|
||||
}
|
||||
is PictureInPictureEvent.SetPipOrientation -> {
|
||||
Timber.tag(loggerTag.value).d("onOrientationChange: ${event.orientation}")
|
||||
pipView?.setPipOrientation(event.orientation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -9,6 +9,7 @@
|
||||
package io.element.android.features.call.impl.pip
|
||||
|
||||
interface PipView {
|
||||
fun setPipOrientation(orientation: Int?)
|
||||
fun setPipParams()
|
||||
fun enterPipMode(): Boolean
|
||||
fun hangUp()
|
||||
|
||||
+7
-1
@@ -118,6 +118,7 @@ internal fun CallScreenView(
|
||||
if (webViewAudioManager?.isInCallMode?.get() == false) {
|
||||
Timber.d("URL $url is loaded, starting in-call audio mode")
|
||||
webViewAudioManager?.onCallStarted()
|
||||
pipState.eventSink(PictureInPictureEvent.OnCallStarted)
|
||||
} else {
|
||||
Timber.d("Can't start in-call audio mode since the app is already in it.")
|
||||
}
|
||||
@@ -130,7 +131,12 @@ internal fun CallScreenView(
|
||||
onInvalidAudioDeviceAdded = { invalidAudioDeviceReason = it },
|
||||
)
|
||||
state.eventSink(CallScreenEvent.SetupMessageChannels(interceptor))
|
||||
val pipController = WebViewPipController(webView)
|
||||
val pipController = WebViewPipController(
|
||||
webView = webView,
|
||||
updatePipOrientation = { orientation ->
|
||||
pipState.eventSink(PictureInPictureEvent.SetPipOrientation(orientation))
|
||||
}
|
||||
)
|
||||
pipState.eventSink(PictureInPictureEvent.SetPipController(pipController))
|
||||
},
|
||||
onDestroyWebView = {
|
||||
|
||||
+13
-3
@@ -11,9 +11,9 @@ package io.element.android.features.call.impl.ui
|
||||
import android.Manifest
|
||||
import android.app.PictureInPictureParams
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Rational
|
||||
import android.view.WindowManager
|
||||
import android.webkit.PermissionRequest
|
||||
import androidx.activity.compose.setContent
|
||||
@@ -49,6 +49,7 @@ import io.element.android.features.call.impl.pip.PipView
|
||||
import io.element.android.features.call.impl.services.CallForegroundService
|
||||
import io.element.android.features.enterprise.api.EnterpriseService
|
||||
import io.element.android.libraries.androidutils.browser.ConsoleMessageLogger
|
||||
import io.element.android.libraries.androidutils.media.setAspectRatioFromOrientation
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.architecture.bindings
|
||||
import io.element.android.libraries.audio.api.AudioFocus
|
||||
@@ -86,6 +87,8 @@ class ElementCallActivity :
|
||||
|
||||
private var eventSink: ((CallScreenEvent) -> Unit)? = null
|
||||
|
||||
private var currentPipOrientation: Int? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@@ -199,6 +202,7 @@ class ElementCallActivity :
|
||||
}
|
||||
DisposableEffect(Unit) {
|
||||
val onPictureInPictureModeChangedListener = Consumer { _: PictureInPictureModeChangedInfo ->
|
||||
setPipParams()
|
||||
pipEventSink(PictureInPictureEvent.OnPictureInPictureModeChanged(isInPictureInPictureMode))
|
||||
if (!isInPictureInPictureMode && !lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
|
||||
Timber.tag(loggerTag.value).d("Exiting PiP mode: Hangup the call")
|
||||
@@ -296,11 +300,17 @@ class ElementCallActivity :
|
||||
}
|
||||
}
|
||||
|
||||
override fun setPipOrientation(orientation: Int?) {
|
||||
currentPipOrientation = orientation
|
||||
setPictureInPictureParams(getPictureInPictureParams())
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
private fun getPictureInPictureParams(): PictureInPictureParams {
|
||||
// Portrait for calls seems more appropriate as a fallback value
|
||||
val orientation = currentPipOrientation ?: Configuration.ORIENTATION_PORTRAIT
|
||||
return PictureInPictureParams.Builder()
|
||||
// Portrait for calls seems more appropriate
|
||||
.setAspectRatio(Rational(3, 5))
|
||||
.setAspectRatioFromOrientation(orientation)
|
||||
.apply {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
setAutoEnterEnabled(true)
|
||||
|
||||
+1
@@ -9,6 +9,7 @@
|
||||
package io.element.android.features.call.impl.utils
|
||||
|
||||
interface PipController {
|
||||
fun setupPipCallbacks()
|
||||
suspend fun canEnterPip(): Boolean
|
||||
fun enterPip()
|
||||
fun exitPip()
|
||||
|
||||
+1
-1
@@ -250,7 +250,7 @@ class WebViewAudioManager(
|
||||
|
||||
hasRegisteredCallbacks = true
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
Timber.d("Setting androidNativeBridge javascript interface in webview")
|
||||
webView.addJavascriptInterface(webViewAudioDeviceSelectedCallback, "androidNativeBridge")
|
||||
|
||||
+30
@@ -8,7 +8,10 @@
|
||||
|
||||
package io.element.android.features.call.impl.utils
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.webkit.WebView
|
||||
import timber.log.Timber
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
@@ -18,7 +21,18 @@ import kotlin.coroutines.suspendCoroutine
|
||||
*/
|
||||
class WebViewPipController(
|
||||
private val webView: WebView,
|
||||
updatePipOrientation: (orientation: Int?) -> Unit,
|
||||
) : PipController {
|
||||
init {
|
||||
// Register the JavaScript interface ahead of time to ensure it's available when the WebView content is loaded and tries to call it
|
||||
webView.addJavascriptInterface(PipBridge(updatePipOrientation), "androidPipBridge")
|
||||
}
|
||||
|
||||
override fun setupPipCallbacks() {
|
||||
Timber.d("Adding callback in controls.onPipMediaOrientationUpdate")
|
||||
webView.evaluateJavascript("controls.onPipMediaOrientationUpdate = (orientation) => { androidPipBridge.setPipOrientation(orientation); };", null)
|
||||
}
|
||||
|
||||
override suspend fun canEnterPip(): Boolean {
|
||||
return suspendCoroutine { continuation ->
|
||||
webView.evaluateJavascript("controls.canEnterPip()") { result ->
|
||||
@@ -36,3 +50,19 @@ class WebViewPipController(
|
||||
webView.evaluateJavascript("controls.disablePip()", null)
|
||||
}
|
||||
}
|
||||
|
||||
private class PipBridge(
|
||||
private val onOrientationChange: (orientation: Int?) -> Unit,
|
||||
) {
|
||||
@JavascriptInterface
|
||||
fun setPipOrientation(orientation: String?) {
|
||||
Timber.d("Picture-in-picture orientation changed in webview, orientation: $orientation")
|
||||
// This callback is used to update the picture-in-picture orientation in the WebView when it changes
|
||||
val orientation = when (orientation) {
|
||||
"portrait" -> Configuration.ORIENTATION_PORTRAIT
|
||||
"landscape" -> Configuration.ORIENTATION_LANDSCAPE
|
||||
else -> return
|
||||
}
|
||||
onOrientationChange(orientation)
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -12,10 +12,13 @@ import io.element.android.features.call.impl.utils.PipController
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
class FakePipController(
|
||||
private val setupPipCallbacksResult: () -> Unit = { lambdaError() },
|
||||
private val canEnterPipResult: () -> Boolean = { lambdaError() },
|
||||
private val enterPipResult: () -> Unit = { lambdaError() },
|
||||
private val exitPipResult: () -> Unit = { lambdaError() },
|
||||
) : PipController {
|
||||
override fun setupPipCallbacks() = setupPipCallbacksResult()
|
||||
|
||||
override suspend fun canEnterPip(): Boolean = canEnterPipResult()
|
||||
|
||||
override fun enterPip() = enterPipResult()
|
||||
|
||||
+3
-1
@@ -13,9 +13,11 @@ import io.element.android.tests.testutils.lambda.lambdaError
|
||||
class FakePipView(
|
||||
private val setPipParamsResult: () -> Unit = { lambdaError() },
|
||||
private val enterPipModeResult: () -> Boolean = { lambdaError() },
|
||||
private val handUpResult: () -> Unit = { lambdaError() }
|
||||
private val handUpResult: () -> Unit = { lambdaError() },
|
||||
private val onOrientationChangeResult: (orientation: Int?) -> Unit = { lambdaError() },
|
||||
) : PipView {
|
||||
override fun setPipParams() = setPipParamsResult()
|
||||
override fun enterPipMode(): Boolean = enterPipModeResult()
|
||||
override fun hangUp() = handUpResult()
|
||||
override fun setPipOrientation(orientation: Int?) = onOrientationChangeResult(orientation)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user