Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 97ad10e2 authored by Luca Zuccarini's avatar Luca Zuccarini
Browse files

Enable origin animations when launching the UMO over the Lockscreen.

Bug: 346865769
Flag: com.android.systemui.media_lockscreen_launch_animation
Test: atest LegacyActivityStarterInternalImplTest MediaControlPanelTest
Change-Id: I103f781edfd241094d58be93e0e585e6ff1545ed
parent 3d879a03
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -1098,3 +1098,13 @@ flag {
    purpose: PURPOSE_BUGFIX
  }
}

flag {
    name: "media_lockscreen_launch_animation"
    namespace : "systemui"
    description : "Enable the origin launch animation for UMO when opening on top of lockscreen."
    bug : "346865769"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}
+169 −50
Original line number Diff line number Diff line
@@ -20,8 +20,11 @@ import android.app.ActivityOptions
import android.app.PendingIntent
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.RemoteException
import android.os.UserHandle
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.View
import android.widget.FrameLayout
import android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR
@@ -29,6 +32,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.ActivityIntentHelper
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.LaunchableView
@@ -36,7 +40,6 @@ import com.android.systemui.assist.AssistManager
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
import com.android.systemui.settings.UserTracker
import com.android.systemui.shade.ShadeController
import com.android.systemui.shade.data.repository.FakeShadeRepository
@@ -51,11 +54,6 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.Optional
@@ -64,14 +62,19 @@ import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.nullable
import org.mockito.Mock
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify

