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

Commit 5c3f6174 authored by Luca Zuccarini's avatar Luca Zuccarini
Browse files

3.5 Update StatusBarNotificationActivityStarter.

It now branches to use the new ActivityTransitionAnimator APIs when
the flag is enabled.

Trying to make each of the CLs as small as possible to keep them
digestible and low risk. For the refactor plan see
go/animlib-shell-refactor-plan.

Bug: 397180418
Flag: com.android.systemui.animation_library_shell_migration
Test: atest StatusBarNotificationActivityStarterTest + manual
Change-Id: Icaabbd59334e181a4a92502e8ab2ea42efdfea30
parent f64bb7c1
Loading
Loading
Loading
Loading
+37 −0
Original line number Diff line number Diff line
@@ -45,6 +45,43 @@ fun createActivityOptions(displayId: Int, transition: RemoteTransition?, cookie:
        .toBundle()
}

/**
 * Returns an [ActivityOptions] bundle created using the given parameters.
 *
 * @param displayId The ID of the display to launch the activity in. Typically this would be the
 *   display the status bar is on.
 * @param transition The animation driver used to start this activity, or null for the default
 *   animation.
 * @param cookie The launch cookie associated with this activity, or null. Only used if [transition]
 *   is also not null.
 * @param isKeyguardShowing Whether keyguard is currently showing.
 * @param eventTime The event time in milliseconds since boot, not including sleep. See
 *   [ActivityOptions.setSourceInfo].
 */
fun createActivityOptions(
    displayId: Int,
    transition: RemoteTransition?,
    cookie: IBinder?,
    isKeyguardShowing: Boolean,
    eventTime: Long,
): Bundle {
    return createDefaultActivityOptions(transition, cookie)
        .apply {
            setSourceInfo(
                if (isKeyguardShowing) {
                    ActivityOptions.SourceInfo.TYPE_LOCKSCREEN
                } else {
                    ActivityOptions.SourceInfo.TYPE_NOTIFICATION
                },
                eventTime,
            )
            launchDisplayId = displayId
            callerDisplayId = displayId
            isPendingIntentBackgroundActivityLaunchAllowed = true
        }
        .toBundle()
}

@SuppressLint("MissingPermission")
private fun createDefaultActivityOptions(
    transition: RemoteTransition?,
+85 −27
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package com.android.systemui.statusbar.phone;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
import static android.service.notification.NotificationListenerService.REASON_CLICK;

import static com.android.systemui.statusbar.phone.ActivityStarterUtilsKt.addCookieIfNeeded;
import static com.android.systemui.statusbar.phone.ActivityStarterUtilsKt.createActivityOptions;
import static com.android.systemui.statusbar.phone.CentralSurfaces.getActivityOptions;

import android.app.ActivityManager;
@@ -54,6 +56,7 @@ import com.android.systemui.EventLogTags;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -85,6 +88,8 @@ import com.android.systemui.wmshell.BubblesManager;

import dagger.Lazy;

import kotlinx.coroutines.CoroutineScope;

import java.util.List;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -118,6 +123,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit

    private final Handler mMainThreadHandler;
    private final Executor mUiBgExecutor;
    private final CoroutineScope mApplicationScope;

    private final NotificationVisibilityProvider mVisibilityProvider;
    private final HeadsUpManager mHeadsUpManager;
@@ -158,6 +164,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
            ShadeDialogContextInteractor contextInteractor,
            @Main Handler mainThreadHandler,
            @Background Executor uiBgExecutor,
            @Application CoroutineScope applicationScope,
            NotificationVisibilityProvider visibilityProvider,
            HeadsUpManager headsUpManager,
            ActivityStarter activityStarter,
@@ -191,6 +198,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
        mContextInteractor = contextInteractor;
        mMainThreadHandler = mainThreadHandler;
        mUiBgExecutor = uiBgExecutor;
        mApplicationScope = applicationScope;
        mVisibilityProvider = visibilityProvider;
        mHeadsUpManager = headsUpManager;
        mActivityStarter = activityStarter;
@@ -503,6 +511,34 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
                            mCommandQueue,
                            displayId,
                            isActivityIntent);

            if (ActivityTransitionAnimator.Companion.shellMigrationEnabled()) {
                ActivityTransitionAnimator.Controller controllerWithCookie =
                        addCookieIfNeeded(animationController);
                ActivityTransitionAnimator.TransitionCookie cookie =
                        controllerWithCookie != null
                                ? controllerWithCookie.getTransitionCookie() : null;

                mActivityTransitionAnimator.startPendingIntentWithAnimation(
                        controllerWithCookie,
                        mApplicationScope,
                        animate,
                        (transition) -> {
                            long eventTime = row.getAndResetLastActionUpTime();
                            Bundle options = eventTime > 0
                                    ? createActivityOptions(
                                    displayId,
                                    transition,
                                    cookie,
                                    mKeyguardStateController.isShowing(),
                                    eventTime)
                                    : createActivityOptions(displayId, transition, cookie);
                            int result = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
                                    null, null, options);
                            mLogger.logSendPendingIntent(entry, intent, result);
                            return result;
                        });
            } else {
                mActivityTransitionAnimator.startPendingIntentWithAnimation(
                        animationController,
                        animate,
@@ -521,6 +557,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
                            mLogger.logSendPendingIntent(entry, intent, result);
                            return result;
                        });
            }
        } catch (PendingIntent.CanceledException e) {
            // the stack trace isn't very helpful here.
            // Just log the exception message.
@@ -548,14 +585,36 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
                                    displayId,
                                    true /* isActivityIntent */);

                    if (ActivityTransitionAnimator.Companion.shellMigrationEnabled()) {
                        ActivityTransitionAnimator.Controller controllerWithCookie =
                                addCookieIfNeeded(animationController);
                        ActivityTransitionAnimator.TransitionCookie cookie =
                                controllerWithCookie != null
                                        ? controllerWithCookie.getTransitionCookie() : null;

                        mActivityTransitionAnimator.startIntentWithAnimation(
                            animationController, animate, intent.getPackage(),
                            (adapter) -> TaskStackBuilder.create(mContext)
                                controllerWithCookie,
                                mApplicationScope,
                                animate,
                                (transition) -> TaskStackBuilder.create(mContext)
                                        .addNextIntentWithParentStack(intent)
                                    .startActivities(getActivityOptions(
                                        .startActivities(
                                                createActivityOptions(
                                                        displayId,
                                                    adapter),
                                                        transition,
                                                        cookie),
                                                new UserHandle(UserHandle.getUserId(appUid))));
                    } else {
                        mActivityTransitionAnimator.startIntentWithAnimation(
                                animationController,
                                animate,
                                intent.getPackage(),
                                (adapter) -> TaskStackBuilder.create(mContext)
                                        .addNextIntentWithParentStack(intent)
                                        .startActivities(
                                                getActivityOptions(displayId, adapter),
                                                new UserHandle(UserHandle.getUserId(appUid))));
                    }
                });
                return true;
            }
