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.model.data.WorkspaceItemInfo 22 import com.android.launcher3.util.Executors 23 import com.android.launcher3.util.LauncherLayoutBuilder 24 import com.android.launcher3.util.LauncherModelHelper 25 import com.android.launcher3.util.LauncherModelHelper.* 26 import com.android.launcher3.util.TestUtil 27 import com.google.common.truth.Truth.assertThat 28 import com.google.common.truth.Truth.assertWithMessage 29 import java.util.concurrent.CountDownLatch 30 import org.junit.After 31 import org.junit.Before 32 import org.junit.Test 33 import org.junit.runner.RunWith 34 35 /** Tests to verify that folder icons are loaded with appropriate resolution */ 36 @SmallTest 37 @RunWith(AndroidJUnit4::class) 38 class FolderIconLoadTest { 39 private lateinit var modelHelper: LauncherModelHelper 40 41 private val uniqueActivities = 42 listOf( 43 TEST_ACTIVITY, 44 TEST_ACTIVITY2, 45 TEST_ACTIVITY3, 46 TEST_ACTIVITY4, 47 TEST_ACTIVITY5, 48 TEST_ACTIVITY6, 49 TEST_ACTIVITY7, 50 TEST_ACTIVITY8, 51 TEST_ACTIVITY9, 52 TEST_ACTIVITY10, 53 TEST_ACTIVITY11, 54 TEST_ACTIVITY12, 55 TEST_ACTIVITY13, 56 TEST_ACTIVITY14 57 ) 58 59 @Before setUpnull60 fun setUp() { 61 modelHelper = LauncherModelHelper() 62 } 63 64 @After 65 @Throws(Exception::class) tearDownnull66 fun tearDown() { 67 modelHelper.destroy() 68 TestUtil.uninstallDummyApp() 69 } 70 71 @Test 72 @Throws(Exception::class) folderLoadedWithHighRes_2x2null73 fun folderLoadedWithHighRes_2x2() { 74 val items = setupAndLoadFolder(4) 75 assertThat(items.size).isEqualTo(4) 76 verifyHighRes(items, 0, 1, 2, 3) 77 } 78 79 @Test 80 @Throws(Exception::class) folderLoadedWithHighRes_3x2null81 fun folderLoadedWithHighRes_3x2() { 82 val items = setupAndLoadFolder(6) 83 assertThat(items.size).isEqualTo(6) 84 verifyHighRes(items, 0, 1, 3, 4) 85 verifyLowRes(items, 2, 5) 86 } 87 88 @Test 89 @Throws(Exception::class) folderLoadedWithHighRes_max_3x3null90 fun folderLoadedWithHighRes_max_3x3() { 91 val idp = LauncherAppState.getIDP(modelHelper.sandboxContext) 92 idp.numFolderColumns = intArrayOf(3, 3, 3, 3) 93 idp.numFolderRows = intArrayOf(3, 3, 3, 3) 94 recreateSupportedDeviceProfiles() 95 96 val items = setupAndLoadFolder(14) 97 verifyHighRes(items, 0, 1, 3, 4) 98 verifyLowRes(items, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13) 99 } 100 101 @Test 102 @Throws(Exception::class) folderLoadedWithHighRes_max_4x4null103 fun folderLoadedWithHighRes_max_4x4() { 104 val idp = LauncherAppState.getIDP(modelHelper.sandboxContext) 105 idp.numFolderColumns = intArrayOf(4, 4, 4, 4) 106 idp.numFolderRows = intArrayOf(4, 4, 4, 4) 107 recreateSupportedDeviceProfiles() 108 109 val items = setupAndLoadFolder(14) 110 verifyHighRes(items, 0, 1, 4, 5) 111 verifyLowRes(items, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13) 112 } 113 114 @Test 115 @Throws(Exception::class) folderLoadedWithHighRes_differentFolderConfigurationsnull116 fun folderLoadedWithHighRes_differentFolderConfigurations() { 117 val idp = LauncherAppState.getIDP(modelHelper.sandboxContext) 118 idp.numFolderColumns = intArrayOf(4, 3, 4, 4) 119 idp.numFolderRows = intArrayOf(4, 3, 4, 4) 120 recreateSupportedDeviceProfiles() 121 122 val items = setupAndLoadFolder(14) 123 verifyHighRes(items, 0, 1, 3, 4, 5) 124 verifyLowRes(items, 2, 6, 7, 8, 9, 10, 11, 12, 13) 125 } 126 127 @Throws(Exception::class) setupAndLoadFoldernull128 private fun setupAndLoadFolder(itemCount: Int): ArrayList<WorkspaceItemInfo> { 129 val builder = 130 LauncherLayoutBuilder() 131 .atWorkspace(0, 0, 1) 132 .putFolder("Sample") 133 .apply { 134 for (i in 0..itemCount - 1) this.addApp(TEST_PACKAGE, uniqueActivities[i]) 135 } 136 .build() 137 138 modelHelper.setupDefaultLayoutProvider(builder) 139 modelHelper.loadModelSync() 140 141 // The first load initializes the DB, load again so that icons are now used from the DB 142 // Wait for the icon cache to be updated and then reload 143 val app = LauncherAppState.getInstance(modelHelper.sandboxContext) 144 val cache = app.iconCache 145 while (cache.isIconUpdateInProgress) { 146 val wait = CountDownLatch(1) 147 Executors.MODEL_EXECUTOR.handler.postDelayed({ wait.countDown() }, 10) 148 wait.await() 149 } 150 TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) { cache.clearMemoryCache() } 151 // Reload again with correct icon state 152 app.model.forceReload() 153 modelHelper.loadModelSync() 154 val collections = modelHelper.getBgDataModel().collections 155 156 assertThat(collections.size()).isEqualTo(1) 157 assertThat(collections.valueAt(0).getAppContents().size).isEqualTo(itemCount) 158 return collections.valueAt(0).getAppContents() 159 } 160 verifyHighResnull161 private fun verifyHighRes(items: ArrayList<WorkspaceItemInfo>, vararg indices: Int) { 162 for (index in indices) { 163 assertWithMessage("Index $index was not highRes") 164 .that(items[index].bitmap.isNullOrLowRes) 165 .isFalse() 166 } 167 } 168 verifyLowResnull169 private fun verifyLowRes(items: ArrayList<WorkspaceItemInfo>, vararg indices: Int) { 170 for (index in indices) { 171 assertWithMessage("Index $index was not lowRes") 172 .that(items[index].bitmap.isNullOrLowRes) 173 .isTrue() 174 } 175 } 176 177 /** Recreate DeviceProfiles after changing InvariantDeviceProfile */ recreateSupportedDeviceProfilesnull178 private fun recreateSupportedDeviceProfiles() { 179 LauncherAppState.getIDP(modelHelper.sandboxContext).supportedProfiles = 180 LauncherAppState.getIDP(modelHelper.sandboxContext).supportedProfiles.map { 181 it.copy(modelHelper.sandboxContext) 182 } 183 } 184 } 185