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

Commit c28eb348 authored by Beverly Tai's avatar Beverly Tai Committed by Android (Google) Code Review
Browse files

Merge "Setup interface for ActivityStarterInternal" into main

parents 66dd0d61 1ad8b6db
Loading
Loading
Loading
Loading
+7 −309
Original line number Diff line number Diff line
@@ -16,57 +16,20 @@

package com.android.systemui.statusbar.phone

import android.app.ActivityOptions
import android.app.PendingIntent
import android.content.Intent
import android.os.Bundle
import android.os.RemoteException
import android.os.UserHandle
import android.view.View
import android.widget.FrameLayout
import android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR
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.SysuiTestCase
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.LaunchableView
import com.android.systemui.assist.AssistManager
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
import com.android.systemui.shade.data.repository.ShadeAnimationRepository
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
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 dagger.Lazy
import java.util.Optional
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
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.MockitoAnnotations
@@ -74,177 +37,22 @@ import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
class ActivityStarterImplTest : SysuiTestCase() {
    @Mock private lateinit var centralSurfaces: CentralSurfaces
    @Mock private lateinit var assistManager: AssistManager
    @Mock private lateinit var dozeServiceHost: DozeServiceHost
    @Mock private lateinit var biometricUnlockController: BiometricUnlockController
    @Mock private lateinit var keyguardViewMediator: KeyguardViewMediator
    @Mock private lateinit var shadeController: ShadeController
    @Mock private lateinit var commandQueue: CommandQueue
    @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
    @Mock private lateinit var mActivityTransitionAnimator: ActivityTransitionAnimator
    @Mock private lateinit var lockScreenUserManager: NotificationLockscreenUserManager
    @Mock private lateinit var statusBarWindowController: StatusBarWindowController
    @Mock private lateinit var notifShadeWindowController: NotificationShadeWindowController
    @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
    @Mock private lateinit var keyguardStateController: KeyguardStateController
    @Mock private lateinit var legacyActivityStarterInternal: LegacyActivityStarterInternalImpl
    @Mock private lateinit var activityStarterInternal: ActivityStarterInternalImpl
    @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
    @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
    @Mock private lateinit var userTracker: UserTracker
    @Mock private lateinit var activityIntentHelper: ActivityIntentHelper
    private lateinit var underTest: ActivityStarterImpl
    private val mainExecutor = FakeExecutor(FakeSystemClock())
    private val shadeAnimationInteractor =
        ShadeAnimationInteractorLegacyImpl(ShadeAnimationRepository(), FakeShadeRepository())

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)
        underTest =
            ActivityStarterImpl(
                Lazy { Optional.of(centralSurfaces) },
                Lazy { assistManager },
                Lazy { dozeServiceHost },
                Lazy { biometricUnlockController },
                Lazy { keyguardViewMediator },
                Lazy { shadeController },
                commandQueue,
                shadeAnimationInteractor,
                Lazy { statusBarKeyguardViewManager },
                Lazy { notifShadeWindowController },
                mActivityTransitionAnimator,
                context,
                DISPLAY_ID,
                lockScreenUserManager,
                statusBarWindowController,
                wakefulnessLifecycle,
                keyguardStateController,
                statusBarStateController,
                keyguardUpdateMonitor,
                deviceProvisionedController,
                userTracker,
                activityIntentHelper,
                mainExecutor,
                statusBarStateController = statusBarStateController,
                mainExecutor = mainExecutor,
                legacyActivityStarter = { legacyActivityStarterInternal },
                activityStarterInternal = { activityStarterInternal },
            )
        whenever(userTracker.userHandle).thenReturn(UserHandle.OWNER)
    }

    @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)

        underTest.startPendingIntentDismissingKeyguard(pendingIntent)
        mainExecutor.runAllReady()

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

    @Test
    fun startPendingIntentMaybeDismissingKeyguard_keyguardShowing_showOverLs_launchAnimator() {
        val pendingIntent = mock(PendingIntent::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)
        whenever(pendingIntent.isActivity).thenReturn(true)
        whenever(keyguardStateController.isShowing).thenReturn(true)
        whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
        whenever(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt()))
            .thenReturn(true)

        underTest.startPendingIntentMaybeDismissingKeyguard(
            intent = pendingIntent,
            animationController = controller,
            intentSentUiThreadCallback = null,
        )
        mainExecutor.runAllReady()

        verify(mActivityTransitionAnimator)
            .startPendingIntentWithAnimation(
                nullable(),
                eq(true),
                nullable(),
                eq(true),
                any(),
            )
    }

    fun startPendingIntentDismissingKeyguard_fillInIntentAndExtraOptions_sendAndReturnResult() {
        val pendingIntent = mock(PendingIntent::class.java)
        val fillInIntent = 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)
        whenever(pendingIntent.isActivity).thenReturn(true)
        whenever(keyguardStateController.isShowing).thenReturn(true)
        whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
        whenever(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt()))
            .thenReturn(false)

        // extra activity options to set on pending intent
        val activityOptions = mock(ActivityOptions::class.java)
        activityOptions.splashScreenStyle = SPLASH_SCREEN_STYLE_SOLID_COLOR
        activityOptions.isPendingIntentBackgroundActivityLaunchAllowedByPermission = false
        val bundleCaptor = argumentCaptor<Bundle>()

        underTest.startPendingIntentMaybeDismissingKeyguard(
            intent = pendingIntent,
            animationController = controller,
            intentSentUiThreadCallback = null,
            fillInIntent = fillInIntent,
            extraOptions = activityOptions.toBundle(),
        )
        mainExecutor.runAllReady()

        // Fill-in intent is passed and options contain extra values specified
        verify(pendingIntent)
            .sendAndReturnResult(
                eq(context),
                eq(0),
                eq(fillInIntent),
                nullable(),
                nullable(),
                nullable(),
                bundleCaptor.capture()
            )
        val options = ActivityOptions.fromBundle(bundleCaptor.value)
        assertThat(options.isPendingIntentBackgroundActivityLaunchAllowedByPermission).isFalse()
        assertThat(options.splashScreenStyle).isEqualTo(SPLASH_SCREEN_STYLE_SOLID_COLOR)
    }

    @Test
    fun startPendingIntentDismissingKeyguard_associatedView_getAnimatorController() {
        val pendingIntent = mock(PendingIntent::class.java)
        val associatedView = mock(ExpandableNotificationRow::class.java)

        underTest.startPendingIntentDismissingKeyguard(
            intent = pendingIntent,
            intentSentUiThreadCallback = null,
            associatedView = associatedView,
        )

        verify(centralSurfaces).getAnimatorControllerFromNotification(associatedView)
    }

    @Test
    fun startActivity_noUserHandleProvided_getUserHandle() {
        val intent = mock(Intent::class.java)

        underTest.startActivity(intent, false)

        verify(userTracker).userHandle
    }

    @Test
