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

Commit a045a061 authored by Xiaowen Lei's avatar Xiaowen Lei Committed by Automerger Merge Worker
Browse files

Merge "Add standalone weather view to lockscreen if decoupling isenabled."...

Merge "Add standalone weather view to lockscreen if decoupling isenabled." into tm-qpr-dev am: 7e9ff1ef

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21062126



Change-Id: Ifc4eb005555b1baefd6b19e74e40fe594d59e33f
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 9639e4c2 7e9ff1ef
Loading
Loading
Loading
Loading
+24 −3
Original line number Diff line number Diff line
@@ -88,7 +88,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
    private final ClockRegistry.ClockChangeListener mClockChangedListener;

    private ViewGroup mStatusArea;
    // If set will replace keyguard_slice_view

    // If the SMARTSPACE flag is set, keyguard_slice_view is replaced by the following views.
    private View mWeatherView;
    private View mSmartspaceView;

    private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@@ -192,10 +194,17 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS

        if (mSmartspaceController.isEnabled()) {
            View ksv = mView.findViewById(R.id.keyguard_slice_view);
            int ksvIndex = mStatusArea.indexOfChild(ksv);
            int viewIndex = mStatusArea.indexOfChild(ksv);
            ksv.setVisibility(View.GONE);

            addSmartspaceView(ksvIndex);
            // TODO(b/261757708): add content observer for the Settings toggle and add/remove
            //  weather according to the Settings.
            if (mSmartspaceController.isDateWeatherDecoupled()) {
                addWeatherView(viewIndex);
                viewIndex += 1;
            }

            addSmartspaceView(viewIndex);
        }

        mSecureSettings.registerContentObserverForUser(
@@ -237,6 +246,18 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
        }
    }

    private void addWeatherView(int index) {
        mWeatherView = mSmartspaceController.buildAndConnectWeatherView(mView);
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
                MATCH_PARENT, WRAP_CONTENT);
        mStatusArea.addView(mWeatherView, index, lp);
        int startPadding = getContext().getResources().getDimensionPixelSize(
                R.dimen.below_clock_padding_start);
        int endPadding = getContext().getResources().getDimensionPixelSize(
                R.dimen.below_clock_padding_end);
        mWeatherView.setPaddingRelative(startPadding, 0, endPadding, 0);
    }

    private void addSmartspaceView(int index) {
        mSmartspaceView = mSmartspaceController.buildAndConnectView(mView);
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+6 −0
Original line number Diff line number Diff line
@@ -107,6 +107,8 @@ import com.android.wm.shell.bubbles.Bubbles;
import java.util.Optional;
import java.util.concurrent.Executor;

import javax.inject.Named;

import dagger.Binds;
import dagger.BindsOptionalOf;
import dagger.Module;
@@ -214,6 +216,10 @@ public abstract class SystemUIModule {
    @BindsOptionalOf
    abstract BcSmartspaceConfigPlugin optionalBcSmartspaceConfigPlugin();

    @BindsOptionalOf
    @Named(SmartspaceModule.WEATHER_SMARTSPACE_DATA_PLUGIN)
    abstract BcSmartspaceDataPlugin optionalWeatherSmartspaceConfigPlugin();

    @BindsOptionalOf
    abstract Recents optionalRecents();

+5 −12
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.systemui.smartspace.dagger
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.smartspace.SmartspacePrecondition
import com.android.systemui.smartspace.SmartspaceTargetFilter
import com.android.systemui.smartspace.filters.LockscreenAndDreamTargetFilter
import com.android.systemui.smartspace.preconditions.LockscreenPrecondition
import dagger.Binds
import dagger.BindsOptionalOf
@@ -34,11 +33,6 @@ abstract class SmartspaceModule {
         */
        const val DREAM_SMARTSPACE_DATA_PLUGIN = "dreams_smartspace_data_plugin"

        /**
         * The lockscreen smartspace target filter.
         */
        const val LOCKSCREEN_SMARTSPACE_TARGET_FILTER = "lockscreen_smartspace_target_filter"

        /**
         * The dream smartspace target filter.
         */
@@ -48,6 +42,11 @@ abstract class SmartspaceModule {
         * The precondition for dream smartspace
         */
        const val DREAM_SMARTSPACE_PRECONDITION = "dream_smartspace_precondition"

        /**
         * The BcSmartspaceDataPlugin for the standalone weather.
         */
        const val WEATHER_SMARTSPACE_DATA_PLUGIN = "weather_smartspace_data_plugin"
    }

    @BindsOptionalOf
@@ -58,12 +57,6 @@ abstract class SmartspaceModule {
    @Named(DREAM_SMARTSPACE_DATA_PLUGIN)
    abstract fun optionalDreamsBcSmartspaceDataPlugin(): BcSmartspaceDataPlugin?

    @Binds
    @Named(LOCKSCREEN_SMARTSPACE_TARGET_FILTER)
    abstract fun provideLockscreenSmartspaceTargetFilter(
        filter: LockscreenAndDreamTargetFilter?
    ): SmartspaceTargetFilter?

    @Binds
    @Named(DREAM_SMARTSPACE_PRECONDITION)
    abstract fun bindSmartspacePrecondition(
+62 −13
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.regionsampling.RegionSampler
import com.android.systemui.shared.regionsampling.UpdateColorCallback
import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.WEATHER_SMARTSPACE_DATA_PLUGIN
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
@@ -60,6 +61,7 @@ import com.android.systemui.util.settings.SecureSettings
import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Named

/** Controller for managing the smartspace view on the lockscreen */
@SysUISingleton
@@ -82,6 +84,8 @@ constructor(
        @Main private val uiExecutor: Executor,
        @Background private val bgExecutor: Executor,
        @Main private val handler: Handler,
        @Named(WEATHER_SMARTSPACE_DATA_PLUGIN)
        optionalWeatherPlugin: Optional<BcSmartspaceDataPlugin>,
        optionalPlugin: Optional<BcSmartspaceDataPlugin>,
        optionalConfigPlugin: Optional<BcSmartspaceConfigPlugin>,
) {
@@ -90,6 +94,7 @@ constructor(
    }

    private var session: SmartspaceSession? = null
    private val weatherPlugin: BcSmartspaceDataPlugin? = optionalWeatherPlugin.orElse(null)
    private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
    private val configPlugin: BcSmartspaceConfigPlugin? = optionalConfigPlugin.orElse(null)

@@ -131,6 +136,10 @@ constructor(

    private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
        execution.assertIsMainThread()

        // The weather data plugin takes unfiltered targets and performs the filtering internally.
        weatherPlugin?.onTargetsAvailable(targets)

        val filteredTargets = targets.filter(::filterSmartspaceTarget)
        plugin?.onTargetsAvailable(filteredTargets)
        if (!isContentUpdatedOnce) {
@@ -209,32 +218,58 @@ constructor(
        return plugin != null
    }

    fun isDateWeatherDecoupled(): Boolean {
        execution.assertIsMainThread()

        return featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED) &&
                weatherPlugin != null
    }

    private fun updateBypassEnabled() {
        val bypassEnabled = bypassController.bypassEnabled
        smartspaceViews.forEach { it.setKeyguardBypassEnabled(bypassEnabled) }
    }

    /**
     * Constructs the smartspace view and connects it to the smartspace service.
     * Constructs the weather view and connects it to the smartspace service.
     */
    fun buildAndConnectView(parent: ViewGroup): View? {
    fun buildAndConnectWeatherView(parent: ViewGroup): View? {
        execution.assertIsMainThread()

        if (!isEnabled()) {
            throw RuntimeException("Cannot build view when not enabled")
        }
        if (!isDateWeatherDecoupled()) {
            throw RuntimeException("Cannot build weather view when not decoupled")
        }

        val view = buildView(parent)
        val view = buildView(parent, weatherPlugin)
        connectSession()

        return view
    }

    fun requestSmartspaceUpdate() {
        session?.requestSmartspaceUpdate()
    /**
     * Constructs the smartspace view and connects it to the smartspace service.
     */
    fun buildAndConnectView(parent: ViewGroup): View? {
        execution.assertIsMainThread()

        if (!isEnabled()) {
            throw RuntimeException("Cannot build view when not enabled")
        }

    private fun buildView(parent: ViewGroup): View? {
        val view = buildView(parent, plugin, configPlugin)
        connectSession()

        return view
    }

    private fun buildView(
            parent: ViewGroup,
            plugin: BcSmartspaceDataPlugin?,
            configPlugin: BcSmartspaceConfigPlugin? = null
    ): View? {
        if (plugin == null) {
            return null
        }
@@ -242,7 +277,7 @@ constructor(
        val ssView = plugin.getView(parent)
        ssView.setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
        ssView.registerDataProvider(plugin)
        ssView.registerConfigProvider(configPlugin)
        configPlugin?.let { ssView.registerConfigProvider(it) }

        ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter {
            override fun startIntent(view: View, intent: Intent, showOnLockscreen: Boolean) {
@@ -273,7 +308,8 @@ constructor(
    }

    private fun connectSession() {
        if (plugin == null || session != null || smartspaceViews.isEmpty()) {
        if (weatherPlugin == null && plugin == null) return
        if (session != null || smartspaceViews.isEmpty()) {
            return
        }

@@ -310,14 +346,20 @@ constructor(
        statusBarStateController.addCallback(statusBarStateListener)
        bypassController.registerOnBypassStateChangedListener(bypassStateChangedListener)

        plugin.registerSmartspaceEventNotifier {
                e -> session?.notifySmartspaceEvent(e)
        }
        weatherPlugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) }
        plugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) }

        updateBypassEnabled()
        reloadSmartspace()
    }

    /**
     * Requests the smartspace session for an update.
     */
    fun requestSmartspaceUpdate() {
        session?.requestSmartspaceUpdate()
    }

    /**
     * Disconnects the smartspace view from the smartspace service and cleans up any resources.
     */
@@ -341,9 +383,13 @@ constructor(
        bypassController.unregisterOnBypassStateChangedListener(bypassStateChangedListener)
        session = null

        weatherPlugin?.registerSmartspaceEventNotifier(null)
        weatherPlugin?.onTargetsAvailable(emptyList())

        plugin?.registerSmartspaceEventNotifier(null)
        plugin?.onTargetsAvailable(emptyList())
        Log.d(TAG, "Ending smartspace session for lockscreen")

        Log.d(TAG, "Ended smartspace session for lockscreen")
    }

    fun addListener(listener: SmartspaceTargetListener) {
@@ -357,8 +403,11 @@ constructor(
    }

    private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
        if (isDateWeatherDecoupled()) {
            return t.featureType != SmartspaceTarget.FEATURE_WEATHER
        }
        if (!showNotifications) {
            return t.getFeatureType() == SmartspaceTarget.FEATURE_WEATHER
            return t.featureType == SmartspaceTarget.FEATURE_WEATHER
        }
        return when (t.userHandle) {
            userTracker.userHandle -> {
+153 −20
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.BcSmartspaceConfigPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin
@@ -111,6 +112,9 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
    @Mock
    private lateinit var handler: Handler

    @Mock
    private lateinit var weatherPlugin: BcSmartspaceDataPlugin

    @Mock
    private lateinit var plugin: BcSmartspaceDataPlugin

@@ -151,6 +155,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
        KeyguardBypassController.OnBypassStateChangedListener
    private lateinit var deviceProvisionedListener: DeviceProvisionedListener

    private lateinit var weatherSmartspaceView: SmartspaceView
    private lateinit var smartspaceView: SmartspaceView

    private val clock = FakeSystemClock()
@@ -176,16 +181,22 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
    fun setUp() {
        MockitoAnnotations.initMocks(this)

        // Todo(b/261760571): flip the flag value here when feature is launched, and update relevant
        //  tests.
        `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(false)

        `when`(secureSettings.getUriFor(PRIVATE_LOCKSCREEN_SETTING))
                .thenReturn(fakePrivateLockscreenSettingUri)
        `when`(secureSettings.getUriFor(NOTIF_ON_LOCKSCREEN_SETTING))
                .thenReturn(fakeNotifOnLockscreenSettingUri)
        `when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(smartspaceSession)
        `when`(weatherPlugin.getView(any())).thenReturn(
                createWeatherSmartspaceView(), createWeatherSmartspaceView())
        `when`(plugin.getView(any())).thenReturn(createSmartspaceView(), createSmartspaceView())
        `when`(userTracker.userProfiles).thenReturn(userList)
        `when`(statusBarStateController.dozeAmount).thenReturn(0.5f)
        `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true)
        `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true)
        `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
        `when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)

        setActiveUser(userHandlePrimary)
        setAllowPrivateNotifications(userHandlePrimary, true)
@@ -210,6 +221,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
                executor,
                bgExecutor,
                handler,
                Optional.of(weatherPlugin),
                Optional.of(plugin),
                Optional.of(configPlugin),
        )
@@ -218,11 +230,22 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
        deviceProvisionedListener = deviceProvisionedCaptor.value
    }

    @Test(expected = RuntimeException::class)
    fun testBuildAndConnectWeatherView_throwsIfDecouplingDisabled() {
        // GIVEN the feature flag is disabled
        `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(false)

        // WHEN we try to build the view
        controller.buildAndConnectWeatherView(fakeParent)

        // THEN an exception is thrown
    }

    @Test
    fun connectOnlyAfterDeviceIsProvisioned() {
    fun testBuildAndConnectView_connectsOnlyAfterDeviceIsProvisioned() {
        // GIVEN an unprovisioned device and an attempt to connect
        `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(false)
        `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(false)
        `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(false)
        `when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(false)

        // WHEN a connection attempt is made and view is attached
        val view = controller.buildAndConnectView(fakeParent)
@@ -232,8 +255,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
        verify(smartspaceManager, never()).createSmartspaceSession(any())

        // WHEN it does become provisioned
        `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true)
        `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true)
        `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
        `when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)
        deviceProvisionedListener.onUserSetupChanged()

        // THEN the session is created
@@ -243,7 +266,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
    }

    @Test
    fun testListenersAreRegistered() {
    fun testAddListener_registersListenersForPlugin() {
        // GIVEN a listener is added after a session is created
        connectSession()

@@ -252,10 +275,12 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {

        // THEN the listener is registered to the underlying plugin
        verify(plugin).registerListener(controllerListener)
        // The listener is registered only for the plugin, not the weather plugin.
        verify(weatherPlugin, never()).registerListener(any())
    }

    @Test
    fun testEarlyRegisteredListenersAreAttachedAfterConnected() {
    fun testAddListener_earlyRegisteredListenersAreAttachedAfterConnected() {
        // GIVEN a listener that is registered before the session is created
        controller.addListener(controllerListener)

@@ -264,10 +289,12 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {

        // THEN the listener is subsequently registered
        verify(plugin).registerListener(controllerListener)
        // The listener is registered only for the plugin, not the weather plugin.
        verify(weatherPlugin, never()).registerListener(any())
    }

    @Test
    fun testEmptyListIsEmittedAndNotifierRemovedAfterDisconnect() {
    fun testDisconnect_emitsEmptyListAndRemovesNotifier() {
        // GIVEN a registered listener on an active session
        connectSession()
        clearInvocations(plugin)
@@ -279,10 +306,12 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
        // THEN the listener receives an empty list of targets and unregisters the notifier
        verify(plugin).onTargetsAvailable(emptyList())
        verify(plugin).registerSmartspaceEventNotifier(null)
        verify(weatherPlugin).onTargetsAvailable(emptyList())
        verify(weatherPlugin).registerSmartspaceEventNotifier(null)
    }

    @Test
    fun testUserChangeReloadsSmartspace() {
    fun testUserChange_reloadsSmartspace() {
        // GIVEN a connected smartspace session
        connectSession()

@@ -294,7 +323,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
    }

    @Test
    fun testSettingsChangeReloadsSmartspace() {
    fun testSettingsChange_reloadsSmartspace() {
        // GIVEN a connected smartspace session
        connectSession()

@@ -306,7 +335,21 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
    }

    @Test
    fun testThemeChangeUpdatesTextColor() {
    fun testThemeChange_updatesTextColor() {
        // GIVEN a connected smartspace session
        connectSession()

        // WHEN the theme changes
        configChangeListener.onThemeChanged()

        // We update the new text color to match the wallpaper color
        verify(smartspaceView).setPrimaryTextColor(anyInt())
    }

    @Test
    fun testThemeChange_ifDecouplingEnabled_updatesTextColor() {
        `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)

        // GIVEN a connected smartspace session
        connectSession()

@@ -314,11 +357,12 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
        configChangeListener.onThemeChanged()

        // We update the new text color to match the wallpaper color
        verify(weatherSmartspaceView).setPrimaryTextColor(anyInt())
        verify(smartspaceView).setPrimaryTextColor(anyInt())
    }

    @Test
    fun testDozeAmountChangeUpdatesView() {
    fun testDozeAmountChange_updatesView() {
        // GIVEN a connected smartspace session
        connectSession()

@@ -330,7 +374,22 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
    }

    @Test
    fun testKeyguardBypassEnabledUpdatesView() {
    fun testDozeAmountChange_ifDecouplingEnabled_updatesViews() {
        `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)

        // GIVEN a connected smartspace session
        connectSession()

        // WHEN the doze amount changes
        statusBarStateListener.onDozeAmountChanged(0.1f, 0.7f)

        // We pass that along to the view
        verify(weatherSmartspaceView).setDozeAmount(0.7f)
        verify(smartspaceView).setDozeAmount(0.7f)
    }

    @Test
    fun testKeyguardBypassEnabled_updatesView() {
        // GIVEN a connected smartspace session
        connectSession()
        `when`(keyguardBypassController.bypassEnabled).thenReturn(true)
@@ -424,6 +483,27 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
        )))
    }

    @Test
    fun testSessionListener_ifDecouplingEnabled_weatherTargetIsFilteredOut() {
        `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
        connectSession()

        // WHEN we receive a list of targets
        val targets = listOf(
                makeTarget(1, userHandlePrimary, isSensitive = true),
                makeTarget(2, userHandlePrimary),
                makeTarget(3, userHandleManaged),
                makeTarget(4, userHandlePrimary, featureType = SmartspaceTarget.FEATURE_WEATHER)
        )

        sessionListener.onTargetsAvailable(targets)

        // THEN all non-sensitive content is still shown
        verify(plugin).onTargetsAvailable(eq(listOf(targets[0], targets[1], targets[2])))
        // No filtering is applied for the weather plugin
        verify(weatherPlugin).onTargetsAvailable(eq(targets))
    }

    @Test
    fun testSettingsAreReloaded() {
        // GIVEN a connected session where the privacy settings later flip to false
@@ -514,6 +594,16 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
        verify(smartspaceView2).registerConfigProvider(configPlugin)
    }

    @Test
    fun testWeatherViewUsesSameSession() {
        `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
        // GIVEN a connected session
        connectSession()

        // No checks is needed here, since connectSession() already checks internally that
        // createSmartspaceSession is invoked only once.
    }

    @Test
    fun testViewGetInitializedWithBypassEnabledState() {
        // GIVEN keyguard bypass is enabled.
@@ -531,8 +621,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
    fun testConnectAttemptBeforeInitializationShouldNotCreateSession() {
        // GIVEN an uninitalized smartspaceView
        // WHEN the device is provisioned
        `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true)
        `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true)
        `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
        `when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)
        deviceProvisionedListener.onDeviceProvisionedChanged()

        // THEN no calls to createSmartspaceSession should occur
@@ -542,9 +632,23 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
    }

    private fun connectSession() {
        if (controller.isDateWeatherDecoupled()) {
            val weatherView = controller.buildAndConnectWeatherView(fakeParent)
            weatherSmartspaceView = weatherView as SmartspaceView
            fakeParent.addView(weatherView)
            controller.stateChangeListener.onViewAttachedToWindow(weatherView)

            verify(weatherSmartspaceView).setUiSurface(
                    BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
            verify(weatherSmartspaceView).registerDataProvider(weatherPlugin)

            verify(weatherSmartspaceView).setPrimaryTextColor(anyInt())
            verify(weatherSmartspaceView).setDozeAmount(0.5f)
        }

        val view = controller.buildAndConnectView(fakeParent)
        smartspaceView = view as SmartspaceView

        fakeParent.addView(view)
        controller.stateChangeListener.onViewAttachedToWindow(view)

        verify(smartspaceView).setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
@@ -554,6 +658,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
                .addOnTargetsAvailableListener(any(), capture(sessionListenerCaptor))
        sessionListener = sessionListenerCaptor.value

        verify(smartspaceManager).createSmartspaceSession(any())

        verify(userTracker).addCallback(capture(userTrackerCaptor), any())
        userListener = userTrackerCaptor.value

@@ -578,9 +684,11 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {

        verify(smartspaceView).setPrimaryTextColor(anyInt())
        verify(smartspaceView).setDozeAmount(0.5f)
        clearInvocations(view)

        fakeParent.addView(view)
        if (controller.isDateWeatherDecoupled()) {
            clearInvocations(weatherSmartspaceView)
        }
        clearInvocations(smartspaceView)
    }

    private fun setActiveUser(userHandle: UserHandle) {
@@ -626,6 +734,31 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
        ).thenReturn(if (value) 1 else 0)
    }

    // Separate function for the weather view, which doesn't implement all functions in interface.
    private fun createWeatherSmartspaceView(): SmartspaceView {
        return spy(object : View(context), SmartspaceView {
            override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {
            }

            override fun setPrimaryTextColor(color: Int) {
            }

            override fun setIsDreaming(isDreaming: Boolean) {
            }

            override fun setUiSurface(uiSurface: String) {
            }

            override fun setDozeAmount(amount: Float) {
            }

            override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) {
            }

            override fun setFalsingManager(falsingManager: FalsingManager?) {
            }
        })
    }
    private fun createSmartspaceView(): SmartspaceView {
        return spy(object : View(context), SmartspaceView {
            override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {