blob: e29552713dcd62b69d41f9fe5113f387393936b3 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.quickstep;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static com.android.quickstep.NavigationModeSwitchRule.Mode.ALL;
import static com.android.quickstep.NavigationModeSwitchRule.Mode.THREE_BUTTON;
import static com.android.quickstep.NavigationModeSwitchRule.Mode.TWO_BUTTON;
import static com.android.quickstep.NavigationModeSwitchRule.Mode.ZERO_BUTTON;
import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_2BUTTON_OVERLAY;
import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_3BUTTON_OVERLAY;
import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_GESTURAL_OVERLAY;
import android.content.Context;
import android.content.pm.PackageManager;
import android.util.Log;
import androidx.test.uiautomator.UiDevice;
import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.util.rule.FailureWatcher;
import com.android.systemui.shared.system.QuickStepContract;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Test rule that allows executing a test with Quickstep on and then Quickstep off.
* The test should be annotated with @QuickstepOnOff.
*/
public class NavigationModeSwitchRule implements TestRule {
static final String TAG = "QuickStepOnOffRule";
public enum Mode {
THREE_BUTTON, TWO_BUTTON, ZERO_BUTTON, ALL
}
// Annotation for tests that need to be run with quickstep enabled and disabled.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NavigationModeSwitch {
Mode mode() default ALL;
}
private final LauncherInstrumentation mLauncher;
public NavigationModeSwitchRule(LauncherInstrumentation launcher) {
mLauncher = launcher;
}
@Override
public Statement apply(Statement base, Description description) {
if (TestHelpers.isInLauncherProcess() &&
description.getAnnotation(NavigationModeSwitch.class) != null) {
Mode mode = description.getAnnotation(NavigationModeSwitch.class).mode();
return new Statement() {
private void assertTrue(String message, boolean condition) {
if(!condition) {
final AssertionError assertionError = new AssertionError(message);
FailureWatcher.onError(mLauncher.getDevice(), description, assertionError);
throw assertionError;
}
}
@Override
public void evaluate() throws Throwable {
mLauncher.enableDebugTracing();
final Context context = getInstrumentation().getContext();
final int currentInteractionMode =
LauncherInstrumentation.getCurrentInteractionMode(context);
final String prevOverlayPkg =
QuickStepContract.isGesturalMode(currentInteractionMode)
? NAV_BAR_MODE_GESTURAL_OVERLAY
: QuickStepContract.isSwipeUpMode(currentInteractionMode)
? NAV_BAR_MODE_2BUTTON_OVERLAY
: NAV_BAR_MODE_3BUTTON_OVERLAY;
final LauncherInstrumentation.NavigationModel originalMode =
mLauncher.getNavigationModel();
try {
if (mode == ZERO_BUTTON || mode == ALL) {
evaluateWithZeroButtons();
}
if (mode == TWO_BUTTON || mode == ALL) {
evaluateWithTwoButtons();
}
if (mode == THREE_BUTTON || mode == ALL) {
evaluateWithThreeButtons();
}
} catch (Exception e) {
Log.e(TAG, "Exception", e);
throw e;
} finally {
assertTrue("Couldn't set overlay",
setActiveOverlay(prevOverlayPkg, originalMode));
}
}
private void evaluateWithThreeButtons() throws Throwable {
if (setActiveOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY,
LauncherInstrumentation.NavigationModel.THREE_BUTTON)) {
base.evaluate();
}
}
private void evaluateWithTwoButtons() throws Throwable {
if (setActiveOverlay(NAV_BAR_MODE_2BUTTON_OVERLAY,
LauncherInstrumentation.NavigationModel.TWO_BUTTON)) {
base.evaluate();
}
}
private void evaluateWithZeroButtons() throws Throwable {
if (setActiveOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY,
LauncherInstrumentation.NavigationModel.ZERO_BUTTON)) {
base.evaluate();
}
}
private boolean packageExists(String packageName) {
try {
PackageManager pm = getInstrumentation().getContext().getPackageManager();
if (pm.getApplicationInfo(packageName, 0 /* flags */) == null) {
return false;
}
} catch (PackageManager.NameNotFoundException e) {
return false;
}
return true;
}
private boolean setActiveOverlay(String overlayPackage,
LauncherInstrumentation.NavigationModel expectedMode) throws Exception {
if (!packageExists(overlayPackage)) {
Log.d(TAG, "setActiveOverlay: " + overlayPackage + " pkg does not exist");
return false;
}
setOverlayPackageEnabled(NAV_BAR_MODE_3BUTTON_OVERLAY,
overlayPackage == NAV_BAR_MODE_3BUTTON_OVERLAY);
setOverlayPackageEnabled(NAV_BAR_MODE_2BUTTON_OVERLAY,
overlayPackage == NAV_BAR_MODE_2BUTTON_OVERLAY);
setOverlayPackageEnabled(NAV_BAR_MODE_GESTURAL_OVERLAY,
overlayPackage == NAV_BAR_MODE_GESTURAL_OVERLAY);
if (currentSysUiNavigationMode() != expectedMode) {
final CountDownLatch latch = new CountDownLatch(1);
final Context targetContext = getInstrumentation().getTargetContext();
final SysUINavigationMode.NavigationModeChangeListener listener =
newMode -> {
if (LauncherInstrumentation.getNavigationModel(newMode.resValue)
== expectedMode) {
latch.countDown();
}
};
final SysUINavigationMode sysUINavigationMode =
SysUINavigationMode.INSTANCE.get(targetContext);
targetContext.getMainExecutor().execute(() ->
sysUINavigationMode.addModeChangeListener(listener));
latch.await(10, TimeUnit.SECONDS);
targetContext.getMainExecutor().execute(() ->
sysUINavigationMode.removeModeChangeListener(listener));
assertTrue("Navigation mode didn't change to " + expectedMode,
currentSysUiNavigationMode() == expectedMode);
}
for (int i = 0; i != 100; ++i) {
if (mLauncher.getNavigationModel() == expectedMode) break;
Thread.sleep(100);
}
assertTrue("Couldn't switch to " + overlayPackage,
mLauncher.getNavigationModel() == expectedMode);
for (int i = 0; i != 100; ++i) {
if (mLauncher.getNavigationModeMismatchError() == null) break;
Thread.sleep(100);
}
final String error = mLauncher.getNavigationModeMismatchError();
assertTrue("Switching nav mode: " + error, error == null);
Thread.sleep(5000);
return true;
}
private void setOverlayPackageEnabled(String overlayPackage, boolean enable)
throws Exception {
Log.d(TAG, "setOverlayPackageEnabled: " + overlayPackage + " " + enable);
final String action = enable ? "enable" : "disable";
UiDevice.getInstance(getInstrumentation()).executeShellCommand(
"cmd overlay " + action + " " + overlayPackage);
}
};
} else {
return base;
}
}
private static LauncherInstrumentation.NavigationModel currentSysUiNavigationMode() {
return LauncherInstrumentation.getNavigationModel(
SysUINavigationMode.getMode(
getInstrumentation().
getTargetContext()).
resValue);
}
}