1 /* 2 * Copyright (C) 2023 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 package com.android.launcher3.model 17 18 import androidx.test.ext.junit.runners.AndroidJUnit4 19 import androidx.test.filters.SmallTest 20 import com.android.launcher3.LauncherAppState 21 import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER 22 import com.android.launcher3.icons.BitmapInfo 23 import com.android.launcher3.icons.waitForUpdateHandlerToFinish 24 import com.android.launcher3.model.data.FolderInfo 25 import com.android.launcher3.model.data.WorkspaceItemInfo 26 import com.android.launcher3.util.Executors 27 import com.android.launcher3.util.LauncherLayoutBuilder 28 import com.android.launcher3.util.LauncherModelHelper 29 import com.android.launcher3.util.LauncherModelHelper.* 30 import com.android.launcher3.util.TestUtil 31 import com.google.common.truth.Truth.assertThat 32 import com.google.common.truth.Truth.assertWithMessage 33 import org.junit.After 34 import org.junit.Before 35 import org.junit.Test 36 import org.junit.runner.RunWith 37 38 /** Tests to verify that folder icons are loaded with appropriate resolution */ 39 @SmallTest 40 @RunWith(AndroidJUnit4::class) 41 class FolderIconLoadTest { 42 43 private lateinit var modelHelper: LauncherModelHelper 44 45 private val uniqueActivities = 46 listOf( 47 TEST_ACTIVITY, 48 TEST_ACTIVITY2, 49 TEST_ACTIVITY3, 50 TEST_ACTIVITY4, 51 TEST_ACTIVITY5, 52 TEST_ACTIVITY6, 53 TEST_ACTIVITY7, 54 TEST_ACTIVITY8, 55 TEST_ACTIVITY9, 56 TEST_ACTIVITY10, 57 TEST_ACTIVITY11, 58 TEST_ACTIVITY12, 59 TEST_ACTIVITY13, 60 TEST_ACTIVITY14, 61 ) 62 63 @Before setUpnull64 fun setUp() { 65 modelHelper = LauncherModelHelper() 66 } 67 68 @After 69 @Throws(Exception::class) tearDownnull70 fun tearDown() { 71 modelHelper.destroy() 72 TestUtil.uninstallDummyApp() 73 } 74 75 @Test 76 @Throws(Exception::class) folderLoadedWithHighRes_2x2null77 fun folderLoadedWithHighRes_2x2() { 78 val items = setupAndLoadFolder(4) 79 assertThat(items.size).isEqualTo(4) 80 verifyHighRes(items, 0, 1, 2, 3) 81 } 82 83 @Test 84 @Throws(Exception::class) folderLoadedWithHighRes_3x2null85 fun folderLoadedWithHighRes_3x2() { 86 val items = setupAndLoadFolder(6) 87 assertThat(items.size).isEqualTo(6) 88 verifyHighRes(items, 0, 1, 3, 4) 89 verifyLowRes(items, 2, 5) 90 } 91 92 @Test 93 @Throws(Exception::class) folderLoadedWithHighRes_max_3x3null94 fun folderLoadedWithHighRes_max_3x3() { 95 val idp = LauncherAppState.getIDP(modelHelper.sandboxContext) 96 idp.numFolderColumns = intArrayOf(3, 3, 3, 3) 97 idp.numFolderRows = intArrayOf(3, 3, 3, 3) 98 recreateSupportedDeviceProfiles() 99 100 val items = setupAndLoadFolder(14) 101 verifyHighRes(items, 0, 1, 3, 4) 102 verifyLowRes(items, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13) 103 } 104 105 @Test 106 @Throws(Exception::class) folderLoadedWithHighRes_max_4x4null107 fun folderLoadedWithHighRes_max_4x4() { 108 val idp = LauncherAppState.getIDP(modelHelper.sandboxContext) 109 idp.numFolderColumns = intArrayOf(4, 4, 4, 4) 110 idp.numFolderRows = intArrayOf(4, 4, 4, 4) 111 recreateSupportedDeviceProfiles() 112 113 val items = setupAndLoadFolder(14) 114 verifyHighRes(items, 0, 1, 4, 5) 115 verifyLowRes(items, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13) 116 } 117 118 @Test 119 @Throws(Exception::class) folderLoadedWithHighRes_differentFolderConfigurationsnull120 fun folderLoadedWithHighRes_differentFolderConfigurations() { 121 val idp = LauncherAppState.getIDP(modelHelper.sandboxContext) 122 idp.numFolderColumns = intArrayOf(4, 3, 4, 4) 123 idp.numFolderRows = intArrayOf(4, 3, 4, 4) 124 recreateSupportedDeviceProfiles() 125 126 val items = setupAndLoadFolder(14) 127 verifyHighRes(items, 0, 1, 3, 4, 5) 128 verifyLowRes(items, 2, 6, 7, 8, 9, 10, 11, 12, 13) 129 } 130 131 @Throws(Exception::class) setupAndLoadFoldernull132 private fun setupAndLoadFolder(itemCount: Int): ArrayList<WorkspaceItemInfo> { 133 val builder = 134 LauncherLayoutBuilder() 135 .atWorkspace(0, 0, 1) 136 .putFolder("Sample") 137 .apply { 138 for (i in 0..itemCount - 1) this.addApp(TEST_PACKAGE, uniqueActivities[i]) 139 } 140 .build() 141 142 modelHelper.setupDefaultLayoutProvider(builder) 143 modelHelper.loadModelSync() 144 145 // The first load initializes the DB, load again so that icons are now used from the DB 146 // Wait for the icon cache to be updated and then reload 147 val app = LauncherAppState.getInstance(modelHelper.sandboxContext) 148 app.iconCache.waitForUpdateHandlerToFinish() 149 150 TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) { app.iconCache.clearMemoryCache() } 151 // Reload again with correct icon state 152 app.model.forceReload() 153 modelHelper.loadModelSync() 154 val collections = 155 modelHelper.bgDataModel.itemsIdMap 156 .filter { it.itemType == ITEM_TYPE_FOLDER } 157 .map { it as FolderInfo } 158 assertThat(collections.size).isEqualTo(1) 159 assertThat(collections[0].getAppContents().size).isEqualTo(itemCount) 160 return collections[0].getAppContents() 161 } 162 verifyHighResnull163 private fun verifyHighRes(items: ArrayList<WorkspaceItemInfo>, vararg indices: Int) { 164 for (index in indices) { 165 assertWithMessage("Index $index was not highRes") 166 .that(items[index].bitmap.isNullOrLowRes) 167 .isFalse() 168 assertWithMessage("Index $index was the default icon") 169 .that(isDefaultIcon(items[index].bitmap)) 170 .isFalse() 171 } 172 } 173 verifyLowResnull174 private fun verifyLowRes(items: ArrayList<WorkspaceItemInfo>, vararg indices: Int) { 175 for (index in indices) { 176 assertWithMessage("Index $index was not lowRes") 177 .that(items[index].bitmap.isNullOrLowRes) 178 .isTrue() 179 assertWithMessage("Index $index was the default icon") 180 .that(isDefaultIcon(items[index].bitmap)) 181 .isFalse() 182 } 183 } 184 isDefaultIconnull185 private fun isDefaultIcon(bitmap: BitmapInfo) = 186 LauncherAppState.getInstance(modelHelper.sandboxContext) 187 .iconCache 188 .isDefaultIcon(bitmap, modelHelper.sandboxContext.user) 189 190 /** Recreate DeviceProfiles after changing InvariantDeviceProfile */ 191 private fun recreateSupportedDeviceProfiles() { 192 LauncherAppState.getIDP(modelHelper.sandboxContext).supportedProfiles = 193 LauncherAppState.getIDP(modelHelper.sandboxContext).supportedProfiles.map { 194 it.copy(modelHelper.sandboxContext) 195 } 196 } 197 } 198