@@ -694,5 +753,4 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
        }
        return true;
    }

}
+58 −13
Original line number Diff line number Diff line
@@ -48,6 +48,8 @@ import android.content.pm.ResolveInfo;
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.service.dreams.IDreamManager;
import android.service.notification.StatusBarNotification;
import android.testing.TestableLooper;
@@ -61,11 +63,11 @@ import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.LockPatternUtils;
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.assist.AssistManager;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
@@ -158,8 +160,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
    @Mock
    private Runnable mFutureDismissalRunnable;
    @Mock
    private StatusBarNotificationActivityStarter mNotificationActivityStarter;
    @Mock
    private ActivityTransitionAnimator mActivityTransitionAnimator;
    @Mock
    private InteractionJankMonitor mJankMonitor;
@@ -174,6 +174,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
    private NotificationEntry mBubbleNotificationEntry;
    private ExpandableNotificationRow mBubbleNotificationRow;
    private FakeShadeDialogContextInteractor mContextInteractor;
    private StatusBarNotificationActivityStarter mUnderTest;


    private final Answer<Void> mCallOnDismiss = answerVoid(
            (OnDismissAction dismissAction, Runnable cancel,
@@ -227,12 +229,13 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
                        mock(NotificationListContainer.class),
                        mHeadsUpManager,
                        mJankMonitor);
        mNotificationActivityStarter =
        mUnderTest =
                new StatusBarNotificationActivityStarter(
                        getContext(),
                        mContextInteractor,
                        mHandler,
                        mUiBgExecutor,
                        mKosmos.getTestScope(),
                        mVisibilityProvider,
                        mHeadsUpManager,
                        mActivityStarter,
@@ -285,6 +288,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
                .when(mHandler).post(any(Runnable.class));
    }

    @EnableFlags(Flags.FLAG_ANIMATION_LIBRARY_SHELL_MIGRATION)
    @Test
    public void testOnNotificationClicked_keyGuardShowing()
            throws PendingIntent.CanceledException, RemoteException {
@@ -301,7 +305,47 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
        when(mKeyguardStateController.isOccluded()).thenReturn(true);

        // When
        mNotificationActivityStarter.onNotificationClicked(mNotificationEntry, mNotificationRow);
        mUnderTest.onNotificationClicked(mNotificationEntry, mNotificationRow);
        // Run the collected runnables in fifo order, the way post() really does.
        while (!runnables.isEmpty()) runnables.remove(0).run();

        // Then
        verify(mShadeController, atLeastOnce()).collapseShade();

        verify(mActivityTransitionAnimator).startPendingIntentWithAnimation(any(), any(),
                eq(false) /* animate */,
                any(ActivityTransitionAnimator.PendingIntentStarter.class));

        verify(mAssistManager).hideAssist();

        InOrder orderVerifier = Mockito.inOrder(mClickNotifier, mOnUserInteractionCallback,
                mFutureDismissalRunnable);
        // Notification calls dismiss callback to remove notification due to FLAG_AUTO_CANCEL
        orderVerifier.verify(mOnUserInteractionCallback)
                .registerFutureDismissal(eq(mNotificationEntry), eq(REASON_CLICK));
        orderVerifier.verify(mClickNotifier).onNotificationClick(
                eq(mNotificationEntry.getKey()), any(NotificationVisibility.class));
        orderVerifier.verify(mFutureDismissalRunnable).run();
    }

    @DisableFlags(Flags.FLAG_ANIMATION_LIBRARY_SHELL_MIGRATION)
    @Test
    public void testOnNotificationClicked_keyGuardShowingLegacy()
            throws PendingIntent.CanceledException, RemoteException {
        // To get the order right, collect posted runnables and run them later
        List<Runnable> runnables = new ArrayList<>();
        doAnswer(answerVoid(r -> runnables.add((Runnable) r)))
                .when(mHandler).post(any(Runnable.class));
        // Given
        Notification notification = mNotificationEntry.getSbn().getNotification();
        notification.contentIntent = mContentIntent;
        notification.flags |= Notification.FLAG_AUTO_CANCEL;

        when(mKeyguardStateController.isShowing()).thenReturn(true);
        when(mKeyguardStateController.isOccluded()).thenReturn(true);

        // When
        mUnderTest.onNotificationClicked(mNotificationEntry, mNotificationRow);
        // Run the collected runnables in fifo order, the way post() really does.
        while (!runnables.isEmpty()) runnables.remove(0).run();

@@ -309,7 +353,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
        verify(mShadeController, atLeastOnce()).collapseShade();

        verify(mActivityTransitionAnimator).startPendingIntentWithAnimation(any(),
                eq(false) /* animate */, any(), any());
                eq(false) /* animate */, any(),
                any(ActivityTransitionAnimator.LegacyPendingIntentStarter.class));

        verify(mAssistManager).hideAssist();

@@ -332,7 +377,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
        sbn.getNotification().contentIntent = null;

        // When
        mNotificationActivityStarter.onNotificationClicked(
        mUnderTest.onNotificationClicked(
                mBubbleNotificationEntry, mBubbleNotificationRow);

        // Then
@@ -366,7 +411,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
        when(mKeyguardStateController.isOccluded()).thenReturn(true);

        // When
        mNotificationActivityStarter.onNotificationClicked(
        mUnderTest.onNotificationClicked(
                mBubbleNotificationEntry, mBubbleNotificationRow);

        // Then
@@ -394,7 +439,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
        when(mKeyguardStateController.isOccluded()).thenReturn(true);

        // When
        mNotificationActivityStarter.onNotificationClicked(
        mUnderTest.onNotificationClicked(
                mBubbleNotificationEntry, mBubbleNotificationRow);

        // Then
@@ -423,7 +468,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
        when(mKeyguardStateController.isOccluded()).thenReturn(true);

        // When
        mNotificationActivityStarter.onNotificationBubbleIconClicked(mBubbleNotificationEntry);
        mUnderTest.onNotificationBubbleIconClicked(mBubbleNotificationEntry);

        // Then
        verify(mBubblesManager).onUserChangedBubble(mBubbleNotificationEntry, false);
@@ -448,7 +493,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
        when(mKeyguardStateController.isOccluded()).thenReturn(true);

        // When
        mNotificationActivityStarter.onNotificationBubbleIconClicked(mNotificationEntry);
        mUnderTest.onNotificationBubbleIconClicked(mNotificationEntry);

        // Then
        verify(mBubblesManager).onUserChangedBubble(mNotificationEntry, true);
@@ -481,7 +526,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {

        // WHEN the intent is launched while dozing
        when(mStatusBarStateController.isDozing()).thenReturn(true);
        mNotificationActivityStarter.launchFullScreenIntent(entry);
        mUnderTest.launchFullScreenIntent(entry);

        // THEN display should try wake up for the full screen intent
        assertThat(mPowerRepository.getLastWakeReason()).isNotNull();
@@ -517,7 +562,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
                .startMocking();

        // WHEN
        mNotificationActivityStarter.launchFullScreenIntent(entry);
        mUnderTest.launchFullScreenIntent(entry);

        // THEN the full screen intent should be logged to statsd.
        verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.FULL_SCREEN_INTENT_LAUNCHED,
+2 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import com.android.systemui.animation.activityTransitionAnimator
import com.android.systemui.assist.assistManager
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.activityStarter
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.settings.userTracker
@@ -56,6 +57,7 @@ val Kosmos.statusBarNotificationActivityStarter by
            shadeDialogContextInteractor,
            fakeExecutorHandler,
            fakeExecutor,
            testScope,
            notificationVisibilityProvider,
            mockHeadsUpManager,
            activityStarter,