• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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