1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.launcher3.model 18 19 import android.content.ComponentName 20 import android.content.pm.ActivityInfo 21 import android.content.pm.ApplicationInfo 22 import android.content.pm.LauncherActivityInfo 23 import android.content.pm.LauncherApps 24 import android.os.Process.myUserHandle 25 import android.platform.test.annotations.EnableFlags 26 import android.platform.test.flag.junit.SetFlagsRule 27 import androidx.test.ext.junit.runners.AndroidJUnit4 28 import com.android.launcher3.AppFilter 29 import com.android.launcher3.Flags.FLAG_ENABLE_PRIVATE_SPACE 30 import com.android.launcher3.LauncherSettings 31 import com.android.launcher3.dagger.LauncherAppComponent 32 import com.android.launcher3.dagger.LauncherAppSingleton 33 import com.android.launcher3.icons.IconCache 34 import com.android.launcher3.model.PackageUpdatedTask.OP_ADD 35 import com.android.launcher3.model.PackageUpdatedTask.OP_REMOVE 36 import com.android.launcher3.model.PackageUpdatedTask.OP_SUSPEND 37 import com.android.launcher3.model.PackageUpdatedTask.OP_UNAVAILABLE 38 import com.android.launcher3.model.PackageUpdatedTask.OP_UNSUSPEND 39 import com.android.launcher3.model.PackageUpdatedTask.OP_UPDATE 40 import com.android.launcher3.model.PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE 41 import com.android.launcher3.model.data.AppInfo 42 import com.android.launcher3.model.data.WorkspaceItemInfo 43 import com.android.launcher3.util.AllModulesForTest 44 import com.android.launcher3.util.Executors 45 import com.android.launcher3.util.LauncherModelHelper 46 import com.android.launcher3.util.TestUtil 47 import com.google.common.truth.Truth.assertThat 48 import dagger.BindsInstance 49 import dagger.Component 50 import org.junit.After 51 import org.junit.Before 52 import org.junit.Rule 53 import org.junit.Test 54 import org.junit.runner.RunWith 55 import org.mockito.kotlin.any 56 import org.mockito.kotlin.doAnswer 57 import org.mockito.kotlin.mock 58 import org.mockito.kotlin.spy 59 import org.mockito.kotlin.verify 60 import org.mockito.kotlin.whenever 61 62 @RunWith(AndroidJUnit4::class) 63 class PackageUpdatedTaskTest { 64 65 @get:Rule val setFlagsRule = SetFlagsRule() 66 67 private val mUser = myUserHandle() 68 private val mLauncherModelHelper = LauncherModelHelper() 69 private val mContext = mLauncherModelHelper.sandboxContext 70 71 private val expectedPackage = "Test.Package" 72 private val expectedComponent = ComponentName(expectedPackage, "TestClass") 73 private val expectedActivityInfo: LauncherActivityInfo = mock<LauncherActivityInfo>() 74 private val expectedWorkspaceItem = spy(WorkspaceItemInfo()) 75 76 private val mockIconCache: IconCache = mock() 77 private val mockAppFilter: AppFilter = mock<AppFilter>() 78 private lateinit var mAllAppsList: AllAppsList 79 80 private val mockApplicationInfo: ApplicationInfo = mock<ApplicationInfo>() 81 private val mockActivityInfo: ActivityInfo = mock<ActivityInfo>() 82 83 private lateinit var mDataModel: BgDataModel 84 private lateinit var mockTaskController: ModelTaskController 85 86 @Before setupnull87 fun setup() { 88 mAllAppsList = spy(AllAppsList(mockIconCache, mockAppFilter)) 89 mContext.initDaggerComponent( 90 DaggerPackageUpdatedTaskTest_TestComponent.builder() 91 .bindAllAppsList(mAllAppsList) 92 .bindAppFilter(mockAppFilter) 93 .bindIconCache(mockIconCache) 94 ) 95 96 mContext.spyService(LauncherApps::class.java).apply { 97 whenever(getActivityList(expectedPackage, mUser)) 98 .thenReturn(listOf(expectedActivityInfo)) 99 } 100 101 mockTaskController = spy((mContext.appComponent as TestComponent).getTaskController()) 102 mDataModel = (mContext.appComponent as TestComponent).getDataModel() 103 104 whenever(mockAppFilter.shouldShowApp(expectedComponent)).thenReturn(true) 105 mockApplicationInfo.apply { 106 uid = 1 107 isArchived = false 108 } 109 mockActivityInfo.isArchived = false 110 expectedActivityInfo.apply { 111 whenever(applicationInfo).thenReturn(mockApplicationInfo) 112 whenever(activityInfo).thenReturn(mockActivityInfo) 113 whenever(componentName).thenReturn(expectedComponent) 114 } 115 expectedWorkspaceItem.apply { 116 itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION 117 container = LauncherSettings.Favorites.CONTAINER_DESKTOP 118 user = mUser 119 whenever(targetPackage).thenReturn(expectedPackage) 120 whenever(targetComponent).thenReturn(expectedComponent) 121 } 122 } 123 124 @After tearDownnull125 fun tearDown() { 126 mLauncherModelHelper.destroy() 127 } 128 129 @Test OP_ADD triggers model callbacks and adds new items to AllAppsListnull130 fun `OP_ADD triggers model callbacks and adds new items to AllAppsList`() { 131 // Given 132 val taskUnderTest = PackageUpdatedTask(OP_ADD, mUser, expectedPackage) 133 // When 134 mDataModel.addItem(mContext, expectedWorkspaceItem, true) 135 TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) { 136 taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList) 137 } 138 // Then 139 verify(mockIconCache).updateIconsForPkg(expectedPackage, mUser) 140 verify(mAllAppsList).addPackage(mContext, expectedPackage, mUser) 141 verify(mockTaskController).bindUpdatedWorkspaceItems(listOf(expectedWorkspaceItem)) 142 verify(mockTaskController).bindUpdatedWidgets(mDataModel) 143 assertThat(mAllAppsList.data.firstOrNull()?.componentName) 144 .isEqualTo(AppInfo(mContext, expectedActivityInfo, mUser).componentName) 145 } 146 147 @Test OP_UPDATE triggers model callbacks and updates items in AllAppsListnull148 fun `OP_UPDATE triggers model callbacks and updates items in AllAppsList`() { 149 // Given 150 val taskUnderTest = PackageUpdatedTask(OP_UPDATE, mUser, expectedPackage) 151 // When 152 mDataModel.addItem(mContext, expectedWorkspaceItem, true) 153 TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) { 154 taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList) 155 } 156 // Then 157 verify(mockIconCache).updateIconsForPkg(expectedPackage, mUser) 158 verify(mAllAppsList).updatePackage(mContext, expectedPackage, mUser) 159 verify(mockTaskController).bindUpdatedWorkspaceItems(listOf(expectedWorkspaceItem)) 160 assertThat(mAllAppsList.data.firstOrNull()?.componentName) 161 .isEqualTo(AppInfo(mContext, expectedActivityInfo, mUser).componentName) 162 } 163 164 @Test OP_REMOVE triggers model callbacks and removes packages and iconsnull165 fun `OP_REMOVE triggers model callbacks and removes packages and icons`() { 166 // Given 167 val taskUnderTest = PackageUpdatedTask(OP_REMOVE, mUser, expectedPackage) 168 // When 169 mDataModel.addItem(mContext, expectedWorkspaceItem, true) 170 TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) { 171 taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList) 172 } 173 // Then 174 verify(mockIconCache).removeIconsForPkg(expectedPackage, mUser) 175 verify(mAllAppsList).removePackage(expectedPackage, mUser) 176 verify(mockTaskController).bindUpdatedWorkspaceItems(listOf(expectedWorkspaceItem)) 177 assertThat(mAllAppsList.data).isEmpty() 178 } 179 180 @Test OP_UNAVAILABLE triggers model callbacks and removes package from AllAppsListnull181 fun `OP_UNAVAILABLE triggers model callbacks and removes package from AllAppsList`() { 182 // Given 183 val taskUnderTest = PackageUpdatedTask(OP_UNAVAILABLE, mUser, expectedPackage) 184 // When 185 mDataModel.addItem(mContext, expectedWorkspaceItem, true) 186 TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) { 187 taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList) 188 } 189 // Then 190 verify(mAllAppsList).removePackage(expectedPackage, mUser) 191 verify(mockTaskController).bindUpdatedWorkspaceItems(listOf(expectedWorkspaceItem)) 192 assertThat(mAllAppsList.data).isEmpty() 193 } 194 195 @Test OP_SUSPEND triggers model callbacks and updates flags in AllAppsListnull196 fun `OP_SUSPEND triggers model callbacks and updates flags in AllAppsList`() { 197 // Given 198 val taskUnderTest = PackageUpdatedTask(OP_SUSPEND, mUser, expectedPackage) 199 // When 200 mDataModel.addItem(mContext, expectedWorkspaceItem, true) 201 mAllAppsList.add(AppInfo(mContext, expectedActivityInfo, mUser), expectedActivityInfo) 202 mAllAppsList.getAndResetChangeFlag() 203 doAnswer {}.whenever(mockTaskController).bindApplicationsIfNeeded() 204 TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) { 205 taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList) 206 } 207 // Then 208 verify(mAllAppsList).updateDisabledFlags(any(), any()) 209 verify(mockTaskController).bindUpdatedWorkspaceItems(listOf(expectedWorkspaceItem)) 210 assertThat(mAllAppsList.getAndResetChangeFlag()).isTrue() 211 } 212 213 @Test OP_UNSUSPEND triggers no callbacks when app not suspendednull214 fun `OP_UNSUSPEND triggers no callbacks when app not suspended`() { 215 // Given 216 val taskUnderTest = PackageUpdatedTask(OP_UNSUSPEND, mUser, expectedPackage) 217 // When 218 mDataModel.addItem(mContext, expectedWorkspaceItem, true) 219 mAllAppsList.getAndResetChangeFlag() 220 doAnswer {}.whenever(mockTaskController).bindApplicationsIfNeeded() 221 TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) { 222 taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList) 223 } 224 // Then 225 verify(mAllAppsList).updateDisabledFlags(any(), any()) 226 verify(mockTaskController).bindUpdatedWorkspaceItems(emptyList()) 227 assertThat(mAllAppsList.getAndResetChangeFlag()).isFalse() 228 } 229 230 @EnableFlags(FLAG_ENABLE_PRIVATE_SPACE) 231 @Test OP_USER_AVAILABILITY_CHANGE triggers no callbacks if current user not work or privatenull232 fun `OP_USER_AVAILABILITY_CHANGE triggers no callbacks if current user not work or private`() { 233 // Given 234 val taskUnderTest = PackageUpdatedTask(OP_USER_AVAILABILITY_CHANGE, mUser, expectedPackage) 235 // When 236 mDataModel.addItem(mContext, expectedWorkspaceItem, true) 237 TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) { 238 taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList) 239 } 240 // Then 241 verify(mAllAppsList).updateDisabledFlags(any(), any()) 242 verify(mockTaskController).bindUpdatedWorkspaceItems(emptyList()) 243 assertThat(mAllAppsList.data).isEmpty() 244 } 245 246 @LauncherAppSingleton 247 @Component(modules = [AllModulesForTest::class]) 248 interface TestComponent : LauncherAppComponent { 249 getDataModelnull250 fun getDataModel(): BgDataModel 251 252 fun getTaskController(): ModelTaskController 253 254 @Component.Builder 255 interface Builder : LauncherAppComponent.Builder { 256 @BindsInstance fun bindAppFilter(appFilter: AppFilter): Builder 257 258 @BindsInstance fun bindIconCache(iconCache: IconCache): Builder 259 260 @BindsInstance fun bindAllAppsList(list: AllAppsList): Builder 261 262 override fun build(): TestComponent 263 } 264 } 265 } 266