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 
17 package com.android.launcher3.allapps;
18 
19 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
20 
21 import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER;
22 import static com.android.launcher3.allapps.UserProfileManager.STATE_DISABLED;
23 import static com.android.launcher3.allapps.UserProfileManager.STATE_ENABLED;
24 import static com.android.launcher3.allapps.UserProfileManager.STATE_TRANSITION;
25 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
26 
27 import static org.junit.Assert.assertEquals;
28 import static org.junit.Assert.assertTrue;
29 import static org.mockito.AdditionalAnswers.answer;
30 import static org.mockito.ArgumentMatchers.any;
31 import static org.mockito.Mockito.doAnswer;
32 import static org.mockito.Mockito.doNothing;
33 import static org.mockito.Mockito.doReturn;
34 import static org.mockito.Mockito.spy;
35 import static org.mockito.Mockito.when;
36 
37 import android.content.ComponentName;
38 import android.content.Context;
39 import android.content.Intent;
40 import android.graphics.Bitmap;
41 import android.graphics.Canvas;
42 import android.graphics.drawable.BitmapDrawable;
43 import android.graphics.drawable.Drawable;
44 import android.os.Process;
45 import android.os.UserHandle;
46 import android.os.UserManager;
47 import android.view.LayoutInflater;
48 import android.view.View;
49 import android.widget.ImageButton;
50 import android.widget.ImageView;
51 import android.widget.LinearLayout;
52 import android.widget.RelativeLayout;
53 import android.widget.TextView;
54 
55 import androidx.test.filters.SmallTest;
56 import androidx.test.runner.AndroidJUnit4;
57 
58 import com.android.launcher3.R;
59 import com.android.launcher3.logging.StatsLogManager;
60 import com.android.launcher3.model.data.AppInfo;
61 import com.android.launcher3.pm.UserCache;
62 import com.android.launcher3.util.ActivityContextWrapper;
63 import com.android.launcher3.util.UserIconInfo;
64 
65 import org.junit.Before;
66 import org.junit.Test;
67 import org.junit.runner.RunWith;
68 import org.mockito.Mock;
69 import org.mockito.MockitoAnnotations;
70 
71 import java.util.ArrayList;
72 import java.util.Arrays;
73 import java.util.List;
74 import java.util.function.Predicate;
75 
76 @SmallTest
77 @RunWith(AndroidJUnit4.class)
78 public class PrivateSpaceHeaderViewTest {
79 
80     private static final UserHandle MAIN_HANDLE = Process.myUserHandle();
81     private static final UserHandle PRIVATE_HANDLE = new UserHandle(11);
82     private static final UserIconInfo MAIN_ICON_INFO =
83             new UserIconInfo(MAIN_HANDLE, UserIconInfo.TYPE_MAIN);
84     private static final UserIconInfo PRIVATE_ICON_INFO =
85             new UserIconInfo(PRIVATE_HANDLE, UserIconInfo.TYPE_PRIVATE);
86     private static final String CAMERA_PACKAGE_NAME = "com.android.launcher3.tests.camera";
87     private static final int CONTAINER_HEADER_ELEMENT_COUNT = 1;
88     private static final int LOCK_UNLOCK_BUTTON_COUNT = 1;
89     private static final int PS_SETTINGS_BUTTON_COUNT_VISIBLE = 1;
90     private static final int PS_SETTINGS_BUTTON_COUNT_INVISIBLE = 0;
91     private static final int PS_TRANSITION_IMAGE_COUNT = 1;
92     private static final int NUM_APP_COLS = 4;
93     private static final int NUM_PRIVATE_SPACE_APPS = 50;
94     private static final int ALL_APPS_HEIGHT = 10;
95     private static final int ALL_APPS_CELL_HEIGHT = 1;
96     private static final int PS_HEADER_HEIGHT = 1;
97     private static final int BIGGER_PS_HEADER_HEIGHT = 2;
98     private static final int SCROLL_NO_WHERE = -1;
99     private static final float HEADER_PROTECTION_HEIGHT = 1F;
100 
101     private Context mContext;
102     private RelativeLayout mPsHeaderLayout;
103     private AlphabeticalAppsList<?> mAlphabeticalAppsList;
104     private PrivateProfileManager mPrivateProfileManager;
105     @Mock
106     private ActivityAllAppsContainerView mAllApps;
107     @Mock
108     private AllAppsStore<?> mAllAppsStore;
109     @Mock
110     private UserCache mUserCache;
111     @Mock
112     private UserManager mUserManager;
113     @Mock
114     private StatsLogManager mStatsLogManager;
115 
116     @Before
setUp()117     public void setUp() {
118         MockitoAnnotations.initMocks(this);
119         mContext = new ActivityContextWrapper(getApplicationContext());
120         when(mAllApps.getContext()).thenReturn(mContext);
121         when(mUserCache.getUserInfo(PRIVATE_HANDLE)).thenReturn(PRIVATE_ICON_INFO);
122         when(mUserCache.getUserProfiles())
123                 .thenReturn(Arrays.asList(MAIN_HANDLE, PRIVATE_HANDLE));
124         when(mUserCache.getUserInfo(Process.myUserHandle())).thenReturn(MAIN_ICON_INFO);
125         mPrivateProfileManager = new PrivateProfileManager(mUserManager,
126                 mAllApps, mStatsLogManager, mUserCache);
127         mPsHeaderLayout = (RelativeLayout) LayoutInflater.from(mContext).inflate(
128                 R.layout.private_space_header, null);
129     }
130 
131     @Test
privateProfileDisabled_psHeaderContainsLockedView()132     public void privateProfileDisabled_psHeaderContainsLockedView() throws Exception {
133         Bitmap unlockButton = getBitmap(mContext.getDrawable(R.drawable.ic_lock));
134         PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
135         when(privateProfileManager.getCurrentState()).thenReturn(STATE_DISABLED);
136         privateProfileManager.bindPrivateSpaceHeaderViewElements(mPsHeaderLayout);
137         awaitTasksCompleted();
138 
139         int totalContainerHeaderView = 0;
140         int totalLockUnlockButtonView = 0;
141         for (int i = 0; i < mPsHeaderLayout.getChildCount(); i++) {
142             View view = mPsHeaderLayout.getChildAt(i);
143             if (view.getId() == R.id.ps_container_header) {
144                 totalContainerHeaderView += 1;
145                 assertEquals(View.VISIBLE, view.getVisibility());
146             } else if (view.getId() == R.id.settingsAndLockGroup) {
147                 ImageView lockIcon = view.findViewById(R.id.lock_icon);
148                 assertTrue(getBitmap(lockIcon.getDrawable()).sameAs(unlockButton));
149                 assertEquals(View.VISIBLE, lockIcon.getVisibility());
150 
151                 // Verify textView shouldn't be showing when disabled.
152                 TextView lockText = view.findViewById(R.id.lock_text);
153                 assertEquals(View.GONE, lockText.getVisibility());
154                 totalLockUnlockButtonView += 1;
155             } else {
156                 assertEquals(View.GONE, view.getVisibility());
157             }
158         }
159 
160         assertEquals(CONTAINER_HEADER_ELEMENT_COUNT, totalContainerHeaderView);
161         assertEquals(LOCK_UNLOCK_BUTTON_COUNT, totalLockUnlockButtonView);
162     }
163 
164     @Test
privateProfileEnabled_psHeaderContainsUnlockedView()165     public void privateProfileEnabled_psHeaderContainsUnlockedView() throws Exception {
166         Bitmap lockImage = getBitmap(mContext.getDrawable(R.drawable.ic_lock));
167         Bitmap settingsImage = getBitmap(mContext.getDrawable(R.drawable.ic_ps_settings));
168         PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
169         when(privateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
170         when(privateProfileManager.isPrivateSpaceSettingsAvailable()).thenReturn(true);
171         privateProfileManager.bindPrivateSpaceHeaderViewElements(mPsHeaderLayout);
172         awaitTasksCompleted();
173 
174         int totalContainerHeaderView = 0;
175         int totalLockUnlockButtonView = 0;
176         int totalSettingsImageView = 0;
177         for (int i = 0; i < mPsHeaderLayout.getChildCount(); i++) {
178             View view = mPsHeaderLayout.getChildAt(i);
179             if (view.getId() == R.id.ps_container_header) {
180                 totalContainerHeaderView += 1;
181                 assertEquals(View.VISIBLE, view.getVisibility());
182             } else if (view.getId() == R.id.settingsAndLockGroup) {
183                 // Look for settings button.
184                 ImageButton settingsButton = view.findViewById(R.id.ps_settings_button);
185                 assertEquals(View.VISIBLE, settingsButton.getVisibility());
186                 totalSettingsImageView += 1;
187                 assertTrue(getBitmap(settingsButton.getDrawable()).sameAs(settingsImage));
188 
189                 // Look for lock_icon and lock_text.
190                 ImageView lockIcon = view.findViewById(R.id.lock_icon);
191                 assertTrue(getBitmap(lockIcon.getDrawable()).sameAs(lockImage));
192                 assertEquals(View.VISIBLE, lockIcon.getVisibility());
193                 TextView lockText = view.findViewById(R.id.lock_text);
194                 assertEquals(View.VISIBLE, lockText.getVisibility());
195                 totalLockUnlockButtonView += 1;
196             } else {
197                 assertEquals(View.GONE, view.getVisibility());
198             }
199         }
200 
201         assertEquals(CONTAINER_HEADER_ELEMENT_COUNT, totalContainerHeaderView);
202         assertEquals(LOCK_UNLOCK_BUTTON_COUNT, totalLockUnlockButtonView);
203         assertEquals(PS_SETTINGS_BUTTON_COUNT_VISIBLE, totalSettingsImageView);
204     }
205 
206     @Test
privateProfileEnabledAndNoSettingsIntent_psHeaderContainsUnlockedView()207     public void privateProfileEnabledAndNoSettingsIntent_psHeaderContainsUnlockedView()
208             throws Exception {
209         Bitmap lockImage = getBitmap(mContext.getDrawable(R.drawable.ic_lock));
210         PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
211         when(privateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
212         when(privateProfileManager.isPrivateSpaceSettingsAvailable()).thenReturn(false);
213         privateProfileManager.bindPrivateSpaceHeaderViewElements(mPsHeaderLayout);
214         awaitTasksCompleted();
215 
216         int totalContainerHeaderView = 0;
217         int totalLockUnlockButtonView = 0;
218         int totalSettingsImageView = 0;
219         for (int i = 0; i < mPsHeaderLayout.getChildCount(); i++) {
220             View view = mPsHeaderLayout.getChildAt(i);
221             if (view.getId() == R.id.ps_container_header) {
222                 totalContainerHeaderView += 1;
223                 assertEquals(View.VISIBLE, view.getVisibility());
224             } else if (view.getId() == R.id.settingsAndLockGroup) {
225                 // Ensure there is no settings button.
226                 ImageButton settingsImage = view.findViewById(R.id.ps_settings_button);
227                 assertEquals(View.GONE, settingsImage.getVisibility());
228 
229                 // Check lock icon and lock text is there.
230                 ImageView lockIcon = view.findViewById(R.id.lock_icon);
231                 assertTrue(getBitmap(lockIcon.getDrawable()).sameAs(lockImage));
232                 assertEquals(View.VISIBLE, lockIcon.getVisibility());
233                 TextView lockText = view.findViewById(R.id.lock_text);
234                 assertEquals(View.VISIBLE, lockText.getVisibility());
235                 totalLockUnlockButtonView += 1;
236             } else {
237                 assertEquals(View.GONE, view.getVisibility());
238             }
239         }
240 
241         assertEquals(CONTAINER_HEADER_ELEMENT_COUNT, totalContainerHeaderView);
242         assertEquals(LOCK_UNLOCK_BUTTON_COUNT, totalLockUnlockButtonView);
243         assertEquals(PS_SETTINGS_BUTTON_COUNT_INVISIBLE, totalSettingsImageView);
244     }
245 
246     @Test
privateProfileTransitioning_psHeaderContainsTransitionView()247     public void privateProfileTransitioning_psHeaderContainsTransitionView() throws Exception {
248         Bitmap transitionImage = getBitmap(mContext.getDrawable(R.drawable.bg_ps_transition_image));
249         PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
250         when(privateProfileManager.getCurrentState()).thenReturn(STATE_TRANSITION);
251         privateProfileManager.bindPrivateSpaceHeaderViewElements(mPsHeaderLayout);
252         awaitTasksCompleted();
253 
254         int totalContainerHeaderView = 0;
255         int totalLockUnlockButtonView = 0;
256         for (int i = 0; i < mPsHeaderLayout.getChildCount(); i++) {
257             View view = mPsHeaderLayout.getChildAt(i);
258             if (view.getId() == R.id.ps_container_header) {
259                 totalContainerHeaderView += 1;
260                 assertEquals(View.VISIBLE, view.getVisibility());
261             } else if (view.getId() == R.id.ps_transition_image
262                     && view instanceof ImageView imageView) {
263                 totalLockUnlockButtonView += 1;
264                 assertEquals(View.VISIBLE, view.getVisibility());
265                 assertTrue(getBitmap(imageView.getDrawable()).sameAs(transitionImage));
266             } else if (view.getId() == R.id.settingsAndLockGroup) {
267                 LinearLayout lockUnlockButton = view.findViewById(R.id.ps_lock_unlock_button);
268                 assertEquals(View.GONE, lockUnlockButton.getVisibility());
269             } else {
270                 assertEquals(View.GONE, view.getVisibility());
271             }
272         }
273 
274         assertEquals(CONTAINER_HEADER_ELEMENT_COUNT, totalContainerHeaderView);
275         assertEquals(PS_TRANSITION_IMAGE_COUNT, totalLockUnlockButtonView);
276     }
277 
278     @Test
scrollForViewToBeVisibleInContainer_withHeader()279     public void scrollForViewToBeVisibleInContainer_withHeader() {
280         when(mAllAppsStore.getApps()).thenReturn(createAppInfoList());
281         PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
282         when(privateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
283         doReturn(splitIntoUserInstalledAndSystemApps()).when(privateProfileManager)
284                 .splitIntoUserInstalledAndSystemApps(any());
285         doReturn(0).when(privateProfileManager).addPrivateSpaceHeader(any());
286         doAnswer(answer(this::addPrivateSpaceHeader)).when(privateProfileManager)
287                 .addPrivateSpaceHeader(any());
288         doNothing().when(privateProfileManager).addPrivateSpaceInstallAppButton(any());
289         doReturn(0).when(privateProfileManager).addSystemAppsDivider(any());
290         when(mAllApps.getHeight()).thenReturn(ALL_APPS_HEIGHT);
291         when(mAllApps.getHeaderProtectionHeight()).thenReturn(HEADER_PROTECTION_HEIGHT);
292         when(mAllApps.isUsingTabs()).thenReturn(true);
293         mAlphabeticalAppsList = new AlphabeticalAppsList<>(mContext, mAllAppsStore,
294                 null, privateProfileManager);
295         mAlphabeticalAppsList.setNumAppsPerRowAllApps(NUM_APP_COLS);
296         mAlphabeticalAppsList.updateItemFilter(info -> info != null
297                 && info.user.equals(MAIN_HANDLE));
298 
299         int rows = (int) (ALL_APPS_HEIGHT - PS_HEADER_HEIGHT - HEADER_PROTECTION_HEIGHT);
300         int position = rows * NUM_APP_COLS - (NUM_APP_COLS-1) + 1;
301 
302         // The number of adapterItems should be the private space apps + one main app + header.
303         assertEquals(NUM_PRIVATE_SPACE_APPS + 1 + 1,
304                 mAlphabeticalAppsList.getAdapterItems().size());
305         assertEquals(position,
306                 privateProfileManager.scrollForHeaderToBeVisibleInContainer(
307                         new AllAppsRecyclerView(mContext),
308                         mAlphabeticalAppsList.getAdapterItems(),
309                         PS_HEADER_HEIGHT,
310                         ALL_APPS_CELL_HEIGHT));
311     }
312 
313     @Test
scrollForViewToBeVisibleInContainer_withHeaderNoTabs()314     public void scrollForViewToBeVisibleInContainer_withHeaderNoTabs() {
315         when(mAllAppsStore.getApps()).thenReturn(createAppInfoList());
316         PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
317         when(privateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
318         doReturn(splitIntoUserInstalledAndSystemApps()).when(privateProfileManager)
319                 .splitIntoUserInstalledAndSystemApps(any());
320         doReturn(0).when(privateProfileManager).addPrivateSpaceHeader(any());
321         doAnswer(answer(this::addPrivateSpaceHeader)).when(privateProfileManager)
322                 .addPrivateSpaceHeader(any());
323         doNothing().when(privateProfileManager).addPrivateSpaceInstallAppButton(any());
324         doReturn(0).when(privateProfileManager).addSystemAppsDivider(any());
325         when(mAllApps.getHeight()).thenReturn(ALL_APPS_HEIGHT);
326         when(mAllApps.getHeaderProtectionHeight()).thenReturn(HEADER_PROTECTION_HEIGHT);
327         when(mAllApps.isUsingTabs()).thenReturn(false);
328         mAlphabeticalAppsList = new AlphabeticalAppsList<>(mContext, mAllAppsStore,
329                 null, privateProfileManager);
330         mAlphabeticalAppsList.setNumAppsPerRowAllApps(NUM_APP_COLS);
331         mAlphabeticalAppsList.updateItemFilter(info -> info != null
332                 && info.user.equals(MAIN_HANDLE));
333 
334         int rows = (int) (ALL_APPS_HEIGHT - PS_HEADER_HEIGHT - HEADER_PROTECTION_HEIGHT) - 1;
335         int position = rows * NUM_APP_COLS - (NUM_APP_COLS-1) + 1;
336 
337         // The number of adapterItems should be the private space apps + one main app + header.
338         assertEquals(NUM_PRIVATE_SPACE_APPS + 1 + 1,
339                 mAlphabeticalAppsList.getAdapterItems().size());
340         assertEquals(position,
341                 privateProfileManager.scrollForHeaderToBeVisibleInContainer(
342                         new AllAppsRecyclerView(mContext),
343                         mAlphabeticalAppsList.getAdapterItems(),
344                         PS_HEADER_HEIGHT,
345                         ALL_APPS_CELL_HEIGHT));
346     }
347 
348     @Test
scrollForViewToBeVisibleInContainer_withHeaderAndLessAppRowSpace()349     public void scrollForViewToBeVisibleInContainer_withHeaderAndLessAppRowSpace() {
350         when(mAllAppsStore.getApps()).thenReturn(createAppInfoList());
351         PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
352         when(privateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
353         doReturn(splitIntoUserInstalledAndSystemApps()).when(privateProfileManager)
354                 .splitIntoUserInstalledAndSystemApps(any());
355         doReturn(0).when(privateProfileManager).addPrivateSpaceHeader(any());
356         doAnswer(answer(this::addPrivateSpaceHeader)).when(privateProfileManager)
357                 .addPrivateSpaceHeader(any());
358         doNothing().when(privateProfileManager).addPrivateSpaceInstallAppButton(any());
359         doReturn(0).when(privateProfileManager).addSystemAppsDivider(any());
360         when(mAllApps.getHeight()).thenReturn(ALL_APPS_HEIGHT);
361         when(mAllApps.isUsingTabs()).thenReturn(true);
362         when(mAllApps.getHeaderProtectionHeight()).thenReturn(HEADER_PROTECTION_HEIGHT);
363         mAlphabeticalAppsList = new AlphabeticalAppsList<>(mContext, mAllAppsStore,
364                 null, privateProfileManager);
365         mAlphabeticalAppsList.setNumAppsPerRowAllApps(NUM_APP_COLS);
366         mAlphabeticalAppsList.updateItemFilter(info -> info != null
367                 && info.user.equals(MAIN_HANDLE));
368 
369         int rows = (int) (ALL_APPS_HEIGHT - BIGGER_PS_HEADER_HEIGHT - HEADER_PROTECTION_HEIGHT);
370         int position = rows * NUM_APP_COLS - (NUM_APP_COLS-1) + 1;
371 
372         // The number of adapterItems should be the private space apps + one main app + header.
373         assertEquals(NUM_PRIVATE_SPACE_APPS + 1 + 1,
374                 mAlphabeticalAppsList.getAdapterItems().size());
375         assertEquals(position,
376                 privateProfileManager.scrollForHeaderToBeVisibleInContainer(
377                         new AllAppsRecyclerView(mContext),
378                         mAlphabeticalAppsList.getAdapterItems(),
379                         BIGGER_PS_HEADER_HEIGHT,
380                         ALL_APPS_CELL_HEIGHT));
381     }
382 
383     @Test
scrollForViewToBeVisibleInContainer_withNoHeader()384     public void scrollForViewToBeVisibleInContainer_withNoHeader() {
385         when(mAllAppsStore.getApps()).thenReturn(createAppInfoList());
386         PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
387         when(privateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
388         doReturn(splitIntoUserInstalledAndSystemApps()).when(privateProfileManager)
389                 .splitIntoUserInstalledAndSystemApps(any());
390         doReturn(0).when(privateProfileManager).addPrivateSpaceHeader(any());
391         doNothing().when(privateProfileManager).addPrivateSpaceInstallAppButton(any());
392         doReturn(0).when(privateProfileManager).addSystemAppsDivider(any());
393         when(mAllApps.getHeight()).thenReturn(ALL_APPS_HEIGHT);
394         when(mAllApps.getHeaderProtectionHeight()).thenReturn(HEADER_PROTECTION_HEIGHT);
395         mAlphabeticalAppsList = new AlphabeticalAppsList<>(mContext, mAllAppsStore,
396                 null, privateProfileManager);
397         mAlphabeticalAppsList.setNumAppsPerRowAllApps(NUM_APP_COLS);
398         mAlphabeticalAppsList.updateItemFilter(info -> info != null
399                 && info.user.equals(MAIN_HANDLE));
400 
401         // The number of adapterItems should be the private space apps + one main app.
402         assertEquals(NUM_PRIVATE_SPACE_APPS + 1,
403                 mAlphabeticalAppsList.getAdapterItems().size());
404         assertEquals(SCROLL_NO_WHERE, privateProfileManager.scrollForHeaderToBeVisibleInContainer(
405                 new AllAppsRecyclerView(mContext),
406                 mAlphabeticalAppsList.getAdapterItems(),
407                 BIGGER_PS_HEADER_HEIGHT,
408                 ALL_APPS_CELL_HEIGHT));
409     }
410 
getBitmap(Drawable drawable)411     private Bitmap getBitmap(Drawable drawable) {
412         Bitmap result;
413         if (drawable instanceof BitmapDrawable) {
414             result = ((BitmapDrawable) drawable).getBitmap();
415         } else {
416             int width = drawable.getIntrinsicWidth();
417             int height = drawable.getIntrinsicHeight();
418             // Some drawables have no intrinsic width - e.g. solid colours.
419             if (width <= 0) {
420                 width = 1;
421             }
422             if (height <= 0) {
423                 height = 1;
424             }
425 
426             result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
427             Canvas canvas = new Canvas(result);
428             drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
429             drawable.draw(canvas);
430         }
431         return result;
432     }
433 
awaitTasksCompleted()434     private static void awaitTasksCompleted() throws Exception {
435         UI_HELPER_EXECUTOR.submit(() -> null).get();
436     }
437 
addPrivateSpaceHeader(List<BaseAllAppsAdapter.AdapterItem> adapterItemList)438     private int addPrivateSpaceHeader(List<BaseAllAppsAdapter.AdapterItem> adapterItemList) {
439         BaseAllAppsAdapter.AdapterItem privateSpaceHeader =
440                 new BaseAllAppsAdapter.AdapterItem(VIEW_TYPE_PRIVATE_SPACE_HEADER);
441         adapterItemList.add(privateSpaceHeader);
442         return adapterItemList.size();
443     }
444 
createAppInfoList()445     private AppInfo[] createAppInfoList() {
446         List<AppInfo> appInfos = new ArrayList<>();
447         ComponentName gmailComponentName = new ComponentName(mContext,
448                 "com.android.launcher3.tests.Activity" + "Gmail");
449         AppInfo gmailAppInfo = new
450                 AppInfo(gmailComponentName, "Gmail", MAIN_HANDLE, new Intent());
451         appInfos.add(gmailAppInfo);
452         ComponentName privateCameraComponentName = new ComponentName(
453                 CAMERA_PACKAGE_NAME, "CameraActivity");
454         for (int i = 0; i < NUM_PRIVATE_SPACE_APPS; i++) {
455             AppInfo privateCameraAppInfo = new AppInfo(privateCameraComponentName,
456                     "Private Camera " + i, PRIVATE_HANDLE, new Intent());
457             appInfos.add(privateCameraAppInfo);
458         }
459         return appInfos.toArray(AppInfo[]::new);
460     }
461 
splitIntoUserInstalledAndSystemApps()462     private Predicate<AppInfo> splitIntoUserInstalledAndSystemApps() {
463         return iteminfo -> iteminfo.componentName == null
464                 || !iteminfo.componentName.getPackageName()
465                 .equals(CAMERA_PACKAGE_NAME);
466     }
467 }
468