diff options
8 files changed, 280 insertions, 21 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java index 6be74a0b5646..ce690e239da0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java @@ -26,6 +26,7 @@ import com.android.systemui.R; import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTileView; +import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository; import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor; import com.android.systemui.util.leak.GarbageMonitor; @@ -34,7 +35,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; -public interface QSHost extends PanelInteractor { +public interface QSHost extends PanelInteractor, CustomTileAddedRepository { String TILES_SETTING = Settings.Secure.QS_TILES; int POSITION_AT_END = -1; @@ -102,9 +103,6 @@ public interface QSHost extends PanelInteractor { void removeTileByUser(ComponentName tile); void changeTilesByUser(List<String> previousTiles, List<String> newTiles); - boolean isTileAdded(ComponentName componentName, int userId); - void setTileAdded(ComponentName componentName, int userId, boolean added); - int indexOf(String tileSpec); InstanceId getNewInstanceId(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt index 958fa71b1fd8..964fe7104324 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt @@ -20,6 +20,8 @@ import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.qs.QSHost import com.android.systemui.qs.QSTileHost +import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository +import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedSharedPrefsRepository import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractorImpl import dagger.Binds @@ -46,5 +48,19 @@ interface QSHostModule { qsHost } } + + @Provides + @JvmStatic + fun provideCustomTileAddedRepository( + featureFlags: FeatureFlags, + qsHost: QSHost, + customTileAddedRepository: CustomTileAddedSharedPrefsRepository + ): CustomTileAddedRepository { + return if (featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) { + customTileAddedRepository + } else { + qsHost + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java index 9f93e4926532..7a10a27f6aca 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java @@ -33,6 +33,7 @@ import androidx.annotation.VisibleForTesting; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener; +import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository; import com.android.systemui.settings.UserTracker; import java.util.List; @@ -59,6 +60,7 @@ public class TileServiceManager { private final TileLifecycleManager mStateManager; private final Handler mHandler; private final UserTracker mUserTracker; + private final CustomTileAddedRepository mCustomTileAddedRepository; private boolean mBindRequested; private boolean mBindAllowed; private boolean mBound; @@ -72,9 +74,10 @@ public class TileServiceManager { private boolean mStarted = false; TileServiceManager(TileServices tileServices, Handler handler, ComponentName component, - BroadcastDispatcher broadcastDispatcher, UserTracker userTracker) { - this(tileServices, handler, userTracker, new TileLifecycleManager(handler, - tileServices.getContext(), tileServices, + BroadcastDispatcher broadcastDispatcher, UserTracker userTracker, + CustomTileAddedRepository customTileAddedRepository) { + this(tileServices, handler, userTracker, customTileAddedRepository, + new TileLifecycleManager(handler, tileServices.getContext(), tileServices, new PackageManagerAdapter(tileServices.getContext()), broadcastDispatcher, new Intent(TileService.ACTION_QS_TILE).setComponent(component), userTracker.getUserHandle())); @@ -82,11 +85,13 @@ public class TileServiceManager { @VisibleForTesting TileServiceManager(TileServices tileServices, Handler handler, UserTracker userTracker, + CustomTileAddedRepository customTileAddedRepository, TileLifecycleManager tileLifecycleManager) { mServices = tileServices; mHandler = handler; mStateManager = tileLifecycleManager; mUserTracker = userTracker; + mCustomTileAddedRepository = customTileAddedRepository; IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); @@ -111,8 +116,8 @@ public class TileServiceManager { mStarted = true; ComponentName component = mStateManager.getComponent(); final int userId = mStateManager.getUserId(); - if (!mServices.getHost().isTileAdded(component, userId)) { - mServices.getHost().setTileAdded(component, userId, true); + if (!mCustomTileAddedRepository.isTileAdded(component, userId)) { + mCustomTileAddedRepository.setTileAdded(component, userId, true); mStateManager.onTileAdded(); mStateManager.flushMessagesAndUnbind(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java index 42536fef17aa..121955cced1a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java @@ -41,6 +41,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository; import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.CommandQueue; @@ -77,6 +78,7 @@ public class TileServices extends IQSService.Stub { private final UserTracker mUserTracker; private final StatusBarIconController mStatusBarIconController; private final PanelInteractor mPanelInteractor; + private final CustomTileAddedRepository mCustomTileAddedRepository; private int mMaxBound = DEFAULT_MAX_BOUND; @@ -89,7 +91,8 @@ public class TileServices extends IQSService.Stub { KeyguardStateController keyguardStateController, CommandQueue commandQueue, StatusBarIconController statusBarIconController, - PanelInteractor panelInteractor) { + PanelInteractor panelInteractor, + CustomTileAddedRepository customTileAddedRepository) { mHost = host; mKeyguardStateController = keyguardStateController; mContext = mHost.getContext(); @@ -101,6 +104,7 @@ public class TileServices extends IQSService.Stub { mStatusBarIconController = statusBarIconController; mCommandQueue.addCallback(mRequestListeningCallback); mPanelInteractor = panelInteractor; + mCustomTileAddedRepository = customTileAddedRepository; } public Context getContext() { @@ -128,7 +132,7 @@ public class TileServices extends IQSService.Stub { protected TileServiceManager onCreateTileService(ComponentName component, BroadcastDispatcher broadcastDispatcher) { return new TileServiceManager(this, mHandlerProvider.get(), component, - broadcastDispatcher, mUserTracker); + broadcastDispatcher, mUserTracker, mCustomTileAddedRepository); } public void freeService(CustomTile tile, TileServiceManager service) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedRepository.kt new file mode 100644 index 000000000000..7fc906b04faf --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedRepository.kt @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2023 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.qs.pipeline.data.repository + +import android.content.ComponentName +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.settings.UserFileManager +import javax.inject.Inject + +/** + * Repository for keeping track of whether a given [CustomTile] [ComponentName] has been added to + * the set of current tiles for a user. This is used to determine when lifecycle methods in + * `TileService` about the tile being added/removed need to be called. + */ +interface CustomTileAddedRepository { + /** + * Check if a particular [CustomTile] associated with [componentName] has been added for + * [userId] and has not been removed since. + */ + fun isTileAdded(componentName: ComponentName, userId: Int): Boolean + + /** + * Persists whether a particular [CustomTile] associated with [componentName] has been added and + * it's currently in the set of selected tiles for [userId]. + */ + fun setTileAdded(componentName: ComponentName, userId: Int, added: Boolean) +} + +@SysUISingleton +class CustomTileAddedSharedPrefsRepository +@Inject +constructor(private val userFileManager: UserFileManager) : CustomTileAddedRepository { + + override fun isTileAdded(componentName: ComponentName, userId: Int): Boolean { + return userFileManager + .getSharedPreferences(TILES, 0, userId) + .getBoolean(componentName.flattenToString(), false) + } + + override fun setTileAdded(componentName: ComponentName, userId: Int, added: Boolean) { + userFileManager + .getSharedPreferences(TILES, 0, userId) + .edit() + .putBoolean(componentName.flattenToString(), added) + .apply() + } + + companion object { + private const val TILES = "tiles_prefs" + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java index 46af89e00db4..9ca7a8521e95 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java @@ -40,6 +40,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.systemui.SysuiTestCase; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository; import com.android.systemui.settings.UserTracker; import org.junit.After; @@ -64,6 +65,8 @@ public class TileServiceManagerTest extends SysuiTestCase { private QSHost mQSHost; @Mock private Context mMockContext; + @Mock + private CustomTileAddedRepository mCustomTileAddedRepository; private HandlerThread mThread; private Handler mHandler; @@ -86,8 +89,9 @@ public class TileServiceManagerTest extends SysuiTestCase { mComponentName = new ComponentName(mContext, TileServiceManagerTest.class); when(mTileLifecycle.getComponent()).thenReturn(mComponentName); + mTileServiceManager = new TileServiceManager(mTileServices, mHandler, mUserTracker, - mTileLifecycle); + mCustomTileAddedRepository, mTileLifecycle); } @After @@ -98,28 +102,34 @@ public class TileServiceManagerTest extends SysuiTestCase { @Test public void testSetTileAddedIfNotAdded() { - when(mQSHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(false); + when(mCustomTileAddedRepository.isTileAdded(eq(mComponentName), anyInt())) + .thenReturn(false); mTileServiceManager.startLifecycleManagerAndAddTile(); - verify(mQSHost).setTileAdded(mComponentName, mUserTracker.getUserId(), true); + verify(mCustomTileAddedRepository) + .setTileAdded(mComponentName, mUserTracker.getUserId(), true); } @Test public void testNotSetTileAddedIfAdded() { - when(mQSHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(true); + when(mCustomTileAddedRepository.isTileAdded(eq(mComponentName), anyInt())) + .thenReturn(true); mTileServiceManager.startLifecycleManagerAndAddTile(); - verify(mQSHost, never()).setTileAdded(eq(mComponentName), anyInt(), eq(true)); + verify(mCustomTileAddedRepository, never()) + .setTileAdded(eq(mComponentName), anyInt(), eq(true)); } @Test public void testSetTileAddedCorrectUser() { int user = 10; when(mUserTracker.getUserId()).thenReturn(user); - when(mQSHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(false); + when(mCustomTileAddedRepository.isTileAdded(eq(mComponentName), anyInt())) + .thenReturn(false); mTileServiceManager.startLifecycleManagerAndAddTile(); - verify(mQSHost).setTileAdded(mComponentName, user, true); + verify(mCustomTileAddedRepository) + .setTileAdded(mComponentName, user, true); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java index fb9336734d99..12b5656725eb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java @@ -42,6 +42,7 @@ import android.testing.TestableLooper.RunWithLooper; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository; import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.CommandQueue; @@ -98,6 +99,8 @@ public class TileServicesTest extends SysuiTestCase { private PanelInteractor mPanelInteractor; @Captor private ArgumentCaptor<CommandQueue.Callbacks> mCallbacksArgumentCaptor; + @Mock + private CustomTileAddedRepository mCustomTileAddedRepository; @Before public void setUp() throws Exception { @@ -115,7 +118,7 @@ public class TileServicesTest extends SysuiTestCase { mTileService = new TestTileServices(mQSHost, provider, mBroadcastDispatcher, mUserTracker, mKeyguardStateController, mCommandQueue, mStatusBarIconController, - mPanelInteractor); + mPanelInteractor, mCustomTileAddedRepository); } @After @@ -293,9 +296,11 @@ public class TileServicesTest extends SysuiTestCase { TestTileServices(QSHost host, Provider<Handler> handlerProvider, BroadcastDispatcher broadcastDispatcher, UserTracker userTracker, KeyguardStateController keyguardStateController, CommandQueue commandQueue, - StatusBarIconController statusBarIconController, PanelInteractor panelInteractor) { + StatusBarIconController statusBarIconController, PanelInteractor panelInteractor, + CustomTileAddedRepository customTileAddedRepository) { super(host, handlerProvider, broadcastDispatcher, userTracker, keyguardStateController, - commandQueue, statusBarIconController, panelInteractor); + commandQueue, statusBarIconController, panelInteractor, + customTileAddedRepository); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt new file mode 100644 index 000000000000..d7ab903c1e2e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2023 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.qs.pipeline.data.repository + +import android.content.ComponentName +import android.content.SharedPreferences +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.settings.UserFileManager +import com.android.systemui.util.FakeSharedPreferences +import com.google.common.truth.Truth.assertThat +import java.io.File +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class CustomTileAddedSharedPreferencesRepositoryTest : SysuiTestCase() { + + private lateinit var underTest: CustomTileAddedSharedPrefsRepository + + @Test + fun setTileAdded_inSharedPreferences() { + val userId = 0 + val sharedPrefs = FakeSharedPreferences() + val userFileManager = FakeUserFileManager(mapOf(userId to sharedPrefs)) + + underTest = CustomTileAddedSharedPrefsRepository(userFileManager) + + underTest.setTileAdded(TEST_COMPONENT, userId, added = true) + assertThat(sharedPrefs.getForComponentName(TEST_COMPONENT)).isTrue() + + underTest.setTileAdded(TEST_COMPONENT, userId, added = false) + assertThat(sharedPrefs.getForComponentName(TEST_COMPONENT)).isFalse() + } + + @Test + fun setTileAdded_differentComponents() { + val userId = 0 + val sharedPrefs = FakeSharedPreferences() + val userFileManager = FakeUserFileManager(mapOf(userId to sharedPrefs)) + + underTest = CustomTileAddedSharedPrefsRepository(userFileManager) + + underTest.setTileAdded(TEST_COMPONENT, userId, added = true) + + assertThat(sharedPrefs.getForComponentName(TEST_COMPONENT)).isTrue() + assertThat(sharedPrefs.getForComponentName(OTHER_TEST_COMPONENT)).isFalse() + } + + @Test + fun setTileAdded_differentUsers() { + val sharedPrefs0 = FakeSharedPreferences() + val sharedPrefs1 = FakeSharedPreferences() + val userFileManager = FakeUserFileManager(mapOf(0 to sharedPrefs0, 1 to sharedPrefs1)) + + underTest = CustomTileAddedSharedPrefsRepository(userFileManager) + + underTest.setTileAdded(TEST_COMPONENT, userId = 1, added = true) + + assertThat(sharedPrefs0.getForComponentName(TEST_COMPONENT)).isFalse() + assertThat(sharedPrefs1.getForComponentName(TEST_COMPONENT)).isTrue() + } + + @Test + fun isTileAdded_fromSharedPreferences() { + val userId = 0 + val sharedPrefs = FakeSharedPreferences() + val userFileManager = FakeUserFileManager(mapOf(userId to sharedPrefs)) + + underTest = CustomTileAddedSharedPrefsRepository(userFileManager) + + assertThat(underTest.isTileAdded(TEST_COMPONENT, userId)).isFalse() + + sharedPrefs.setForComponentName(TEST_COMPONENT, true) + assertThat(underTest.isTileAdded(TEST_COMPONENT, userId)).isTrue() + + sharedPrefs.setForComponentName(TEST_COMPONENT, false) + assertThat(underTest.isTileAdded(TEST_COMPONENT, userId)).isFalse() + } + + @Test + fun isTileAdded_differentComponents() { + val userId = 0 + val sharedPrefs = FakeSharedPreferences() + val userFileManager = FakeUserFileManager(mapOf(userId to sharedPrefs)) + + underTest = CustomTileAddedSharedPrefsRepository(userFileManager) + + sharedPrefs.setForComponentName(OTHER_TEST_COMPONENT, true) + + assertThat(underTest.isTileAdded(TEST_COMPONENT, userId)).isFalse() + assertThat(underTest.isTileAdded(OTHER_TEST_COMPONENT, userId)).isTrue() + } + + @Test + fun isTileAdded_differentUsers() { + val sharedPrefs0 = FakeSharedPreferences() + val sharedPrefs1 = FakeSharedPreferences() + val userFileManager = FakeUserFileManager(mapOf(0 to sharedPrefs0, 1 to sharedPrefs1)) + + underTest = CustomTileAddedSharedPrefsRepository(userFileManager) + + sharedPrefs1.setForComponentName(TEST_COMPONENT, true) + + assertThat(underTest.isTileAdded(TEST_COMPONENT, userId = 0)).isFalse() + assertThat(underTest.isTileAdded(TEST_COMPONENT, userId = 1)).isTrue() + } + + private fun SharedPreferences.getForComponentName(componentName: ComponentName): Boolean { + return getBoolean(componentName.flattenToString(), false) + } + + private fun SharedPreferences.setForComponentName( + componentName: ComponentName, + value: Boolean + ) { + edit().putBoolean(componentName.flattenToString(), value).commit() + } + + companion object { + private val TEST_COMPONENT = ComponentName("pkg", "cls") + private val OTHER_TEST_COMPONENT = ComponentName("pkg", "other") + } +} + +private const val FILE_NAME = "tiles_prefs" + +private class FakeUserFileManager(private val sharedPrefs: Map<Int, SharedPreferences>) : + UserFileManager { + override fun getFile(fileName: String, userId: Int): File { + throw UnsupportedOperationException() + } + + override fun getSharedPreferences(fileName: String, mode: Int, userId: Int): SharedPreferences { + if (fileName != FILE_NAME) { + throw IllegalArgumentException("Preference files must be $FILE_NAME") + } + return sharedPrefs.getValue(userId) + } +} |