1 /*
2  * Copyright (C) 2021 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.ui;
17 
18 import static com.android.launcher3.LauncherPrefs.WORK_EDU_STEP;
19 import static com.android.launcher3.LauncherState.ALL_APPS;
20 import static com.android.launcher3.LauncherState.NORMAL;
21 import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
22 import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
23 import static com.android.launcher3.util.TestUtil.installDummyAppForUser;
24 import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
25 import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
26 
27 import static org.junit.Assert.assertEquals;
28 import static org.junit.Assert.assertTrue;
29 import static org.junit.Assume.assumeTrue;
30 
31 import android.util.Log;
32 import android.view.View;
33 
34 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
35 import androidx.test.ext.junit.runners.AndroidJUnit4;
36 import androidx.test.filters.LargeTest;
37 
38 import com.android.launcher3.Launcher;
39 import com.android.launcher3.LauncherPrefs;
40 import com.android.launcher3.R;
41 import com.android.launcher3.allapps.ActivityAllAppsContainerView;
42 import com.android.launcher3.allapps.AllAppsPagedView;
43 import com.android.launcher3.allapps.WorkEduCard;
44 import com.android.launcher3.allapps.WorkPausedCard;
45 import com.android.launcher3.allapps.WorkProfileManager;
46 import com.android.launcher3.tapl.LauncherInstrumentation;
47 import com.android.launcher3.util.TestUtil;
48 import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
49 import com.android.launcher3.util.rule.TestStabilityRule.Stability;
50 
51 import org.junit.After;
52 import org.junit.Before;
53 import org.junit.Test;
54 import org.junit.runner.RunWith;
55 
56 import java.io.IOException;
57 import java.util.Objects;
58 import java.util.function.Predicate;
59 
60 @LargeTest
61 @RunWith(AndroidJUnit4.class)
62 public class TaplWorkProfileTest extends AbstractLauncherUiTest<Launcher> {
63 
64     private static final int WORK_PAGE = ActivityAllAppsContainerView.AdapterHolder.WORK;
65 
66     private int mProfileUserId;
67     private boolean mWorkProfileSetupSuccessful;
68     private final String TAG = "WorkProfileTest";
69 
70     @Before
71     @Override
setUp()72     public void setUp() throws Exception {
73         super.setUp();
74         initialize(this);
75         String output =
76                 mDevice.executeShellCommand(
77                         "pm create-user --profileOf 0 --managed TestProfile");
78         updateWorkProfileSetupSuccessful("pm create-user", output);
79 
80         String[] tokens = output.split("\\s+");
81         mProfileUserId = Integer.parseInt(tokens[tokens.length - 1]);
82         StringBuilder logStr = new StringBuilder().append("profileId: ").append(mProfileUserId);
83         for (String str : tokens) {
84             logStr.append(str).append("\n");
85         }
86         installDummyAppForUser(mProfileUserId);
87         updateWorkProfileSetupSuccessful("am start-user", output);
88 
89         if (!mWorkProfileSetupSuccessful) {
90             return; // no need to setup launcher since all tests will skip.
91         }
92 
93         mDevice.pressHome();
94         waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
95         waitForStateTransitionToEnd("Launcher internal state didn't switch to Normal",
96                 () -> NORMAL);
97         waitForResumed("Launcher internal state is still Background");
98         mLauncher.getWorkspace().switchToAllApps();
99         waitForStateTransitionToEnd("Launcher internal state didn't switch to All Apps",
100                 () -> ALL_APPS);
101     }
102 
103     @After
removeWorkProfile()104     public void removeWorkProfile() throws Exception {
105         executeOnLauncherInTearDown(launcher -> {
106             if (launcher.getAppsView() == null) {
107                 return;
108             }
109             launcher.getAppsView().getAppsStore().disableDeferUpdates(DEFER_UPDATES_TEST);
110         });
111         TestUtil.uninstallDummyApp();
112 
113         mLauncher.runToState(
114                 () -> {
115                     try {
116                         mDevice.executeShellCommand("pm remove-user --wait " + mProfileUserId);
117                     } catch (IOException e) {
118                         throw new RuntimeException(e);
119                     }
120                 },
121                 NORMAL_STATE_ORDINAL,
122                 "executing pm 'remove-user' command");
123     }
124 
waitForWorkTabSetup()125     private void waitForWorkTabSetup() {
126         waitForLauncherCondition("Work tab not setup", launcher -> {
127             if (launcher.getAppsView().getContentView() instanceof AllAppsPagedView) {
128                 launcher.getAppsView().getAppsStore().enableDeferUpdates(DEFER_UPDATES_TEST);
129                 return true;
130             }
131             return false;
132         }, LauncherInstrumentation.WAIT_TIME_MS);
133     }
134 
135     @Test
136     @com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord // b/325383911
workTabExists()137     public void workTabExists() {
138         assumeTrue(mWorkProfileSetupSuccessful);
139         waitForWorkTabSetup();
140         waitForLauncherCondition("Personal tab is missing",
141                 launcher -> launcher.getAppsView().isPersonalTabVisible(),
142                 LauncherInstrumentation.WAIT_TIME_MS);
143         waitForLauncherCondition("Work tab is missing",
144                 launcher -> launcher.getAppsView().isWorkTabVisible(),
145                 LauncherInstrumentation.WAIT_TIME_MS);
146     }
147 
148     // Staging; will be promoted to presubmit if stable
149     @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
150     @ScreenRecord
151     @Test
toggleWorks()152     public void toggleWorks() {
153         assumeTrue(mWorkProfileSetupSuccessful);
154         waitForWorkTabSetup();
155         executeOnLauncher(launcher -> {
156             AllAppsPagedView pagedView = (AllAppsPagedView) launcher.getAppsView().getContentView();
157             pagedView.setCurrentPage(WORK_PAGE);
158         });
159 
160         WorkProfileManager manager = getFromLauncher(l -> l.getAppsView().getWorkManager());
161 
162 
163         waitForLauncherCondition("work profile initial state check failed", launcher ->
164                         manager.getWorkModeSwitch() != null
165                                 && manager.getCurrentState() == WorkProfileManager.STATE_ENABLED
166                                 && manager.getWorkModeSwitch().isEnabled(),
167                 LauncherInstrumentation.WAIT_TIME_MS);
168 
169         //start work profile toggle OFF test
170         executeOnLauncher(l -> {
171             // Ensure updates are not deferred so notification happens when apps pause.
172             l.getAppsView().getAppsStore().disableDeferUpdates(DEFER_UPDATES_TEST);
173             l.getAppsView().getWorkManager().getWorkModeSwitch().performClick();
174         });
175 
176         waitForLauncherCondition("Work profile toggle OFF failed", launcher -> {
177             manager.reset(); // pulls current state from system
178             return manager.getCurrentState() == WorkProfileManager.STATE_DISABLED;
179         }, LauncherInstrumentation.WAIT_TIME_MS);
180 
181         waitForWorkCard("Work paused card not shown", view -> view instanceof WorkPausedCard);
182 
183         // start work profile toggle ON test
184         executeOnLauncher(l -> {
185             ActivityAllAppsContainerView<?> allApps = l.getAppsView();
186             assertEquals("Work tab is not focused", allApps.getCurrentPage(), WORK_PAGE);
187             View workPausedCard = allApps.getActiveRecyclerView()
188                     .findViewHolderForAdapterPosition(0).itemView;
189             workPausedCard.findViewById(R.id.enable_work_apps).performClick();
190         });
191         waitForLauncherCondition("Work profile toggle ON failed", launcher -> {
192             manager.reset(); // pulls current state from system
193             return manager.getCurrentState() == WorkProfileManager.STATE_ENABLED;
194         }, LauncherInstrumentation.WAIT_TIME_MS);
195 
196     }
197 
198     @ScreenRecord // b/322823478
199     @Test
testEdu()200     public void testEdu() {
201         assumeTrue(mWorkProfileSetupSuccessful);
202         waitForWorkTabSetup();
203         executeOnLauncher(l -> {
204             LauncherPrefs.get(l).putSync(WORK_EDU_STEP.to(0));
205             ((AllAppsPagedView) l.getAppsView().getContentView()).setCurrentPage(WORK_PAGE);
206             l.getAppsView().getWorkManager().reset();
207         });
208 
209         waitForWorkCard("Work profile education not shown", view -> view instanceof WorkEduCard);
210     }
211 
waitForWorkCard(String message, Predicate<View> workCardCheck)212     private void waitForWorkCard(String message, Predicate<View> workCardCheck) {
213         waitForLauncherCondition(message, l -> {
214             l.getAppsView().getAppsStore().disableDeferUpdates(DEFER_UPDATES_TEST);
215             ViewHolder holder = l.getAppsView().getActiveRecyclerView()
216                     .findViewHolderForAdapterPosition(0);
217             try {
218                 return holder != null && workCardCheck.test(holder.itemView);
219             } finally {
220                 l.getAppsView().getAppsStore().enableDeferUpdates(DEFER_UPDATES_TEST);
221             }
222         }, LauncherInstrumentation.WAIT_TIME_MS);
223     }
224 
updateWorkProfileSetupSuccessful(String cli, String output)225     private void updateWorkProfileSetupSuccessful(String cli, String output) {
226         Log.d(TAG, "updateWorkProfileSetupSuccessful, cli=" + cli + " " + "output=" + output);
227         if (output.startsWith("Success")) {
228             assertTrue(output, output.startsWith("Success"));
229             mWorkProfileSetupSuccessful = true;
230         } else {
231             mWorkProfileSetupSuccessful = false;
232         }
233     }
234 }
235