@ExperimentalCoroutinesApi
@SmallTest
@@ -132,22 +135,22 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
                mainExecutor = mainExecutor,
                communalSceneInteractor = communalSceneInteractor,
            )
        whenever(userTracker.userHandle).thenReturn(UserHandle.OWNER)
        whenever(communalSceneInteractor.isIdleOnCommunal).thenReturn(MutableStateFlow(false))
        `when`(userTracker.userHandle).thenReturn(UserHandle.OWNER)
        `when`(communalSceneInteractor.isIdleOnCommunal).thenReturn(MutableStateFlow(false))
    }

    @Test
    fun startPendingIntentDismissingKeyguard_keyguardShowing_dismissWithAction() {
        val pendingIntent = mock(PendingIntent::class.java)
        whenever(pendingIntent.isActivity).thenReturn(true)
        whenever(keyguardStateController.isShowing).thenReturn(true)
        whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
        `when`(pendingIntent.isActivity).thenReturn(true)
        `when`(keyguardStateController.isShowing).thenReturn(true)
        `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)

        underTest.startPendingIntentDismissingKeyguard(intent = pendingIntent, dismissShade = true)
        mainExecutor.runAllReady()

        verify(statusBarKeyguardViewManager)
            .dismissWithAction(any(OnDismissAction::class.java), eq(null), anyBoolean(), eq(null))
            .dismissWithAction(any(), eq(null), anyBoolean(), eq(null))
    }

    @Test
@@ -160,10 +163,10 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
            }
        parent.addView(view)
        val controller = ActivityTransitionAnimator.Controller.fromView(view)
        whenever(pendingIntent.isActivity).thenReturn(true)
        whenever(keyguardStateController.isShowing).thenReturn(true)
        whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
        whenever(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt()))
        `when`(pendingIntent.isActivity).thenReturn(true)
        `when`(keyguardStateController.isShowing).thenReturn(true)
        `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
        `when`(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt()))
            .thenReturn(true)

        startPendingIntentMaybeDismissingKeyguard(
@@ -175,9 +178,9 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {

        verify(activityTransitionAnimator)
            .startPendingIntentWithAnimation(
                nullable(),
                nullable(ActivityTransitionAnimator.Controller::class.java),
                eq(true),
                nullable(),
                nullable(String::class.java),
                eq(true),
                any(),
            )
@@ -193,10 +196,10 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
            }
        parent.addView(view)
        val controller = ActivityTransitionAnimator.Controller.fromView(view)
        whenever(pendingIntent.isActivity).thenReturn(true)
        whenever(keyguardStateController.isShowing).thenReturn(true)
        whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
        whenever(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt()))
        `when`(pendingIntent.isActivity).thenReturn(true)
        `when`(keyguardStateController.isShowing).thenReturn(true)
        `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
        `when`(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt()))
            .thenReturn(false)

        // extra activity options to set on pending intent
@@ -220,12 +223,12 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
                eq(context),
                eq(0),
                eq(fillInIntent),
                nullable(),
                nullable(),
                nullable(),
                nullable(PendingIntent.OnFinished::class.java),
                nullable(Handler::class.java),
                nullable(String::class.java),
                bundleCaptor.capture()
            )
        val options = ActivityOptions.fromBundle(bundleCaptor.value)
        val options = ActivityOptions.fromBundle(bundleCaptor.firstValue)
        assertThat(options.isPendingIntentBackgroundActivityLaunchAllowedByPermission).isFalse()
        assertThat(options.splashScreenStyle).isEqualTo(SPLASH_SCREEN_STYLE_SOLID_COLOR)
    }
@@ -245,6 +248,74 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
        verify(centralSurfaces).getAnimatorControllerFromNotification(associatedView)
    }

    @EnableFlags(Flags.FLAG_MEDIA_LOCKSCREEN_LAUNCH_ANIMATION)
    @Test
    fun startPendingIntentDismissingKeyguard_transitionAnimator_animateOverOcclusion() {
        val parent = FrameLayout(context)
        val view =
            object : View(context), LaunchableView {
                override fun setShouldBlockVisibilityChanges(block: Boolean) {}
            }
        parent.addView(view)
        val controller = ActivityTransitionAnimator.Controller.fromView(view)
        val pendingIntent = mock(PendingIntent::class.java)
        `when`(pendingIntent.isActivity).thenReturn(true)
        `when`(keyguardStateController.isShowing).thenReturn(true)
        `when`(keyguardStateController.isOccluded).thenReturn(true)

        underTest.startPendingIntentDismissingKeyguard(
            intent = pendingIntent,
            dismissShade = true,
            animationController = controller,
            showOverLockscreen = true,
            skipLockscreenChecks = true
        )
        mainExecutor.runAllReady()

        verify(activityTransitionAnimator)
            .startPendingIntentWithAnimation(
                nullable(ActivityTransitionAnimator.Controller::class.java),
                eq(true),
                nullable(String::class.java),
                eq(true),
                any(),
            )
    }

    @DisableFlags(Flags.FLAG_MEDIA_LOCKSCREEN_LAUNCH_ANIMATION)
    @Test
    fun startPendingIntentDismissingKeyguard_transitionAnimator_doNotAnimateOverOcclusion() {
        val parent = FrameLayout(context)
        val view =
            object : View(context), LaunchableView {
                override fun setShouldBlockVisibilityChanges(block: Boolean) {}
            }
        parent.addView(view)
        val controller = ActivityTransitionAnimator.Controller.fromView(view)
        val pendingIntent = mock(PendingIntent::class.java)
        `when`(pendingIntent.isActivity).thenReturn(true)
        `when`(keyguardStateController.isShowing).thenReturn(true)
        `when`(keyguardStateController.isOccluded).thenReturn(true)

        underTest.startPendingIntentDismissingKeyguard(
            intent = pendingIntent,
            dismissShade = true,
            animationController = controller,
            showOverLockscreen = true,
            skipLockscreenChecks = true
        )
        mainExecutor.runAllReady()

        verify(activityTransitionAnimator)
            .startPendingIntentWithAnimation(
                nullable(ActivityTransitionAnimator.Controller::class.java),
                eq(false),
                nullable(String::class.java),
                eq(true),
                any(),
            )
    }

    @Test
    fun startActivity_noUserHandleProvided_getUserHandle() {
        val intent = mock(Intent::class.java)
@@ -254,13 +325,66 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
        verify(userTracker).userHandle
    }

    @EnableFlags(Flags.FLAG_MEDIA_LOCKSCREEN_LAUNCH_ANIMATION)
    @Test
    fun startActivity_transitionAnimator_animateOverOcclusion() {
        val intent = mock(Intent::class.java)
        val parent = FrameLayout(context)
        val view =
            object : View(context), LaunchableView {
                override fun setShouldBlockVisibilityChanges(block: Boolean) {}
            }
        parent.addView(view)
        val controller = ActivityTransitionAnimator.Controller.fromView(view)
        `when`(keyguardStateController.isShowing).thenReturn(true)
        `when`(keyguardStateController.isOccluded).thenReturn(true)

        mainExecutor.runAllReady()
        underTest.startActivity(intent, true, controller, true, null)

        verify(activityTransitionAnimator)
            .startIntentWithAnimation(
                nullable(ActivityTransitionAnimator.Controller::class.java),
                eq(true),
                nullable(String::class.java),
                eq(true),
                any(),
            )
    }

    @DisableFlags(Flags.FLAG_MEDIA_LOCKSCREEN_LAUNCH_ANIMATION)
    @Test
    fun startActivity_transitionAnimator_doNotAnimateOverOcclusion() {
        val intent = mock(Intent::class.java)
        val parent = FrameLayout(context)
        val view =
            object : View(context), LaunchableView {
                override fun setShouldBlockVisibilityChanges(block: Boolean) {}
            }
        parent.addView(view)
        val controller = ActivityTransitionAnimator.Controller.fromView(view)
        `when`(keyguardStateController.isShowing).thenReturn(true)
        `when`(keyguardStateController.isOccluded).thenReturn(true)

        mainExecutor.runAllReady()
        underTest.startActivity(intent, true, controller, true, null)

        verify(activityTransitionAnimator)
            .startIntentWithAnimation(
                nullable(ActivityTransitionAnimator.Controller::class.java),
                eq(false),
                nullable(String::class.java),
                eq(true),
                any(),
            )
    }

    @Test
    fun dismissKeyguardThenExecute_startWakeAndUnlock() {
        whenever(wakefulnessLifecycle.wakefulness)
            .thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP)
        whenever(keyguardStateController.canDismissLockScreen()).thenReturn(true)
        whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(false)
        whenever(dozeServiceHost.isPulsing).thenReturn(true)
        `when`(wakefulnessLifecycle.wakefulness).thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP)
        `when`(keyguardStateController.canDismissLockScreen()).thenReturn(true)
        `when`(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(false)
        `when`(dozeServiceHost.isPulsing).thenReturn(true)

        underTest.dismissKeyguardThenExecute({ true }, {}, false)

@@ -271,25 +395,20 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
    @Test
    fun dismissKeyguardThenExecute_keyguardIsShowing_dismissWithAction() {
        val customMessage = "Enter your pin."
        whenever(keyguardStateController.isShowing).thenReturn(true)
        `when`(keyguardStateController.isShowing).thenReturn(true)

        underTest.dismissKeyguardThenExecute({ true }, {}, false, customMessage)

        verify(statusBarKeyguardViewManager)
            .dismissWithAction(
                any(OnDismissAction::class.java),
                any(Runnable::class.java),
                eq(false),
                eq(customMessage)
            )
            .dismissWithAction(any(), any(), eq(false), eq(customMessage))
    }

    @Test
    fun dismissKeyguardThenExecute_awakeDreams() {
        val customMessage = "Enter your pin."
        var dismissActionExecuted = false
        whenever(keyguardStateController.isShowing).thenReturn(false)
        whenever(keyguardUpdateMonitor.isDreaming).thenReturn(true)
        `when`(keyguardStateController.isShowing).thenReturn(false)
        `when`(keyguardUpdateMonitor.isDreaming).thenReturn(true)

        underTest.dismissKeyguardThenExecute(
            {
@@ -308,9 +427,9 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
    @Test
    @Throws(RemoteException::class)
    fun executeRunnableDismissingKeyguard_dreaming_notShowing_awakenDreams() {
        whenever(keyguardStateController.isShowing).thenReturn(false)
        whenever(keyguardStateController.isOccluded).thenReturn(false)
        whenever(keyguardUpdateMonitor.isDreaming).thenReturn(true)
        `when`(keyguardStateController.isShowing).thenReturn(false)
        `when`(keyguardStateController.isOccluded).thenReturn(false)
        `when`(keyguardUpdateMonitor.isDreaming).thenReturn(true)

        underTest.executeRunnableDismissingKeyguard(
            runnable = {},
@@ -326,9 +445,9 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
    @Test
    @Throws(RemoteException::class)
    fun executeRunnableDismissingKeyguard_notDreaming_notShowing_doNotAwakenDreams() {
        whenever(keyguardStateController.isShowing).thenReturn(false)
        whenever(keyguardStateController.isOccluded).thenReturn(false)
        whenever(keyguardUpdateMonitor.isDreaming).thenReturn(false)
        `when`(keyguardStateController.isShowing).thenReturn(false)
        `when`(keyguardStateController.isOccluded).thenReturn(false)
        `when`(keyguardUpdateMonitor.isDreaming).thenReturn(false)

        underTest.executeRunnableDismissingKeyguard(
            runnable = {},
+18 −7
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.media.controls.ui.controller;
import static android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS;

import static com.android.settingslib.flags.Flags.legacyLeAudioSharing;
import static com.android.systemui.Flags.mediaLockscreenLaunchAnimation;
import static com.android.systemui.media.controls.shared.model.SmartspaceMediaDataKt.NUM_REQUIRED_RECOMMENDATIONS;

import android.animation.Animator;
@@ -577,6 +578,15 @@ public class MediaControlPanel {
                        && mActivityIntentHelper.wouldPendingShowOverLockscreen(clickIntent,
                        mLockscreenUserManager.getCurrentUserId());
                if (showOverLockscreen) {
                    if (mediaLockscreenLaunchAnimation()) {
                        mActivityStarter.startPendingIntentMaybeDismissingKeyguard(
                                clickIntent,
                                /* dismissShade = */ true,
                                /* intentSentUiThreadCallback = */ null,
                                buildLaunchAnimatorController(mMediaViewHolder.getPlayer()),
                                /* fillIntent = */ null,
                                /* extraOptions = */ null);
                    } else {
                        try {
                            ActivityOptions opts = ActivityOptions.makeBasic();
                            opts.setPendingIntentBackgroundActivityStartMode(
@@ -585,6 +595,7 @@ public class MediaControlPanel {
                        } catch (PendingIntent.CanceledException e) {
                            Log.e(TAG, "Pending intent for " + key + " was cancelled");
                        }
                    }
                } else {
                    mActivityStarter.postStartActivityDismissingKeyguard(clickIntent,
                            buildLaunchAnimatorController(mMediaViewHolder.getPlayer()));
+4 −2
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.view.WindowManager
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.ActivityIntentHelper
import com.android.systemui.Flags.communalHub
import com.android.systemui.Flags.mediaLockscreenLaunchAnimation
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.DelegateTransitionAnimatorController
import com.android.systemui.assist.AssistManager
@@ -635,8 +636,9 @@ constructor(
        isActivityIntent: Boolean,
        showOverLockscreen: Boolean,
    ): Boolean {
        // TODO(b/294418322): Support launch animations when occluded.
        if (keyguardStateController.isOccluded) {
        // TODO(b/294418322): always support launch animations when occluded.
        val ignoreOcclusion = showOverLockscreen && mediaLockscreenLaunchAnimation()
        if (keyguardStateController.isOccluded && !ignoreOcclusion) {
            return false
        }

+34 −1
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import android.media.MediaMetadata
import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.os.Bundle
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.DeviceFlagsValueProvider
@@ -1771,8 +1772,40 @@ public class MediaControlPanelTest : SysuiTestCase() {
        verify(logger).logSeek(anyInt(), eq(PACKAGE), eq(instanceId))
    }

    @EnableFlags(Flags.FLAG_MEDIA_LOCKSCREEN_LAUNCH_ANIMATION)
    @Test
    fun tapContentView_showOverLockscreen_openActivity() {
    fun tapContentView_showOverLockscreen_openActivity_withOriginAnimation() {
        // WHEN we are on lockscreen and this activity can show over lockscreen
        whenever(keyguardStateController.isShowing).thenReturn(true)
        whenever(activityIntentHelper.wouldPendingShowOverLockscreen(any(), any())).thenReturn(true)

        val clickIntent = mock(Intent::class.java)
        val pendingIntent = mock(PendingIntent::class.java)
        whenever(pendingIntent.intent).thenReturn(clickIntent)
        val captor = ArgumentCaptor.forClass(View.OnClickListener::class.java)
        val data = mediaData.copy(clickIntent = pendingIntent)
        player.attachPlayer(viewHolder)
        player.bindPlayer(data, KEY)
        verify(viewHolder.player).setOnClickListener(captor.capture())

        // THEN it sends the PendingIntent without dismissing keyguard first,
        // and does not use the Intent directly (see b/271845008)
        captor.value.onClick(viewHolder.player)
        verify(activityStarter)
            .startPendingIntentMaybeDismissingKeyguard(
                eq(pendingIntent),
                eq(true),
                eq(null),
                any(),
                eq(null),
                eq(null),
            )
        verify(activityStarter, never()).postStartActivityDismissingKeyguard(eq(clickIntent), any())
    }

    @DisableFlags(Flags.FLAG_MEDIA_LOCKSCREEN_LAUNCH_ANIMATION)
    @Test
    fun tapContentView_showOverLockscreen_openActivity_withoutOriginAnimation() {
        // WHEN we are on lockscreen and this activity can show over lockscreen
        whenever(keyguardStateController.isShowing).thenReturn(true)
        whenever(activityIntentHelper.wouldPendingShowOverLockscreen(any(), any())).thenReturn(true)