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 18 19 import android.content.ComponentName 20 import android.content.ContentValues 21 import android.database.sqlite.SQLiteDatabase 22 import android.os.Process.myUserHandle 23 import android.os.UserHandle 24 import android.util.Xml 25 import androidx.test.ext.junit.runners.AndroidJUnit4 26 import androidx.test.filters.SmallTest 27 import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback 28 import com.android.launcher3.AutoInstallsLayout.SourceResources 29 import com.android.launcher3.AutoInstallsLayout.TAG_WORKSPACE 30 import com.android.launcher3.AutoInstallsLayout.USER_TYPE_WORK 31 import com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_PROVIDER 32 import com.android.launcher3.LauncherSettings.Favorites.CONTAINER 33 import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP 34 import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT 35 import com.android.launcher3.LauncherSettings.Favorites.INTENT 36 import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE 37 import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION 38 import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET 39 import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER 40 import com.android.launcher3.LauncherSettings.Favorites.PROFILE_ID 41 import com.android.launcher3.LauncherSettings.Favorites.SPANX 42 import com.android.launcher3.LauncherSettings.Favorites.SPANY 43 import com.android.launcher3.LauncherSettings.Favorites._ID 44 import com.android.launcher3.dagger.LauncherAppComponent 45 import com.android.launcher3.dagger.LauncherAppSingleton 46 import com.android.launcher3.model.data.AppInfo 47 import com.android.launcher3.pm.UserCache 48 import com.android.launcher3.util.AllModulesMinusApiWrapper 49 import com.android.launcher3.util.ApiWrapper 50 import com.android.launcher3.util.Executors 51 import com.android.launcher3.util.LauncherLayoutBuilder 52 import com.android.launcher3.util.LauncherModelHelper 53 import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext 54 import com.android.launcher3.util.TestUtil 55 import com.android.launcher3.util.UserIconInfo 56 import com.android.launcher3.util.UserIconInfo.TYPE_MAIN 57 import com.android.launcher3.util.UserIconInfo.TYPE_WORK 58 import com.android.launcher3.widget.LauncherWidgetHolder 59 import com.google.common.truth.Truth.assertThat 60 import dagger.BindsInstance 61 import dagger.Component 62 import java.io.StringReader 63 import org.junit.After 64 import org.junit.Before 65 import org.junit.Test 66 import org.junit.runner.RunWith 67 import org.mockito.Mock 68 import org.mockito.MockitoAnnotations 69 import org.mockito.kotlin.doReturn 70 import org.mockito.kotlin.mock 71 import org.mockito.kotlin.whenever 72 73 /** Tests for [AutoInstallsLayout] */ 74 @SmallTest 75 @RunWith(AndroidJUnit4::class) 76 class AutoInstallsLayoutTest { 77 78 lateinit var modelHelper: LauncherModelHelper 79 lateinit var targetContext: SandboxModelContext 80 81 lateinit var callback: MyCallback 82 83 @Mock lateinit var widgetHolder: LauncherWidgetHolder 84 @Mock lateinit var db: SQLiteDatabase 85 86 @Before setupnull87 fun setup() { 88 MockitoAnnotations.initMocks(this) 89 modelHelper = LauncherModelHelper() 90 targetContext = modelHelper.sandboxContext 91 callback = MyCallback() 92 } 93 94 @After tearDownnull95 fun tearDown() { 96 modelHelper.destroy() 97 } 98 99 @Test pending_icon_added_on_homenull100 fun pending_icon_added_on_home() { 101 LauncherLayoutBuilder() 102 .atWorkspace(1, 1, 0) 103 .putApp("p1", "c1") 104 .toAutoInstallsLayout() 105 .loadLayout(db) 106 107 assertThat(callback.items.size).isEqualTo(1) 108 assertThat(callback.items[0][ITEM_TYPE]).isEqualTo(ITEM_TYPE_APPLICATION) 109 assertThat(callback.items[0][INTENT]) 110 .isEqualTo(AppInfo.makeLaunchIntent(ComponentName("p1", "c1")).toUri(0)) 111 assertThat(callback.items[0][CONTAINER]).isEqualTo(CONTAINER_DESKTOP) 112 assertThat(callback.items[0].containsKey(PROFILE_ID)).isFalse() 113 } 114 115 @Test pending_icon_added_on_hotseatnull116 fun pending_icon_added_on_hotseat() { 117 LauncherLayoutBuilder() 118 .atHotseat(1) 119 .putApp("p1", "c1") 120 .toAutoInstallsLayout() 121 .loadLayout(db) 122 123 assertThat(callback.items.size).isEqualTo(1) 124 assertThat(callback.items[0][ITEM_TYPE]).isEqualTo(ITEM_TYPE_APPLICATION) 125 assertThat(callback.items[0][CONTAINER]).isEqualTo(CONTAINER_HOTSEAT) 126 } 127 128 @Test widget_added_to_homenull129 fun widget_added_to_home() { 130 LauncherLayoutBuilder() 131 .atWorkspace(1, 1, 0) 132 .putWidget("p1", "c1", 2, 3) 133 .toAutoInstallsLayout() 134 .loadLayout(db) 135 136 assertThat(callback.items.size).isEqualTo(1) 137 assertThat(callback.items[0][ITEM_TYPE]).isEqualTo(ITEM_TYPE_APPWIDGET) 138 assertThat(callback.items[0][CONTAINER]).isEqualTo(CONTAINER_DESKTOP) 139 assertThat(callback.items[0][APPWIDGET_PROVIDER]) 140 .isEqualTo(ComponentName("p1", "c1").flattenToString()) 141 assertThat(callback.items[0][SPANX]).isEqualTo(2.toString()) 142 assertThat(callback.items[0][SPANY]).isEqualTo(3.toString()) 143 } 144 145 @Test items_added_to_foldernull146 fun items_added_to_folder() { 147 LauncherLayoutBuilder() 148 .atHotseat(1) 149 .putFolder("Test") 150 .addApp("p1", "c") 151 .addApp("p2", "c") 152 .addApp("p3", "c") 153 .build() 154 .toAutoInstallsLayout() 155 .loadLayout(db) 156 157 assertThat(callback.items.size).isEqualTo(4) 158 assertThat(callback.items[0][ITEM_TYPE]).isEqualTo(ITEM_TYPE_FOLDER) 159 assertThat(callback.items[0][CONTAINER]).isEqualTo(CONTAINER_HOTSEAT) 160 161 val folderId = callback.items[0][_ID] 162 assertThat(callback.items[1][CONTAINER]).isEqualTo(folderId) 163 assertThat(callback.items[2][CONTAINER]).isEqualTo(folderId) 164 assertThat(callback.items[3][CONTAINER]).isEqualTo(folderId) 165 } 166 167 @Test work_item_added_to_homenull168 fun work_item_added_to_home() { 169 val original = ApiWrapper.INSTANCE[targetContext] 170 val apiWrapperMock = 171 mock<MyApiWrapper>(defaultAnswer = { it.method.invoke(original, *it.arguments) }) 172 targetContext.initDaggerComponent( 173 DaggerAutoInstallsLayoutTestComponent.builder().bindApiWrapper(apiWrapperMock) 174 ) 175 doReturn( 176 mapOf( 177 myUserHandle() to UserIconInfo(myUserHandle(), TYPE_MAIN, 0), 178 UserHandle.of(20) to UserIconInfo(UserHandle.of(20), TYPE_WORK, 20), 179 ) 180 ) 181 .whenever(apiWrapperMock) 182 .queryAllUsers() 183 184 val cache = UserCache.getInstance(targetContext) 185 TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) { 186 assertThat(cache.userProfiles.size).isEqualTo(2) 187 } 188 189 LauncherLayoutBuilder() 190 .atWorkspace(1, 1, 0) 191 .putApp("p1", "c1", USER_TYPE_WORK) 192 .toAutoInstallsLayout() 193 .loadLayout(db) 194 195 assertThat(callback.items.size).isEqualTo(1) 196 assertThat(callback.items[0][ITEM_TYPE]).isEqualTo(ITEM_TYPE_APPLICATION) 197 assertThat(callback.items[0][INTENT]) 198 .isEqualTo(AppInfo.makeLaunchIntent(ComponentName("p1", "c1")).toUri(0)) 199 assertThat(callback.items[0][CONTAINER]).isEqualTo(CONTAINER_DESKTOP) 200 assertThat(callback.items[0][PROFILE_ID]).isEqualTo(20) 201 } 202 LauncherLayoutBuildernull203 private fun LauncherLayoutBuilder.toAutoInstallsLayout() = 204 AutoInstallsLayout( 205 targetContext, 206 widgetHolder, 207 callback, 208 SourceResources.wrap(targetContext.resources), 209 { Xml.newPullParser().also { it.setInput(StringReader(build())) } }, 210 TAG_WORKSPACE, 211 ) 212 213 class MyCallback : LayoutParserCallback { 214 215 val items = ArrayList<ContentValues>() 216 generateNewItemIdnull217 override fun generateNewItemId() = items.size 218 219 override fun insertAndCheck(db: SQLiteDatabase?, values: ContentValues): Int { 220 val id = values[_ID] 221 items.add(ContentValues(values)) 222 return if (id is Int) id else 0 223 } 224 } 225 } 226 227 class MyApiWrapper : ApiWrapper(null) 228 229 @LauncherAppSingleton 230 @Component(modules = [AllModulesMinusApiWrapper::class]) 231 interface AutoInstallsLayoutTestComponent : LauncherAppComponent { 232 233 @Component.Builder 234 interface Builder : LauncherAppComponent.Builder { bindApiWrappernull235 @BindsInstance fun bindApiWrapper(wrapper: ApiWrapper): Builder 236 237 override fun build(): AutoInstallsLayoutTestComponent 238 } 239 } 240