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