@@ -258,115 +66,9 @@ class ActivityStarterImplTest : SysuiTestCase() {

    @Test
    fun postStartActivityDismissingKeyguard_intent_postsOnMain() {
        whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
        val intent = mock(Intent::class.java)

        underTest.postStartActivityDismissingKeyguard(intent, 0)
        underTest.postStartActivityDismissingKeyguard(mock(Intent::class.java), 0)

        assertThat(mainExecutor.numPending()).isEqualTo(1)
        mainExecutor.runAllReady()

        verify(deviceProvisionedController).isDeviceProvisioned
        verify(shadeController).collapseShadeForActivityStart()
    }

    @Test
    fun postStartActivityDismissingKeyguard_intent_notDeviceProvisioned_doesNotProceed() {
        whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false)
        val intent = mock(Intent::class.java)

        underTest.postStartActivityDismissingKeyguard(intent, 0)
        mainExecutor.runAllReady()

        verify(deviceProvisionedController).isDeviceProvisioned
        verify(shadeController, never()).collapseShadeForActivityStart()
    }

    @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)

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

        verify(biometricUnlockController)
            .startWakeAndUnlock(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING)
    }

    @Test
    fun dismissKeyguardThenExecute_keyguardIsShowing_dismissWithAction() {
        val customMessage = "Enter your pin."
        whenever(keyguardStateController.isShowing).thenReturn(true)

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

        verify(statusBarKeyguardViewManager)
            .dismissWithAction(
                any(OnDismissAction::class.java),
                any(Runnable::class.java),
                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)

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

        verify(centralSurfaces).awakenDreams()
        assertThat(dismissActionExecuted).isTrue()
    }

    @Test
    @Throws(RemoteException::class)
    fun executeRunnableDismissingKeyguard_dreaming_notShowing_awakenDreams() {
        whenever(keyguardStateController.isShowing).thenReturn(false)
        whenever(keyguardStateController.isOccluded).thenReturn(false)
        whenever(keyguardUpdateMonitor.isDreaming).thenReturn(true)

        underTest.executeRunnableDismissingKeyguard(
            runnable = {},
            cancelAction = null,
            dismissShade = false,
            afterKeyguardGone = false,
            deferred = false
        )

        verify(centralSurfaces, times(1)).awakenDreams()
    }

    @Test
    @Throws(RemoteException::class)
    fun executeRunnableDismissingKeyguard_notDreaming_notShowing_doNotAwakenDreams() {
        whenever(keyguardStateController.isShowing).thenReturn(false)
        whenever(keyguardStateController.isOccluded).thenReturn(false)
        whenever(keyguardUpdateMonitor.isDreaming).thenReturn(false)

        underTest.executeRunnableDismissingKeyguard(
            runnable = {},
            cancelAction = null,
            dismissShade = false,
            afterKeyguardGone = false,
            deferred = false
        )

        verify(centralSurfaces, never()).awakenDreams()
    }

    @Test
@@ -377,8 +79,4 @@ class ActivityStarterImplTest : SysuiTestCase() {
        mainExecutor.runAllReady()
        verify(statusBarStateController).setLeaveOpenOnKeyguardHide(true)
    }

    private companion object {
        private const val DISPLAY_ID = 0
    }
}
+358 −0

