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

Commit 65cb6056 authored by Luca Zuccarini's avatar Luca Zuccarini Committed by Android (Google) Code Review
Browse files

Merge "Enable origin animations when launching the UMO over the Lockscreen." into main

parents f8554bbe 97ad10e2
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -1105,3 +1105,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)