1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 package com.android.launcher3.compat;
17 
18 import static com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING;
19 
20 import static com.google.common.truth.Truth.assertThat;
21 
22 import android.content.pm.PackageInstaller.SessionParams;
23 import android.content.pm.PackageManager;
24 import android.graphics.Bitmap;
25 import android.platform.test.annotations.RequiresFlagsEnabled;
26 import android.platform.test.flag.junit.CheckFlagsRule;
27 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
28 import android.text.TextUtils;
29 
30 import androidx.test.filters.LargeTest;
31 import androidx.test.runner.AndroidJUnit4;
32 
33 import com.android.launcher3.Launcher;
34 import com.android.launcher3.LauncherState;
35 import com.android.launcher3.tapl.AllApps;
36 import com.android.launcher3.ui.AbstractLauncherUiTest;
37 import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
38 import com.android.launcher3.util.TestUtil;
39 import com.android.launcher3.util.rule.ViewCaptureRule;
40 
41 import org.junit.After;
42 import org.junit.Rule;
43 import org.junit.Test;
44 import org.junit.runner.RunWith;
45 
46 import java.io.IOException;
47 import java.util.UUID;
48 
49 
50 /**
51  * Test to verify promise icon flow.
52  */
53 @LargeTest
54 @RunWith(AndroidJUnit4.class)
55 public class TaplPromiseIconUiTest extends AbstractLauncherUiTest<Launcher> {
56 
57     @Rule
58     public final CheckFlagsRule mCheckFlagsRule =
59             DeviceFlagsValueProvider.createCheckFlagsRule();
60 
61     public static final String PACKAGE_NAME = "test.promise.app";
62     public static final String DUMMY_PACKAGE = "com.example.android.aardwolf";
63     public static final String DUMMY_LABEL = "Aardwolf";
64 
65     private int mSessionId = -1;
66 
67     @Override
setUp()68     public void setUp() throws Exception {
69         super.setUp();
70         mDevice.pressHome();
71         waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
72         waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
73         mSessionId = -1;
74     }
75 
76     @After
tearDown()77     public void tearDown() throws IOException {
78         if (mSessionId > -1) {
79             mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
80         }
81         TestUtil.uninstallDummyApp();
82     }
83 
84     /**
85      * Create a session and return the id.
86      */
createSession(String packageName, String label, Bitmap icon)87     private int createSession(String packageName, String label, Bitmap icon) throws Throwable {
88         SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
89         params.setAppPackageName(packageName);
90         params.setAppLabel(label);
91         params.setAppIcon(icon);
92         params.setInstallReason(PackageManager.INSTALL_REASON_USER);
93         return mTargetContext.getPackageManager().getPackageInstaller().createSession(params);
94     }
95 
96     @Test
testPromiseIcon_addedFromEligibleSession()97     public void testPromiseIcon_addedFromEligibleSession() throws Throwable {
98         final String appLabel = "Test Promise App " + UUID.randomUUID().toString();
99         final ItemOperator findPromiseApp = (info, view) ->
100                 info != null && TextUtils.equals(info.title, appLabel);
101 
102         // Create and add test session
103         mSessionId = createSession(PACKAGE_NAME, appLabel,
104                 Bitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8));
105 
106         // Verify promise icon is added
107         waitForLauncherCondition("Test Promise App not found on workspace", launcher ->
108                 launcher.getWorkspace().getFirstMatch(findPromiseApp) != null);
109 
110         // Remove session
111         mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
112         mSessionId = -1;
113 
114         // Verify promise icon is removed
115         waitForLauncherCondition("Test Promise App not removed from workspace", launcher ->
116                 launcher.getWorkspace().getFirstMatch(findPromiseApp) == null);
117     }
118 
119     @Test
120     @ViewCaptureRule.MayProduceNoFrames
testPromiseIcon_notAddedFromIneligibleSession()121     public void testPromiseIcon_notAddedFromIneligibleSession() throws Throwable {
122         final String appLabel = "Test Promise App " + UUID.randomUUID().toString();
123         final ItemOperator findPromiseApp = (info, view) ->
124                 info != null && TextUtils.equals(info.title, appLabel);
125 
126         // Create and add test session without icon or label
127         mSessionId = createSession(PACKAGE_NAME, null, null);
128 
129         // Sleep for duration of animation if a view was to be added + some buffer time.
130         Thread.sleep(Launcher.NEW_APPS_PAGE_MOVE_DELAY + Launcher.NEW_APPS_ANIMATION_DELAY + 500);
131 
132         // Verify promise icon is not added
133         waitForLauncherCondition("Test Promise App not found on workspace", launcher ->
134                 launcher.getWorkspace().getFirstMatch(findPromiseApp) == null);
135     }
136 
137     @Test
138     @RequiresFlagsEnabled(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING)
testPromiseIcon_addedArchivedApp()139     public void testPromiseIcon_addedArchivedApp() throws Throwable {
140         installDummyAppAndWaitForUIUpdate();
141         assertThat(mDevice.executeShellCommand(String.format("pm archive %s", DUMMY_PACKAGE)))
142                 .isEqualTo("Success\n");
143 
144         // Create and add test session
145         mSessionId = createSession(DUMMY_PACKAGE, /* label= */ "",
146                 Bitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8));
147 
148         // Verify promise icon is added to all apps view. The icon may not be added to the
149         // workspace even if there might be no icon present for archived app. But icon will
150         // always be in all apps view. In case an icon is not added, an exception would be thrown.
151         final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
152 
153         // Wait for the promise icon to be added.
154         waitForLauncherCondition(
155                 DUMMY_PACKAGE + " app was not found on all apps after being archived",
156                 launcher -> {
157                     try {
158                         allApps.getAppIcon(DUMMY_LABEL);
159                     } catch (Throwable t) {
160                         return false;
161                     }
162                     return true;
163                 });
164 
165         // Remove session
166         mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
167         mSessionId = -1;
168     }
169 
installDummyAppAndWaitForUIUpdate()170     private void installDummyAppAndWaitForUIUpdate() throws IOException {
171         TestUtil.installDummyApp();
172         mLauncher.waitForModelQueueCleared();
173         mLauncher.waitForLauncherInitialized();
174     }
175 }
176