File added.

Preview size limit exceeded, changes collapsed.

+12 −627

File changed.

Preview size limit exceeded, changes collapsed.

+88 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.statusbar.phone

import android.app.PendingIntent
import android.content.Intent
import android.os.Bundle
import android.os.UserHandle
import android.view.View
import com.android.systemui.ActivityIntentHelper
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.plugins.ActivityStarter

interface ActivityStarterInternal {
    /**
     * Starts a pending intent after dismissing keyguard.
     *
     * This can be called in a background thread (to prevent calls in [ActivityIntentHelper] in the
     * main thread).
     */
    fun startPendingIntentDismissingKeyguard(
        intent: PendingIntent,
        intentSentUiThreadCallback: Runnable? = null,
        associatedView: View? = null,
        animationController: ActivityTransitionAnimator.Controller? = null,
        showOverLockscreen: Boolean = false,
        fillInIntent: Intent? = null,
        extraOptions: Bundle? = null,
    )

    /** Starts an activity after dismissing keyguard. */
    fun startActivityDismissingKeyguard(
        intent: Intent,
        dismissShade: Boolean,
        onlyProvisioned: Boolean = false,
        callback: ActivityStarter.Callback? = null,
        flags: Int = 0,
        animationController: ActivityTransitionAnimator.Controller? = null,
        customMessage: String? = null,
        disallowEnterPictureInPictureWhileLaunching: Boolean = false,
        userHandle: UserHandle? = null,
    )

    /** Starts an Activity. */
    fun startActivity(
        intent: Intent,
        dismissShade: Boolean,
        animationController: ActivityTransitionAnimator.Controller?,
        showOverLockscreenWhenLocked: Boolean,
        userHandle: UserHandle? = null,
    )

    /** Executes an action after dismissing keyguard. */
    fun dismissKeyguardThenExecute(
        action: ActivityStarter.OnDismissAction,
        cancel: Runnable?,
        afterKeyguardGone: Boolean,
        customMessage: String? = null,
    )

    /** Executes an action after dismissing keyguard. */
    fun executeRunnableDismissingKeyguard(
        runnable: Runnable?,
        cancelAction: Runnable? = null,
        dismissShade: Boolean = false,
        afterKeyguardGone: Boolean = false,
        deferred: Boolean = false,
        willAnimateOnKeyguard: Boolean = false,
        customMessage: String? = null,
    )

    /** Whether we should animate an activity launch. */
    fun shouldAnimateLaunch(isActivityIntent: Boolean): Boolean
}
+96 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.statusbar.phone

import android.app.PendingIntent
import android.content.Intent
import android.os.Bundle
import android.os.UserHandle
import android.view.View
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.ActivityStarter
import javax.inject.Inject

/**
 * Encapsulates the activity logic for activity starter when flexiglass is enabled.
 *
 * TODO: b/308819693
 */
@SysUISingleton
class ActivityStarterInternalImpl @Inject constructor() : ActivityStarterInternal {
    override fun startPendingIntentDismissingKeyguard(
        intent: PendingIntent,
        intentSentUiThreadCallback: Runnable?,
        associatedView: View?,
        animationController: ActivityTransitionAnimator.Controller?,
        showOverLockscreen: Boolean,
        fillInIntent: Intent?,
        extraOptions: Bundle?
    ) {
        TODO("Not yet implemented b/308819693")
    }

    override fun startActivityDismissingKeyguard(
        intent: Intent,
        dismissShade: Boolean,
        onlyProvisioned: Boolean,
        callback: ActivityStarter.Callback?,
        flags: Int,
        animationController: ActivityTransitionAnimator.Controller?,
        customMessage: String?,
        disallowEnterPictureInPictureWhileLaunching: Boolean,
        userHandle: UserHandle?
    ) {
        TODO("Not yet implemented b/308819693")
    }

    override fun startActivity(
        intent: Intent,
        dismissShade: Boolean,
        animationController: ActivityTransitionAnimator.Controller?,
        showOverLockscreenWhenLocked: Boolean,
        userHandle: UserHandle?
    ) {
        TODO("Not yet implemented b/308819693")
    }

    override fun dismissKeyguardThenExecute(
        action: ActivityStarter.OnDismissAction,
        cancel: Runnable?,
        afterKeyguardGone: Boolean,
        customMessage: String?
    ) {
        TODO("Not yet implemented b/308819693")
    }

    override fun executeRunnableDismissingKeyguard(
        runnable: Runnable?,
        cancelAction: Runnable?,
        dismissShade: Boolean,
        afterKeyguardGone: Boolean,
        deferred: Boolean,
        willAnimateOnKeyguard: Boolean,
        customMessage: String?
    ) {
        TODO("Not yet implemented b/308819693")
    }

    override fun shouldAnimateLaunch(isActivityIntent: Boolean): Boolean {
        TODO("Not yet implemented b/308819693")
    }
}
Loading