diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java index 36185b1573..2b3fb989ed 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java @@ -612,6 +612,7 @@ public class TaskbarManager { */ public void setSetupUIVisible(boolean isVisible) { mSharedState.setupUIVisible = isVisible; + mAllAppsActionManager.setSetupUiVisible(isVisible); TaskbarActivityContext taskbar = getTaskbarForDisplay(getDefaultDisplayId()); if (taskbar != null) { taskbar.setSetupUIVisible(isVisible); diff --git a/quickstep/src/com/android/quickstep/AllAppsActionManager.kt b/quickstep/src/com/android/quickstep/AllAppsActionManager.kt index 6fd68d551e..b807a4bfe3 100644 --- a/quickstep/src/com/android/quickstep/AllAppsActionManager.kt +++ b/quickstep/src/com/android/quickstep/AllAppsActionManager.kt @@ -21,10 +21,16 @@ import android.app.PendingIntent import android.app.RemoteAction import android.content.Context import android.graphics.drawable.Icon +import android.provider.Settings +import android.provider.Settings.Secure.USER_SETUP_COMPLETE import android.view.accessibility.AccessibilityManager import com.android.launcher3.R +import com.android.launcher3.util.SettingsCache +import com.android.launcher3.util.SettingsCache.OnChangeListener import java.util.concurrent.Executor +private val USER_SETUP_COMPLETE_URI = Settings.Secure.getUriFor(USER_SETUP_COMPLETE) + /** * Registers a [RemoteAction] for toggling All Apps if needed. * @@ -38,6 +44,12 @@ class AllAppsActionManager( private val createAllAppsPendingIntent: () -> PendingIntent, ) { + private val onSettingsChangeListener = OnChangeListener { v -> isUserSetupComplete = v } + + init { + SettingsCache.INSTANCE[context].register(USER_SETUP_COMPLETE_URI, onSettingsChangeListener) + } + /** `true` if home and overview are the same Activity. */ var isHomeAndOverviewSame = false set(value) { @@ -52,12 +64,27 @@ class AllAppsActionManager( updateSystemAction() } + /** `true` if the setup UI is visible. */ + var isSetupUiVisible = false + set(value) { + field = value + updateSystemAction() + } + + private var isUserSetupComplete = + SettingsCache.INSTANCE[context].getValue(USER_SETUP_COMPLETE_URI, 0) + set(value) { + field = value + updateSystemAction() + } + /** `true` if the action should be registered. */ var isActionRegistered = false private set private fun updateSystemAction() { - val shouldRegisterAction = isHomeAndOverviewSame || isTaskbarPresent + val isInSetupFlow = isSetupUiVisible || !isUserSetupComplete + val shouldRegisterAction = (isHomeAndOverviewSame || isTaskbarPresent) && !isInSetupFlow if (isActionRegistered == shouldRegisterAction) return isActionRegistered = shouldRegisterAction @@ -84,8 +111,10 @@ class AllAppsActionManager( isActionRegistered = false context .getSystemService(AccessibilityManager::class.java) - ?.unregisterSystemAction( - GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS, - ) + ?.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS) + SettingsCache.INSTANCE[context].unregister( + USER_SETUP_COMPLETE_URI, + onSettingsChangeListener, + ) } } diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/util/SettingsCacheSandbox.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/util/SettingsCacheSandbox.kt index dcd5352d97..52238c83d9 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/util/SettingsCacheSandbox.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/util/SettingsCacheSandbox.kt @@ -17,19 +17,22 @@ package com.android.launcher3.util import android.net.Uri +import com.android.launcher3.util.SettingsCache.OnChangeListener import org.mockito.kotlin.any import org.mockito.kotlin.doAnswer import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever -/** - * Provides a sandboxed [SettingsCache] for testing. - * - * Note that listeners registered to [cache] will never be invoked. - */ +/** Provides [SettingsCache] sandboxed from system settings for testing. */ class SettingsCacheSandbox { private val values = mutableMapOf() + private val listeners = mutableMapOf>() - /** Fake cache that delegates [SettingsCache.getValue] to [values]. */ + /** + * Fake cache that delegates: + * - [SettingsCache.getValue] to [values] + * - [SettingsCache.mListenerMap] to [listeners]. + */ val cache = mock { on { getValue(any()) } doAnswer { mock.getValue(it.getArgument(0), 1) } @@ -37,11 +40,22 @@ class SettingsCacheSandbox { { values.getOrDefault(it.getArgument(0), it.getArgument(1)) == 1 } + + doAnswer { + listeners.getOrPut(it.getArgument(0)) { mutableSetOf() }.add(it.getArgument(1)) + } + .whenever(mock) + .register(any(), any()) + doAnswer { listeners[it.getArgument(0)]?.remove(it.getArgument(1)) } + .whenever(mock) + .unregister(any(), any()) } operator fun get(key: Uri): Int? = values[key] operator fun set(key: Uri, value: Int) { + if (value == values[key]) return values[key] = value + listeners[key]?.forEach { it.onSettingsChanged(value == 1) } } } diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/AllAppsActionManagerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/AllAppsActionManagerTest.kt index 73b35e8a5c..a1bd107caf 100644 --- a/quickstep/tests/multivalentTests/src/com/android/quickstep/AllAppsActionManagerTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/AllAppsActionManagerTest.kt @@ -18,32 +18,59 @@ package com.android.quickstep import android.app.PendingIntent import android.content.IIntentSender +import android.provider.Settings +import android.provider.Settings.Secure.USER_SETUP_COMPLETE import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.dagger.LauncherAppComponent +import com.android.launcher3.dagger.LauncherAppSingleton +import com.android.launcher3.util.AllModulesForTest import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR +import com.android.launcher3.util.SandboxApplication +import com.android.launcher3.util.SettingsCache +import com.android.launcher3.util.SettingsCacheSandbox import com.android.launcher3.util.TestUtil import com.google.common.truth.Truth.assertThat +import dagger.BindsInstance +import dagger.Component import java.util.concurrent.Semaphore import java.util.concurrent.TimeUnit.SECONDS +import org.junit.After +import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith private const val TIMEOUT = 5L +private val USER_SETUP_COMPLETE_URI = Settings.Secure.getUriFor(USER_SETUP_COMPLETE) @RunWith(AndroidJUnit4::class) class AllAppsActionManagerTest { private val callbackSemaphore = Semaphore(0) private val bgExecutor = UI_HELPER_EXECUTOR - private val allAppsActionManager = - AllAppsActionManager( - InstrumentationRegistry.getInstrumentation().targetContext, - bgExecutor, - ) { - callbackSemaphore.release() - PendingIntent(IIntentSender.Default()) + @get:Rule val context = SandboxApplication() + + private val settingsCacheSandbox = + SettingsCacheSandbox().also { it[USER_SETUP_COMPLETE_URI] = 1 } + + private val allAppsActionManager by + lazy(LazyThreadSafetyMode.NONE) { + AllAppsActionManager(context, bgExecutor) { + callbackSemaphore.release() + PendingIntent(IIntentSender.Default()) + } } + @Before + fun initDaggerComponent() { + context.initDaggerComponent( + DaggerAllAppsActionManagerTestComponent.builder() + .bindSettingsCache(settingsCacheSandbox.cache) + ) + } + + @After fun destroyManager() = allAppsActionManager.onDestroy() + @Test fun taskbarPresent_actionRegistered() { allAppsActionManager.isTaskbarPresent = true @@ -88,4 +115,50 @@ class AllAppsActionManagerTest { assertThat(callbackSemaphore.tryAcquire(TIMEOUT, SECONDS)).isTrue() assertThat(allAppsActionManager.isActionRegistered).isTrue() } + + @Test + fun taskbarPresent_userSetupIncomplete_actionUnregistered() { + settingsCacheSandbox[USER_SETUP_COMPLETE_URI] = 0 + allAppsActionManager.isTaskbarPresent = true + assertThat(allAppsActionManager.isActionRegistered).isFalse() + } + + @Test + fun taskbarPresent_setupUiVisible_actionUnregistered() { + allAppsActionManager.isSetupUiVisible = true + allAppsActionManager.isTaskbarPresent = true + assertThat(allAppsActionManager.isActionRegistered).isFalse() + } + + @Test + fun taskbarPresent_userSetupCompleted_actionRegistered() { + settingsCacheSandbox[USER_SETUP_COMPLETE_URI] = 0 + allAppsActionManager.isTaskbarPresent = true + + settingsCacheSandbox[USER_SETUP_COMPLETE_URI] = 1 + assertThat(callbackSemaphore.tryAcquire(TIMEOUT, SECONDS)).isTrue() + assertThat(allAppsActionManager.isActionRegistered).isTrue() + } + + @Test + fun taskbarPresent_setupUiDismissed_actionRegistered() { + allAppsActionManager.isSetupUiVisible = true + allAppsActionManager.isTaskbarPresent = true + + allAppsActionManager.isSetupUiVisible = false + assertThat(callbackSemaphore.tryAcquire(TIMEOUT, SECONDS)).isTrue() + assertThat(allAppsActionManager.isActionRegistered).isTrue() + } +} + +@LauncherAppSingleton +@Component(modules = [AllModulesForTest::class]) +interface AllAppsActionManagerTestComponent : LauncherAppComponent { + + @Component.Builder + interface Builder : LauncherAppComponent.Builder { + @BindsInstance fun bindSettingsCache(settingsCache: SettingsCache): Builder + + override fun build(): AllAppsActionManagerTestComponent + } }