Make ZenIconLoader injectable (in SystemUI)
And provide a direct-executor-service version for ZenModeInteractor in Kosmos.
(This allows us to get rid of all usages of ZenIconLoader.setInstance(), which was used exclusively for this exact purpose).
Bug: 360399800
Test: atest ModesTileTest ZenModeInteractorTest
Flag: android.app.modes_ui
Change-Id: I16219ef486b17b2e5f6819008bcc6fb0697b0f80
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java
index fe0f98a..43c6c50 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java
@@ -31,7 +31,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
import com.google.common.util.concurrent.FluentFuture;
import com.google.common.util.concurrent.ListenableFuture;
@@ -53,25 +52,22 @@
private final LruCache<ZenIcon.Key, Drawable> mCache;
private final ListeningExecutorService mBackgroundExecutor;
+ /** Obtains the singleton {@link ZenIconLoader}. */
public static ZenIconLoader getInstance() {
if (sInstance == null) {
- sInstance = new ZenIconLoader();
+ sInstance = new ZenIconLoader(Executors.newFixedThreadPool(4));
}
return sInstance;
}
- /** Replaces the singleton instance of {@link ZenIconLoader} by the provided one. */
- @VisibleForTesting(otherwise = VisibleForTesting.NONE)
- public static void setInstance(@Nullable ZenIconLoader instance) {
- sInstance = instance;
- }
-
- private ZenIconLoader() {
- this(Executors.newFixedThreadPool(4));
- }
-
- @VisibleForTesting
- public ZenIconLoader(ExecutorService backgroundExecutor) {
+ /**
+ * Constructs a ZenIconLoader with the specified {@code backgroundExecutor}.
+ *
+ * <p>ZenIconLoader <em>should be a singleton</em>, so this should only be used to instantiate
+ * and provide the singleton instance in a module. If the app doesn't support dependency
+ * injection, use {@link #getInstance} instead.
+ */
+ public ZenIconLoader(@NonNull ExecutorService backgroundExecutor) {
mCache = new LruCache<>(50);
mBackgroundExecutor =
MoreExecutors.listeningDecorator(backgroundExecutor);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
index 03660e9..5fd3a24 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
@@ -25,7 +25,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
-import com.android.settingslib.notification.modes.ZenIconLoader
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.asIcon
import com.android.systemui.coroutines.collectLastValue
@@ -38,7 +37,6 @@
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
-import com.google.common.util.concurrent.MoreExecutors
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.toCollection
@@ -66,8 +64,6 @@
addOverride(R.drawable.ic_zen_mode_type_bedtime, BEDTIME_DRAWABLE)
addOverride(R.drawable.ic_zen_mode_type_driving, DRIVING_DRAWABLE)
}
- // TODO: b/360399800 - Remove; ZenIconLoader should always use direct executor for tests
- ZenIconLoader.setInstance(ZenIconLoader(MoreExecutors.newDirectExecutorService()))
}
@EnableFlags(Flags.FLAG_MODES_UI)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt
index 468e682..d2bc54e0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt
@@ -23,7 +23,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.notification.modes.TestModeBuilder
-import com.android.settingslib.notification.modes.ZenIconLoader
import com.android.settingslib.notification.modes.ZenMode
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -35,12 +34,10 @@
import com.android.systemui.statusbar.policy.ui.dialog.mockModesDialogEventLogger
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
-import com.google.common.util.concurrent.MoreExecutors
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.clearInvocations
@@ -67,12 +64,6 @@
mockDialogEventLogger
)
- @Before
- fun setUp() {
- // TODO: b/360399800 - Remove; ZenIconLoader should always use direct executor for tests
- ZenIconLoader.setInstance(ZenIconLoader(MoreExecutors.newDirectExecutorService()))
- }
-
@Test
fun tiles_filtersOutUserDisabledModes() =
testScope.runTest {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index 71bcdfcb..6cebcbd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -22,8 +22,10 @@
import com.android.internal.R;
import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
+import com.android.settingslib.notification.modes.ZenIconLoader;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.LogBufferFactory;
import com.android.systemui.settings.UserTracker;
@@ -79,6 +81,7 @@
import dagger.Provides;
import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
import javax.inject.Named;
@@ -236,4 +239,12 @@
static LogBuffer provideCastControllerLog(LogBufferFactory factory) {
return factory.create("CastControllerLog", 50);
}
+
+ /** Provides a {@link ZenIconLoader} that fetches icons in a background thread. */
+ @Provides
+ @SysUISingleton
+ static ZenIconLoader provideZenIconLoader(
+ @UiBackground ExecutorService backgroundExecutorService) {
+ return new ZenIconLoader(backgroundExecutorService);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
index e87916e..520f3f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
@@ -50,9 +50,8 @@
private val zenModeRepository: ZenModeRepository,
private val notificationSettingsRepository: NotificationSettingsRepository,
@Background private val bgDispatcher: CoroutineDispatcher,
+ private val iconLoader: ZenIconLoader,
) {
- private val iconLoader: ZenIconLoader = ZenIconLoader.getInstance()
-
val isZenModeEnabled: Flow<Boolean> =
zenModeRepository.globalZenMode
.map {
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
index ecf1165..70774f13 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
@@ -28,6 +28,7 @@
import dagger.Provides;
import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.inject.Singleton;
@@ -81,6 +82,18 @@
@Singleton
@UiBackground
public static Executor provideUiBackgroundExecutor() {
+ return provideUiBackgroundExecutorService();
+ }
+
+ /**
+ * Provide an ExecutorService specifically for running UI operations on a separate thread.
+ *
+ * <p>Keep submitted runnables short and to the point, just as with any other UI code.
+ */
+ @Provides
+ @Singleton
+ @UiBackground
+ public static ExecutorService provideUiBackgroundExecutorService() {
return Executors.newSingleThreadExecutor();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
index dadfb37..8435b1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
@@ -25,7 +25,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.settingslib.notification.modes.ZenIconLoader
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.kosmos.testDispatcher
@@ -51,13 +50,11 @@
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
-import com.google.common.util.concurrent.MoreExecutors
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
-import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
@@ -74,15 +71,6 @@
private val testScope = kosmos.testScope
private val testDispatcher = kosmos.testDispatcher
- companion object {
- @BeforeClass
- @JvmStatic
- fun setup() {
- // TODO: b/360399800 - Remove; ZenIconLoader should always use direct executor for tests
- ZenIconLoader.setInstance(ZenIconLoader(MoreExecutors.newDirectExecutorService()))
- }
- }
-
@Mock private lateinit var qsHost: QSHost
@Mock private lateinit var metricsLogger: MetricsLogger
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
index 8d6f50f..a0b0572 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
@@ -35,7 +35,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.notification.modes.TestModeBuilder
-import com.android.settingslib.notification.modes.ZenIconLoader
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -71,14 +70,12 @@
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.time.DateFormatUtil
import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.util.concurrent.MoreExecutors
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
-import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Answers
@@ -114,12 +111,6 @@
private const val SCREEN_RECORD_SLOT = "screen_record"
private const val CONNECTED_DISPLAY_SLOT = "connected_display"
private const val MANAGED_PROFILE_SLOT = "managed_profile"
-
- @BeforeClass
- @JvmStatic
- fun setup() {
- ZenIconLoader.setInstance(ZenIconLoader(MoreExecutors.newDirectExecutorService()))
- }
}
@Mock private lateinit var iconController: StatusBarIconController
diff --git a/packages/SystemUI/tests/utils/src/com/android/settingslib/notification/modes/ZenIconLoaderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/settingslib/notification/modes/ZenIconLoaderKosmos.kt
new file mode 100644
index 0000000..8541d77
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/settingslib/notification/modes/ZenIconLoaderKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.settingslib.notification.modes
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.google.common.util.concurrent.MoreExecutors
+
+val Kosmos.zenIconLoader by Fixture { ZenIconLoader(MoreExecutors.newDirectExecutorService()) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt
index f1f876c..61b53c9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.policy.domain.interactor
import android.content.testableContext
+import com.android.settingslib.notification.modes.zenIconLoader
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testDispatcher
@@ -29,5 +30,6 @@
zenModeRepository = zenModeRepository,
notificationSettingsRepository = notificationSettingsRepository,
bgDispatcher = testDispatcher,
+ iconLoader = zenIconLoader,
)
}