mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-03 16:10:00 +00:00
Follow a native call into picture-in-picture from the app
supportsPictureInPicture and smallestScreenSize have to be declared here, because a library cannot change a host Activity's manifest entry. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -53,9 +53,10 @@
|
|||||||
-->
|
-->
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|uiMode"
|
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboardHidden|uiMode"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
|
android:supportsPictureInPicture="true"
|
||||||
android:theme="@style/Theme.ElementX.Splash"
|
android:theme="@style/Theme.ElementX.Splash"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
|||||||
@@ -70,6 +70,19 @@ class MainActivity : NodeActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pre-Android 12 way into picture-in-picture for a native call.
|
||||||
|
*
|
||||||
|
* Everything else about it is installed where the call is drawn, which knows whose call it is.
|
||||||
|
* This cannot be: an Activity override has no session in scope. From Android 12 the system is told
|
||||||
|
* up front that this Activity would like to shrink, and the call handles it without ever coming
|
||||||
|
* through here.
|
||||||
|
*/
|
||||||
|
override fun onUserLeaveHint() {
|
||||||
|
super.onUserLeaveHint()
|
||||||
|
appBindings.nativeCallPip().onUserLeaveHint(this)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun MainContent(appBindings: AppBindings) {
|
private fun MainContent(appBindings: AppBindings) {
|
||||||
val migrationState = appBindings.migrationEntryPoint().present()
|
val migrationState = appBindings.migrationEntryPoint().present()
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import dev.zacsweers.metro.AppScope
|
|||||||
import dev.zacsweers.metro.ContributesTo
|
import dev.zacsweers.metro.ContributesTo
|
||||||
import dev.zacsweers.metro.Multibinds
|
import dev.zacsweers.metro.Multibinds
|
||||||
import io.element.android.features.api.MigrationEntryPoint
|
import io.element.android.features.api.MigrationEntryPoint
|
||||||
|
import io.element.android.features.callnative.api.NativeCallPip
|
||||||
import io.element.android.features.enterprise.api.AppStartupHook
|
import io.element.android.features.enterprise.api.AppStartupHook
|
||||||
import io.element.android.features.enterprise.api.EnterpriseService
|
import io.element.android.features.enterprise.api.EnterpriseService
|
||||||
import io.element.android.features.lockscreen.api.LockScreenEntryPoint
|
import io.element.android.features.lockscreen.api.LockScreenEntryPoint
|
||||||
@@ -52,6 +53,13 @@ interface AppBindings {
|
|||||||
|
|
||||||
fun buildMeta(): BuildMeta
|
fun buildMeta(): BuildMeta
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For picture-in-picture. Only an Activity is told it is being left, and only the call knows
|
||||||
|
* whether that is worth shrinking for, so `MainActivity` needs this much of the native call and
|
||||||
|
* no more.
|
||||||
|
*/
|
||||||
|
fun nativeCallPip(): NativeCallPip
|
||||||
|
|
||||||
fun sentrySdkDsn(): SentrySdkDsn?
|
fun sentrySdkDsn(): SentrySdkDsn?
|
||||||
|
|
||||||
@Multibinds(allowEmpty = true)
|
@Multibinds(allowEmpty = true)
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ dependencies {
|
|||||||
implementation(libs.coil)
|
implementation(libs.coil)
|
||||||
|
|
||||||
implementation(projects.features.announcement.api)
|
implementation(projects.features.announcement.api)
|
||||||
|
implementation(projects.features.callnative.api)
|
||||||
implementation(projects.features.ftue.api)
|
implementation(projects.features.ftue.api)
|
||||||
implementation(projects.features.linknewdevice.api)
|
implementation(projects.features.linknewdevice.api)
|
||||||
implementation(projects.features.share.api)
|
implementation(projects.features.share.api)
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import io.element.android.appnav.room.RoomFlowNode
|
|||||||
import io.element.android.appnav.room.RoomNavigationTarget
|
import io.element.android.appnav.room.RoomNavigationTarget
|
||||||
import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode
|
import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode
|
||||||
import io.element.android.compound.colors.SemanticColorsLightDark
|
import io.element.android.compound.colors.SemanticColorsLightDark
|
||||||
|
import io.element.android.features.callnative.api.NativeCallHost
|
||||||
import io.element.android.features.createroom.api.CreateRoomEntryPoint
|
import io.element.android.features.createroom.api.CreateRoomEntryPoint
|
||||||
import io.element.android.features.enterprise.api.EnterpriseService
|
import io.element.android.features.enterprise.api.EnterpriseService
|
||||||
import io.element.android.features.enterprise.api.SessionEnterpriseService
|
import io.element.android.features.enterprise.api.SessionEnterpriseService
|
||||||
@@ -144,6 +145,7 @@ class LoggedInFlowNode(
|
|||||||
private val shareEntryPoint: ShareEntryPoint,
|
private val shareEntryPoint: ShareEntryPoint,
|
||||||
private val matrixClient: MatrixClient,
|
private val matrixClient: MatrixClient,
|
||||||
private val sendingQueue: SendQueues,
|
private val sendingQueue: SendQueues,
|
||||||
|
private val nativeCallHost: NativeCallHost,
|
||||||
private val incomingVerificationEntryPoint: IncomingVerificationEntryPoint,
|
private val incomingVerificationEntryPoint: IncomingVerificationEntryPoint,
|
||||||
private val mediaPreviewConfigMigration: MediaPreviewConfigMigration,
|
private val mediaPreviewConfigMigration: MediaPreviewConfigMigration,
|
||||||
private val sessionEnterpriseService: SessionEnterpriseService,
|
private val sessionEnterpriseService: SessionEnterpriseService,
|
||||||
@@ -701,15 +703,19 @@ class LoggedInFlowNode(
|
|||||||
) {
|
) {
|
||||||
CompositionLocalProvider(LocalMapTilerConfig provides updatedMapTilerConfig) {
|
CompositionLocalProvider(LocalMapTilerConfig provides updatedMapTilerConfig) {
|
||||||
val isOnline by syncService.isOnline.collectAsState()
|
val isOnline by syncService.isOnline.collectAsState()
|
||||||
ConnectivityIndicatorContainer(
|
// Outside the connectivity indicator so the two strips stack rather than fight: an
|
||||||
isOnline = isOnline,
|
// offline banner during a call belongs under the call bar, not over it.
|
||||||
modifier = modifier,
|
nativeCallHost.Render(modifier = modifier) { callContentModifier ->
|
||||||
) { contentModifier ->
|
ConnectivityIndicatorContainer(
|
||||||
Box(modifier = contentModifier) {
|
isOnline = isOnline,
|
||||||
val ftueState by ftueService.state.collectAsState()
|
modifier = callContentModifier,
|
||||||
BackstackView(transitionHandler = rememberLoggedInFlowTransitionHandler(backstack))
|
) { contentModifier ->
|
||||||
if (ftueState is FtueState.Complete) {
|
Box(modifier = contentModifier) {
|
||||||
PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent)
|
val ftueState by ftueService.state.collectAsState()
|
||||||
|
BackstackView(transitionHandler = rememberLoggedInFlowTransitionHandler(backstack))
|
||||||
|
if (ftueState is FtueState.Complete) {
|
||||||
|
PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user