version:1.5
fix:迁移到奥乐云平台 add:
This commit is contained in:
388
tests/src/com/aoleyun/os/ui/AbstractLauncherUiTest.java
Normal file
388
tests/src/com/aoleyun/os/ui/AbstractLauncherUiTest.java
Normal file
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.aoleyun.os.ui;
|
||||
|
||||
import static androidx.test.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import static java.lang.System.exit;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.LauncherActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.uiautomator.By;
|
||||
import androidx.test.uiautomator.BySelector;
|
||||
import androidx.test.uiautomator.Direction;
|
||||
import androidx.test.uiautomator.UiDevice;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
import androidx.test.uiautomator.Until;
|
||||
|
||||
import com.aoleyun.os.Launcher;
|
||||
import com.aoleyun.os.LauncherAppState;
|
||||
import com.aoleyun.os.LauncherModel;
|
||||
import com.aoleyun.os.LauncherSettings;
|
||||
import com.aoleyun.os.LauncherState;
|
||||
import com.aoleyun.os.MainThreadExecutor;
|
||||
import com.aoleyun.os.ResourceUtils;
|
||||
import com.aoleyun.os.Utilities;
|
||||
import com.aoleyun.os.compat.LauncherAppsCompat;
|
||||
import com.aoleyun.os.model.AppLaunchTracker;
|
||||
import com.aoleyun.os.tapl.LauncherInstrumentation;
|
||||
import com.aoleyun.os.tapl.TestHelpers;
|
||||
import com.aoleyun.os.util.Wait;
|
||||
import com.aoleyun.os.util.rule.FailureWatcher;
|
||||
import com.aoleyun.os.util.rule.LauncherActivityRule;
|
||||
import com.aoleyun.os.util.rule.ShellCommandRule;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.rules.RuleChain;
|
||||
import org.junit.rules.TestRule;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Base class for all instrumentation tests providing various utility methods.
|
||||
*/
|
||||
public abstract class AbstractLauncherUiTest {
|
||||
|
||||
public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
|
||||
public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5;
|
||||
|
||||
public static final long SHORT_UI_TIMEOUT = 300;
|
||||
public static final long DEFAULT_UI_TIMEOUT = 10000;
|
||||
private static final String TAG = "AbstractLauncherUiTest";
|
||||
|
||||
protected MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
|
||||
protected final UiDevice mDevice = UiDevice.getInstance(getInstrumentation());
|
||||
protected final LauncherInstrumentation mLauncher =
|
||||
new LauncherInstrumentation(getInstrumentation());
|
||||
protected Context mTargetContext;
|
||||
protected String mTargetPackage;
|
||||
|
||||
protected AbstractLauncherUiTest() {
|
||||
try {
|
||||
mDevice.setOrientationNatural();
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (TestHelpers.isInLauncherProcess()) Utilities.enableRunningInTestHarnessForTests();
|
||||
}
|
||||
|
||||
protected final LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
|
||||
|
||||
@Rule
|
||||
public ShellCommandRule mDefaultLauncherRule =
|
||||
TestHelpers.isInLauncherProcess() ? ShellCommandRule.setDefaultLauncher() : null;
|
||||
|
||||
@Rule
|
||||
public ShellCommandRule mDisableHeadsUpNotification =
|
||||
ShellCommandRule.disableHeadsUpNotification();
|
||||
|
||||
// Annotation for tests that need to be run in portrait and landscape modes.
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
protected @interface PortraitLandscape {
|
||||
}
|
||||
|
||||
protected TestRule getRulesInsideActivityMonitor() {
|
||||
return RuleChain.
|
||||
outerRule(new PortraitLandscapeRunner(this)).
|
||||
around(new FailureWatcher(mDevice));
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestRule mOrderSensitiveRules = RuleChain.
|
||||
outerRule(mActivityMonitor).
|
||||
around(getRulesInsideActivityMonitor());
|
||||
|
||||
public UiDevice getDevice() {
|
||||
return mDevice;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
// Disable app tracker
|
||||
AppLaunchTracker.INSTANCE.initializeForTesting(new AppLaunchTracker());
|
||||
|
||||
mTargetContext = InstrumentationRegistry.getTargetContext();
|
||||
mTargetPackage = mTargetContext.getPackageName();
|
||||
// Unlock the phone
|
||||
mDevice.executeShellCommand("input keyevent 82");
|
||||
}
|
||||
|
||||
@After
|
||||
public void verifyLauncherState() {
|
||||
try {
|
||||
// Limits UI tests affecting tests running after them.
|
||||
waitForModelLoaded();
|
||||
} catch (Throwable t) {
|
||||
Log.e(TAG,
|
||||
"Couldn't deinit after a test, exiting tests, see logs for failures that "
|
||||
+ "could have caused this",
|
||||
t);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
protected void lockRotation(boolean naturalOrientation) throws RemoteException {
|
||||
if (naturalOrientation) {
|
||||
mDevice.setOrientationNatural();
|
||||
} else {
|
||||
mDevice.setOrientationRight();
|
||||
}
|
||||
}
|
||||
|
||||
protected void clearLauncherData() throws IOException {
|
||||
if (TestHelpers.isInLauncherProcess()) {
|
||||
LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
|
||||
LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
|
||||
resetLoaderState();
|
||||
} else {
|
||||
mDevice.executeShellCommand("pm clear " + mDevice.getLauncherPackageName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls the {@param container} until it finds an object matching {@param condition}.
|
||||
*
|
||||
* @return the matching object.
|
||||
*/
|
||||
protected UiObject2 scrollAndFind(UiObject2 container, BySelector condition) {
|
||||
final int margin = ResourceUtils.getNavbarSize(
|
||||
ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1;
|
||||
container.setGestureMargins(0, 0, 0, margin);
|
||||
|
||||
int i = 0;
|
||||
for (; ; ) {
|
||||
// findObject can only execute after spring settles.
|
||||
mDevice.wait(Until.findObject(condition), SHORT_UI_TIMEOUT);
|
||||
UiObject2 widget = container.findObject(condition);
|
||||
if (widget != null && widget.getVisibleBounds().intersects(
|
||||
0, 0, mDevice.getDisplayWidth(),
|
||||
mDevice.getDisplayHeight() - margin)) {
|
||||
return widget;
|
||||
}
|
||||
if (++i > 40) fail("Too many attempts");
|
||||
container.scroll(Direction.DOWN, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all icons from homescreen and hotseat.
|
||||
*/
|
||||
public void clearHomescreen() throws Throwable {
|
||||
LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
|
||||
LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
|
||||
LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
|
||||
LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
|
||||
resetLoaderState();
|
||||
}
|
||||
|
||||
protected void resetLoaderState() {
|
||||
try {
|
||||
mMainThreadExecutor.execute(
|
||||
() -> LauncherAppState.getInstance(mTargetContext).getModel().forceReload());
|
||||
} catch (Throwable t) {
|
||||
throw new IllegalArgumentException(t);
|
||||
}
|
||||
waitForModelLoaded();
|
||||
}
|
||||
|
||||
protected void waitForModelLoaded() {
|
||||
waitForLauncherCondition("Launcher model didn't load", launcher -> {
|
||||
final LauncherModel model = LauncherAppState.getInstance(mTargetContext).getModel();
|
||||
return model.getCallback() == null || model.isModelLoaded();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the callback on the UI thread and returns the result.
|
||||
*/
|
||||
protected <T> T getOnUiThread(final Callable<T> callback) {
|
||||
try {
|
||||
return mMainThreadExecutor.submit(callback).get();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> T getFromLauncher(Function<Launcher, T> f) {
|
||||
if (!TestHelpers.isInLauncherProcess()) return null;
|
||||
return getOnUiThread(() -> f.apply(mActivityMonitor.getActivity()));
|
||||
}
|
||||
|
||||
protected void executeOnLauncher(Consumer<Launcher> f) {
|
||||
getFromLauncher(launcher -> {
|
||||
f.accept(launcher);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
// Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call expecting
|
||||
// the results of that gesture because the wait can hide flakeness.
|
||||
protected void waitForState(String message, LauncherState state) {
|
||||
waitForLauncherCondition(message,
|
||||
launcher -> launcher.getStateManager().getCurrentStableState() == state);
|
||||
}
|
||||
|
||||
protected void waitForResumed(String message) {
|
||||
waitForLauncherCondition(message, launcher -> launcher.hasBeenResumed());
|
||||
}
|
||||
|
||||
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
|
||||
// flakiness.
|
||||
protected void waitForLauncherCondition(String message, Function<Launcher, Boolean> condition) {
|
||||
waitForLauncherCondition(message, condition, DEFAULT_ACTIVITY_TIMEOUT);
|
||||
}
|
||||
|
||||
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
|
||||
// flakiness.
|
||||
protected void waitForLauncherCondition(
|
||||
String message, Function<Launcher, Boolean> condition, long timeout) {
|
||||
if (!TestHelpers.isInLauncherProcess()) return;
|
||||
Wait.atMost(message, () -> getFromLauncher(condition), timeout);
|
||||
}
|
||||
|
||||
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
|
||||
// flakiness.
|
||||
protected void waitForLauncherCondition(
|
||||
String message,
|
||||
Runnable testThreadAction, Function<Launcher, Boolean> condition,
|
||||
long timeout) {
|
||||
if (!TestHelpers.isInLauncherProcess()) return;
|
||||
Wait.atMost(message, () -> {
|
||||
testThreadAction.run();
|
||||
return getFromLauncher(condition);
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
protected LauncherActivityInfo getSettingsApp() {
|
||||
return LauncherAppsCompat.getInstance(mTargetContext)
|
||||
.getActivityList("com.android.settings",
|
||||
Process.myUserHandle()).get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast receiver which blocks until the result is received.
|
||||
*/
|
||||
public class BlockingBroadcastReceiver extends BroadcastReceiver {
|
||||
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
private Intent mIntent;
|
||||
|
||||
public BlockingBroadcastReceiver(String action) {
|
||||
mTargetContext.registerReceiver(this, new IntentFilter(action));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
mIntent = intent;
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
public Intent blockingGetIntent() throws InterruptedException {
|
||||
latch.await(DEFAULT_BROADCAST_TIMEOUT_SECS, TimeUnit.SECONDS);
|
||||
mTargetContext.unregisterReceiver(this);
|
||||
return mIntent;
|
||||
}
|
||||
|
||||
public Intent blockingGetExtraIntent() throws InterruptedException {
|
||||
Intent intent = blockingGetIntent();
|
||||
return intent == null ? null : (Intent) intent.getParcelableExtra(Intent.EXTRA_INTENT);
|
||||
}
|
||||
}
|
||||
|
||||
protected void startAppFast(String packageName) {
|
||||
final Instrumentation instrumentation = getInstrumentation();
|
||||
final Intent intent = instrumentation.getContext().getPackageManager().
|
||||
getLaunchIntentForPackage(packageName);
|
||||
intent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
instrumentation.getTargetContext().startActivity(intent);
|
||||
assertTrue(packageName + " didn't start",
|
||||
mDevice.wait(Until.hasObject(By.pkg(packageName).depth(0)), DEFAULT_UI_TIMEOUT));
|
||||
}
|
||||
|
||||
protected void startTestActivity(int activityNumber) {
|
||||
final String packageName = TaplTestsLauncher3.getAppPackageName();
|
||||
final Instrumentation instrumentation = getInstrumentation();
|
||||
final Intent intent = instrumentation.getContext().getPackageManager().
|
||||
getLaunchIntentForPackage(packageName);
|
||||
intent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setComponent(new ComponentName(packageName,
|
||||
"com.aoleyun.os.tests.Activity" + activityNumber));
|
||||
instrumentation.getTargetContext().startActivity(intent);
|
||||
assertTrue(packageName + " didn't start",
|
||||
mDevice.wait(
|
||||
Until.hasObject(By.pkg(packageName).text("TestActivity" + activityNumber)),
|
||||
DEFAULT_UI_TIMEOUT));
|
||||
}
|
||||
|
||||
public static String resolveSystemApp(String category) {
|
||||
return getInstrumentation().getContext().getPackageManager().resolveActivity(
|
||||
new Intent(Intent.ACTION_MAIN).addCategory(category),
|
||||
PackageManager.MATCH_SYSTEM_ONLY).
|
||||
activityInfo.packageName;
|
||||
}
|
||||
|
||||
protected void closeLauncherActivity() {
|
||||
// Destroy Launcher activity.
|
||||
executeOnLauncher(launcher -> {
|
||||
if (launcher != null) {
|
||||
launcher.finish();
|
||||
}
|
||||
});
|
||||
waitForLauncherCondition(
|
||||
"Launcher still active", launcher -> launcher == null, DEFAULT_UI_TIMEOUT);
|
||||
}
|
||||
|
||||
protected boolean isInBackground(Launcher launcher) {
|
||||
return !launcher.hasBeenResumed();
|
||||
}
|
||||
|
||||
protected boolean isInState(LauncherState state) {
|
||||
if (!TestHelpers.isInLauncherProcess()) return true;
|
||||
return getFromLauncher(launcher -> launcher.getStateManager().getState() == state);
|
||||
}
|
||||
|
||||
protected int getAllAppsScroll(Launcher launcher) {
|
||||
return launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
|
||||
}
|
||||
}
|
||||
129
tests/src/com/aoleyun/os/ui/DefaultLayoutProviderTest.java
Normal file
129
tests/src/com/aoleyun/os/ui/DefaultLayoutProviderTest.java
Normal file
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Copyright (C) 2019 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.aoleyun.os.ui;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.MediumTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.aoleyun.os.LauncherAppWidgetProviderInfo;
|
||||
import com.aoleyun.os.testcomponent.TestCommandReceiver;
|
||||
import com.aoleyun.os.util.LauncherLayoutBuilder;
|
||||
import com.aoleyun.os.util.rule.ShellCommandRule;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
@MediumTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class DefaultLayoutProviderTest extends AbstractLauncherUiTest {
|
||||
|
||||
@Rule
|
||||
public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
|
||||
|
||||
private static final String SETTINGS_APP = "com.android.settings";
|
||||
|
||||
private Context mContext;
|
||||
private String mAuthority;
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
mContext = InstrumentationRegistry.getContext();
|
||||
|
||||
PackageManager pm = mTargetContext.getPackageManager();
|
||||
ProviderInfo pi = pm.getProviderInfo(new ComponentName(mContext,
|
||||
TestCommandReceiver.class), 0);
|
||||
mAuthority = pi.authority;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomProfileLoaded_with_icon_on_hotseat() throws Exception {
|
||||
writeLayout(new LauncherLayoutBuilder().atHotseat(0).putApp(SETTINGS_APP, SETTINGS_APP));
|
||||
|
||||
// Launch the home activity
|
||||
mActivityMonitor.startLauncher();
|
||||
waitForModelLoaded();
|
||||
|
||||
mLauncher.getWorkspace().getHotseatAppIcon(getSettingsApp().getLabel().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomProfileLoaded_with_widget() throws Exception {
|
||||
// A non-restored widget with no config screen gets restored automatically.
|
||||
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
|
||||
|
||||
writeLayout(new LauncherLayoutBuilder().atWorkspace(0, 1, 0)
|
||||
.putWidget(info.getComponent().getPackageName(),
|
||||
info.getComponent().getClassName(), 2, 2));
|
||||
|
||||
// Launch the home activity
|
||||
mActivityMonitor.startLauncher();
|
||||
waitForModelLoaded();
|
||||
|
||||
// Verify widget present
|
||||
assertTrue("Widget is not present",
|
||||
mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomProfileLoaded_with_folder() throws Exception {
|
||||
writeLayout(new LauncherLayoutBuilder().atHotseat(0).putFolder(android.R.string.copy)
|
||||
.addApp(SETTINGS_APP, SETTINGS_APP)
|
||||
.addApp(SETTINGS_APP, SETTINGS_APP)
|
||||
.addApp(SETTINGS_APP, SETTINGS_APP)
|
||||
.build());
|
||||
|
||||
// Launch the home activity
|
||||
mActivityMonitor.startLauncher();
|
||||
waitForModelLoaded();
|
||||
|
||||
mLauncher.getWorkspace().getHotseatFolder("Folder: Copy");
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() throws Exception {
|
||||
mDevice.executeShellCommand("settings delete secure launcher3.layout.provider");
|
||||
}
|
||||
|
||||
private void writeLayout(LauncherLayoutBuilder builder) throws Exception {
|
||||
mDevice.executeShellCommand("settings put secure launcher3.layout.provider " + mAuthority);
|
||||
ParcelFileDescriptor pfd = mTargetContext.getContentResolver().openFileDescriptor(
|
||||
Uri.parse("content://" + mAuthority + "/launcher_layout"), "w");
|
||||
|
||||
try (OutputStreamWriter writer = new OutputStreamWriter(new AutoCloseOutputStream(pfd))) {
|
||||
builder.build(writer);
|
||||
}
|
||||
clearLauncherData();
|
||||
}
|
||||
}
|
||||
63
tests/src/com/aoleyun/os/ui/PortraitLandscapeRunner.java
Normal file
63
tests/src/com/aoleyun/os/ui/PortraitLandscapeRunner.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package com.aoleyun.os.ui;
|
||||
|
||||
import android.view.Surface;
|
||||
|
||||
import com.aoleyun.os.tapl.TestHelpers;
|
||||
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
class PortraitLandscapeRunner implements TestRule {
|
||||
private AbstractLauncherUiTest mTest;
|
||||
|
||||
public PortraitLandscapeRunner(AbstractLauncherUiTest test) {
|
||||
mTest = test;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement base, Description description) {
|
||||
if (!TestHelpers.isInLauncherProcess() ||
|
||||
description.getAnnotation(AbstractLauncherUiTest.PortraitLandscape.class) == null) {
|
||||
return base;
|
||||
}
|
||||
|
||||
return new Statement() {
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
try {
|
||||
mTest.mDevice.pressHome();
|
||||
mTest.waitForLauncherCondition("Launcher activity wasn't created",
|
||||
launcher -> launcher != null);
|
||||
|
||||
mTest.executeOnLauncher(launcher ->
|
||||
launcher.getRotationHelper().forceAllowRotationForTesting(
|
||||
true));
|
||||
|
||||
evaluateInPortrait();
|
||||
evaluateInLandscape();
|
||||
} finally {
|
||||
mTest.mDevice.setOrientationNatural();
|
||||
mTest.executeOnLauncher(launcher ->
|
||||
launcher.getRotationHelper().forceAllowRotationForTesting(
|
||||
false));
|
||||
mTest.mLauncher.setExpectedRotation(Surface.ROTATION_0);
|
||||
}
|
||||
}
|
||||
|
||||
private void evaluateInPortrait() throws Throwable {
|
||||
mTest.mDevice.setOrientationNatural();
|
||||
mTest.mLauncher.setExpectedRotation(Surface.ROTATION_0);
|
||||
base.evaluate();
|
||||
mTest.getDevice().pressHome();
|
||||
}
|
||||
|
||||
private void evaluateInLandscape() throws Throwable {
|
||||
mTest.mDevice.setOrientationLeft();
|
||||
mTest.mLauncher.setExpectedRotation(Surface.ROTATION_90);
|
||||
base.evaluate();
|
||||
mTest.getDevice().pressHome();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
352
tests/src/com/aoleyun/os/ui/TaplTestsLauncher3.java
Normal file
352
tests/src/com/aoleyun/os/ui/TaplTestsLauncher3.java
Normal file
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.aoleyun.os.ui;
|
||||
|
||||
import static androidx.test.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.aoleyun.os.Launcher;
|
||||
import com.aoleyun.os.LauncherState;
|
||||
import com.aoleyun.os.popup.ArrowPopup;
|
||||
import com.aoleyun.os.tapl.AllApps;
|
||||
import com.aoleyun.os.tapl.AppIcon;
|
||||
import com.aoleyun.os.tapl.AppIconMenu;
|
||||
import com.aoleyun.os.tapl.AppIconMenuItem;
|
||||
import com.aoleyun.os.tapl.Widgets;
|
||||
import com.aoleyun.os.tapl.Workspace;
|
||||
import com.aoleyun.os.views.OptionsPopupView;
|
||||
import com.aoleyun.os.widget.WidgetsFullSheet;
|
||||
import com.aoleyun.os.widget.WidgetsRecyclerView;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
|
||||
private static final String APP_NAME = "LauncherTestApp";
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
initialize(this);
|
||||
}
|
||||
|
||||
public static void initialize(AbstractLauncherUiTest test) throws Exception {
|
||||
test.clearLauncherData();
|
||||
test.mDevice.pressHome();
|
||||
test.waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
|
||||
test.waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
|
||||
test.waitForResumed("Launcher internal state is still Background");
|
||||
// Check that we switched to home.
|
||||
test.mLauncher.getWorkspace();
|
||||
}
|
||||
|
||||
// Please don't add negative test cases for methods that fail only after a long wait.
|
||||
public static void expectFail(String message, Runnable action) {
|
||||
boolean failed = false;
|
||||
try {
|
||||
action.run();
|
||||
} catch (AssertionError e) {
|
||||
failed = true;
|
||||
}
|
||||
assertTrue(message, failed);
|
||||
}
|
||||
|
||||
private boolean isWorkspaceScrollable(Launcher launcher) {
|
||||
return launcher.getWorkspace().getPageCount() > 1;
|
||||
}
|
||||
|
||||
private int getCurrentWorkspacePage(Launcher launcher) {
|
||||
return launcher.getWorkspace().getCurrentPage();
|
||||
}
|
||||
|
||||
private WidgetsRecyclerView getWidgetsView(Launcher launcher) {
|
||||
return WidgetsFullSheet.getWidgetsView(launcher);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDevicePressMenu() throws Exception {
|
||||
mDevice.pressMenu();
|
||||
mDevice.waitForIdle();
|
||||
executeOnLauncher(
|
||||
launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu",
|
||||
OptionsPopupView.getOptionsPopup(launcher) != null));
|
||||
// Check that pressHome works when the menu is shown.
|
||||
mLauncher.pressHome();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testPressHomeOnAllAppsContextMenu() throws Exception {
|
||||
final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
|
||||
allApps.freeze();
|
||||
try {
|
||||
allApps.getAppIcon("TestActivity7").openMenu();
|
||||
} finally {
|
||||
allApps.unfreeze();
|
||||
}
|
||||
mLauncher.pressHome();
|
||||
}
|
||||
|
||||
public static void runAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
|
||||
allApps.freeze();
|
||||
try {
|
||||
assertNotNull("allApps parameter is null", allApps);
|
||||
|
||||
assertTrue(
|
||||
"Launcher internal state is not All Apps",
|
||||
test.isInState(LauncherState.ALL_APPS));
|
||||
|
||||
// Test flinging forward and backward.
|
||||
test.executeOnLauncher(launcher -> assertEquals(
|
||||
"All Apps started in already scrolled state", 0,
|
||||
test.getAllAppsScroll(launcher)));
|
||||
|
||||
allApps.flingForward();
|
||||
assertTrue("Launcher internal state is not All Apps",
|
||||
test.isInState(LauncherState.ALL_APPS));
|
||||
final Integer flingForwardY = test.getFromLauncher(
|
||||
launcher -> test.getAllAppsScroll(launcher));
|
||||
test.executeOnLauncher(
|
||||
launcher -> assertTrue("flingForward() didn't scroll App Apps",
|
||||
flingForwardY > 0));
|
||||
|
||||
allApps.flingBackward();
|
||||
assertTrue(
|
||||
"Launcher internal state is not All Apps",
|
||||
test.isInState(LauncherState.ALL_APPS));
|
||||
final Integer flingBackwardY = test.getFromLauncher(
|
||||
launcher -> test.getAllAppsScroll(launcher));
|
||||
test.executeOnLauncher(launcher -> assertTrue("flingBackward() didn't scroll App Apps",
|
||||
flingBackwardY < flingForwardY));
|
||||
|
||||
// Test scrolling down to YouTube.
|
||||
assertNotNull("All apps: can't fine YouTube", allApps.getAppIcon("YouTube"));
|
||||
// Test scrolling up to Camera.
|
||||
assertNotNull("All apps: can't fine Camera", allApps.getAppIcon("Camera"));
|
||||
// Test failing to find a non-existing app.
|
||||
final AllApps allAppsFinal = allApps;
|
||||
expectFail("All apps: could find a non-existing app",
|
||||
() -> allAppsFinal.getAppIcon("NO APP"));
|
||||
|
||||
assertTrue(
|
||||
"Launcher internal state is not All Apps",
|
||||
test.isInState(LauncherState.ALL_APPS));
|
||||
} finally {
|
||||
allApps.unfreeze();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
public void testWorkspaceSwitchToAllApps() {
|
||||
assertNotNull("switchToAllApps() returned null",
|
||||
mLauncher.getWorkspace().switchToAllApps());
|
||||
assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkspace() throws Exception {
|
||||
final Workspace workspace = mLauncher.getWorkspace();
|
||||
|
||||
// Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there.
|
||||
executeOnLauncher(launcher -> assertFalse("Initial workspace state is scrollable",
|
||||
isWorkspaceScrollable(launcher)));
|
||||
assertNull("Chrome app was found on empty workspace",
|
||||
workspace.tryGetWorkspaceAppIcon("Chrome"));
|
||||
|
||||
workspace.ensureWorkspaceIsScrollable();
|
||||
|
||||
executeOnLauncher(
|
||||
launcher -> assertEquals("Ensuring workspace scrollable didn't switch to page #1",
|
||||
1, getCurrentWorkspacePage(launcher)));
|
||||
executeOnLauncher(
|
||||
launcher -> assertTrue("ensureScrollable didn't make workspace scrollable",
|
||||
isWorkspaceScrollable(launcher)));
|
||||
assertNotNull("ensureScrollable didn't add Chrome app",
|
||||
workspace.tryGetWorkspaceAppIcon("Chrome"));
|
||||
|
||||
// Test flinging workspace.
|
||||
workspace.flingBackward();
|
||||
assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
|
||||
executeOnLauncher(
|
||||
launcher -> assertEquals("Flinging back didn't switch workspace to page #0",
|
||||
0, getCurrentWorkspacePage(launcher)));
|
||||
|
||||
workspace.flingForward();
|
||||
executeOnLauncher(
|
||||
launcher -> assertEquals("Flinging forward didn't switch workspace to page #1",
|
||||
1, getCurrentWorkspacePage(launcher)));
|
||||
assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
|
||||
|
||||
// Test starting a workspace app.
|
||||
final AppIcon app = workspace.tryGetWorkspaceAppIcon("Chrome");
|
||||
assertNotNull("No Chrome app in workspace", app);
|
||||
}
|
||||
|
||||
public static void runIconLaunchFromAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
|
||||
allApps.freeze();
|
||||
try {
|
||||
final AppIcon app = allApps.getAppIcon("TestActivity7");
|
||||
assertNotNull("AppIcon.launch returned null", app.launch(getAppPackageName()));
|
||||
test.executeOnLauncher(launcher -> assertTrue(
|
||||
"Launcher activity is the top activity; expecting another activity to be the top "
|
||||
+ "one",
|
||||
test.isInBackground(launcher)));
|
||||
} finally {
|
||||
allApps.unfreeze();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
public void testAppIconLaunchFromAllAppsFromHome() throws Exception {
|
||||
final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
|
||||
assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
|
||||
|
||||
runIconLaunchFromAllAppsTest(this, allApps);
|
||||
}
|
||||
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
public void testWidgets() throws Exception {
|
||||
// Test opening widgets.
|
||||
executeOnLauncher(launcher ->
|
||||
assertTrue("Widgets is initially opened", getWidgetsView(launcher) == null));
|
||||
Widgets widgets = mLauncher.getWorkspace().openAllWidgets();
|
||||
assertNotNull("openAllWidgets() returned null", widgets);
|
||||
widgets = mLauncher.getAllWidgets();
|
||||
assertNotNull("getAllWidgets() returned null", widgets);
|
||||
executeOnLauncher(launcher ->
|
||||
assertTrue("Widgets is not shown", getWidgetsView(launcher).isShown()));
|
||||
executeOnLauncher(launcher -> assertEquals("Widgets is scrolled upon opening",
|
||||
0, getWidgetsScroll(launcher)));
|
||||
|
||||
// Test flinging widgets.
|
||||
widgets.flingForward();
|
||||
Integer flingForwardY = getFromLauncher(launcher -> getWidgetsScroll(launcher));
|
||||
executeOnLauncher(launcher -> assertTrue("Flinging forward didn't scroll widgets",
|
||||
flingForwardY > 0));
|
||||
|
||||
widgets.flingBackward();
|
||||
executeOnLauncher(launcher -> assertTrue("Flinging backward didn't scroll widgets",
|
||||
getWidgetsScroll(launcher) < flingForwardY));
|
||||
|
||||
mLauncher.pressHome();
|
||||
waitForLauncherCondition("Widgets were not closed",
|
||||
launcher -> getWidgetsView(launcher) == null);
|
||||
}
|
||||
|
||||
private int getWidgetsScroll(Launcher launcher) {
|
||||
return getWidgetsView(launcher).getCurrentScrollY();
|
||||
}
|
||||
|
||||
private boolean isOptionsPopupVisible(Launcher launcher) {
|
||||
final ArrowPopup popup = OptionsPopupView.getOptionsPopup(launcher);
|
||||
return popup != null && popup.isShown();
|
||||
}
|
||||
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
public void testLaunchMenuItem() throws Exception {
|
||||
final AllApps allApps = mLauncher.
|
||||
getWorkspace().
|
||||
switchToAllApps();
|
||||
allApps.freeze();
|
||||
try {
|
||||
final AppIconMenu menu = allApps.
|
||||
getAppIcon(APP_NAME).
|
||||
openMenu();
|
||||
|
||||
executeOnLauncher(
|
||||
launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu",
|
||||
isOptionsPopupVisible(launcher)));
|
||||
|
||||
menu.getMenuItem(1).launch(getAppPackageName());
|
||||
} finally {
|
||||
allApps.unfreeze();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
public void testDragAppIcon() throws Throwable {
|
||||
// 1. Open all apps and wait for load complete.
|
||||
// 2. Drag icon to homescreen.
|
||||
// 3. Verify that the icon works on homescreen.
|
||||
final AllApps allApps = mLauncher.getWorkspace().
|
||||
switchToAllApps();
|
||||
allApps.freeze();
|
||||
try {
|
||||
allApps.
|
||||
getAppIcon(APP_NAME).
|
||||
dragToWorkspace().
|
||||
getWorkspaceAppIcon(APP_NAME).
|
||||
launch(getAppPackageName());
|
||||
} finally {
|
||||
allApps.unfreeze();
|
||||
}
|
||||
executeOnLauncher(launcher -> assertTrue(
|
||||
"Launcher activity is the top activity; expecting another activity to be the top "
|
||||
+ "one",
|
||||
isInBackground(launcher)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
public void testDragShortcut() throws Throwable {
|
||||
// 1. Open all apps and wait for load complete.
|
||||
// 2. Find the app and long press it to show shortcuts.
|
||||
// 3. Press icon center until shortcuts appear
|
||||
final AllApps allApps = mLauncher.
|
||||
getWorkspace().
|
||||
switchToAllApps();
|
||||
allApps.freeze();
|
||||
try {
|
||||
final AppIconMenuItem menuItem = allApps.
|
||||
getAppIcon(APP_NAME).
|
||||
openMenu().
|
||||
getMenuItem(0);
|
||||
final String shortcutName = menuItem.getText();
|
||||
|
||||
// 4. Drag the first shortcut to the home screen.
|
||||
// 5. Verify that the shortcut works on home screen
|
||||
// (the app opens and has the same text as the shortcut).
|
||||
menuItem.
|
||||
dragToWorkspace().
|
||||
getWorkspaceAppIcon(shortcutName).
|
||||
launch(getAppPackageName());
|
||||
} finally {
|
||||
allApps.unfreeze();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getAppPackageName() {
|
||||
return getInstrumentation().getContext().getPackageName();
|
||||
}
|
||||
}
|
||||
187
tests/src/com/aoleyun/os/ui/TestViewHelpers.java
Normal file
187
tests/src/com/aoleyun/os/ui/TestViewHelpers.java
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.aoleyun.os.ui;
|
||||
|
||||
import static androidx.test.InstrumentationRegistry.getInstrumentation;
|
||||
import static androidx.test.InstrumentationRegistry.getTargetContext;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.os.Process;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.test.uiautomator.By;
|
||||
import androidx.test.uiautomator.BySelector;
|
||||
import androidx.test.uiautomator.UiDevice;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
import androidx.test.uiautomator.Until;
|
||||
|
||||
import com.aoleyun.os.LauncherAppWidgetProviderInfo;
|
||||
import com.aoleyun.os.R;
|
||||
import com.aoleyun.os.compat.AppWidgetManagerCompat;
|
||||
import com.aoleyun.os.testcomponent.AppWidgetNoConfig;
|
||||
import com.aoleyun.os.testcomponent.AppWidgetWithConfig;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class TestViewHelpers {
|
||||
private static final String TAG = "TestViewHelpers";
|
||||
|
||||
private static UiDevice getDevice() {
|
||||
return UiDevice.getInstance(getInstrumentation());
|
||||
}
|
||||
|
||||
public static UiObject2 findViewById(int id) {
|
||||
return getDevice().wait(Until.findObject(getSelectorForId(id)),
|
||||
AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT);
|
||||
}
|
||||
|
||||
public static BySelector getSelectorForId(int id) {
|
||||
final Context targetContext = getTargetContext();
|
||||
String name = targetContext.getResources().getResourceEntryName(id);
|
||||
return By.res(targetContext.getPackageName(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a widget provider which can fit on the home screen.
|
||||
*
|
||||
* @param test test suite.
|
||||
* @param hasConfigureScreen if true, a provider with a config screen is returned.
|
||||
*/
|
||||
public static LauncherAppWidgetProviderInfo findWidgetProvider(AbstractLauncherUiTest test,
|
||||
final boolean hasConfigureScreen) {
|
||||
LauncherAppWidgetProviderInfo info =
|
||||
test.getOnUiThread(new Callable<LauncherAppWidgetProviderInfo>() {
|
||||
@Override
|
||||
public LauncherAppWidgetProviderInfo call() throws Exception {
|
||||
ComponentName cn = new ComponentName(getInstrumentation().getContext(),
|
||||
hasConfigureScreen ? AppWidgetWithConfig.class
|
||||
: AppWidgetNoConfig.class);
|
||||
Log.d(TAG, "findWidgetProvider componentName=" + cn.flattenToString());
|
||||
return AppWidgetManagerCompat.getInstance(getTargetContext())
|
||||
.findProvider(cn, Process.myUserHandle());
|
||||
}
|
||||
});
|
||||
if (info == null) {
|
||||
throw new IllegalArgumentException("No valid widget provider");
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drags an icon to the center of homescreen.
|
||||
*
|
||||
* @param icon object that is either app icon or shortcut icon
|
||||
*/
|
||||
public static void dragToWorkspace(UiObject2 icon, boolean expectedToShowShortcuts) {
|
||||
Point center = icon.getVisibleCenter();
|
||||
|
||||
// Action Down
|
||||
final long downTime = SystemClock.uptimeMillis();
|
||||
sendPointer(downTime, MotionEvent.ACTION_DOWN, center);
|
||||
|
||||
UiObject2 dragLayer = findViewById(R.id.drag_layer);
|
||||
|
||||
if (expectedToShowShortcuts) {
|
||||
// Make sure shortcuts show up, and then move a bit to hide them.
|
||||
assertNotNull(findViewById(R.id.deep_shortcuts_container));
|
||||
|
||||
Point moveLocation = new Point(center);
|
||||
int distanceToMove =
|
||||
getTargetContext().getResources().getDimensionPixelSize(
|
||||
R.dimen.deep_shortcuts_start_drag_threshold) + 50;
|
||||
if (moveLocation.y - distanceToMove >= dragLayer.getVisibleBounds().top) {
|
||||
moveLocation.y -= distanceToMove;
|
||||
} else {
|
||||
moveLocation.y += distanceToMove;
|
||||
}
|
||||
movePointer(downTime, center, moveLocation);
|
||||
|
||||
assertNull(findViewById(R.id.deep_shortcuts_container));
|
||||
}
|
||||
|
||||
// Wait until Remove/Delete target is visible
|
||||
assertNotNull(findViewById(R.id.delete_target_text));
|
||||
|
||||
Point moveLocation = dragLayer.getVisibleCenter();
|
||||
|
||||
// Move to center
|
||||
movePointer(downTime, center, moveLocation);
|
||||
sendPointer(downTime, MotionEvent.ACTION_UP, moveLocation);
|
||||
|
||||
// Wait until remove target is gone.
|
||||
getDevice().wait(Until.gone(getSelectorForId(R.id.delete_target_text)),
|
||||
AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT);
|
||||
}
|
||||
|
||||
private static void movePointer(long downTime, Point from, Point to) {
|
||||
while (!from.equals(to)) {
|
||||
try {
|
||||
Thread.sleep(20);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
from.x = getNextMoveValue(to.x, from.x);
|
||||
from.y = getNextMoveValue(to.y, from.y);
|
||||
sendPointer(downTime, MotionEvent.ACTION_MOVE, from);
|
||||
}
|
||||
}
|
||||
|
||||
private static int getNextMoveValue(int targetValue, int oldValue) {
|
||||
if (targetValue - oldValue > 10) {
|
||||
return oldValue + 10;
|
||||
} else if (targetValue - oldValue < -10) {
|
||||
return oldValue - 10;
|
||||
} else {
|
||||
return targetValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendPointer(long downTime, int action, Point point) {
|
||||
MotionEvent event = MotionEvent.obtain(downTime,
|
||||
SystemClock.uptimeMillis(), action, point.x, point.y, 0);
|
||||
getInstrumentation().sendPointerSync(event);
|
||||
event.recycle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens widget tray and returns the recycler view.
|
||||
*/
|
||||
public static UiObject2 openWidgetsTray() {
|
||||
final UiDevice device = getDevice();
|
||||
device.pressKeyCode(KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON);
|
||||
device.waitForIdle();
|
||||
return findViewById(R.id.widgets_list_view);
|
||||
}
|
||||
|
||||
public static View findChildView(ViewGroup parent, Function<View, Boolean> condition) {
|
||||
for (int i = 0; i < parent.getChildCount(); ++i) {
|
||||
final View child = parent.getChildAt(i);
|
||||
if (condition.apply(child)) return child;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
67
tests/src/com/aoleyun/os/ui/WorkTabTest.java
Normal file
67
tests/src/com/aoleyun/os/ui/WorkTabTest.java
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 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.aoleyun.os.ui;
|
||||
|
||||
import static com.aoleyun.os.LauncherState.ALL_APPS;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class WorkTabTest extends AbstractLauncherUiTest {
|
||||
|
||||
private int mProfileUserId;
|
||||
|
||||
@Before
|
||||
public void createWorkProfile() throws Exception {
|
||||
String output =
|
||||
mDevice.executeShellCommand(
|
||||
"pm create-user --profileOf 0 --managed TestProfile");
|
||||
assertTrue("Failed to create work profile", output.startsWith("Success"));
|
||||
|
||||
String[] tokens = output.split("\\s+");
|
||||
mProfileUserId = Integer.parseInt(tokens[tokens.length - 1]);
|
||||
|
||||
mDevice.executeShellCommand("am start-user " + mProfileUserId);
|
||||
}
|
||||
|
||||
@After
|
||||
public void removeWorkProfile() throws Exception {
|
||||
mDevice.executeShellCommand("pm remove-user " + mProfileUserId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void workTabExists() {
|
||||
mActivityMonitor.startLauncher();
|
||||
|
||||
executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
|
||||
|
||||
/*
|
||||
assertTrue("Personal tab is missing", waitForLauncherCondition(
|
||||
launcher -> launcher.getAppsView().isPersonalTabVisible()));
|
||||
assertTrue("Work tab is missing", waitForLauncherCondition(
|
||||
launcher -> launcher.getAppsView().isWorkTabVisible()));
|
||||
*/
|
||||
}
|
||||
}
|
||||
184
tests/src/com/aoleyun/os/ui/widget/AddConfigWidgetTest.java
Normal file
184
tests/src/com/aoleyun/os/ui/widget/AddConfigWidgetTest.java
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.aoleyun.os.ui.widget;
|
||||
|
||||
import static androidx.test.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.Intent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
import androidx.test.uiautomator.By;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
import com.aoleyun.os.ItemInfo;
|
||||
import com.aoleyun.os.LauncherAppWidgetInfo;
|
||||
import com.aoleyun.os.LauncherAppWidgetProviderInfo;
|
||||
import com.aoleyun.os.Workspace;
|
||||
import com.aoleyun.os.testcomponent.WidgetConfigActivity;
|
||||
import com.aoleyun.os.ui.AbstractLauncherUiTest;
|
||||
import com.aoleyun.os.ui.TestViewHelpers;
|
||||
import com.aoleyun.os.util.Condition;
|
||||
import com.aoleyun.os.util.Wait;
|
||||
import com.aoleyun.os.util.rule.ShellCommandRule;
|
||||
import com.aoleyun.os.widget.WidgetCell;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Test to verify widget configuration is properly shown.
|
||||
*/
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AddConfigWidgetTest extends AbstractLauncherUiTest {
|
||||
|
||||
@Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
|
||||
|
||||
private LauncherAppWidgetProviderInfo mWidgetInfo;
|
||||
private AppWidgetManager mAppWidgetManager;
|
||||
|
||||
private int mWidgetId;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
mWidgetInfo = TestViewHelpers.findWidgetProvider(this, true /* hasConfigureScreen */);
|
||||
mAppWidgetManager = AppWidgetManager.getInstance(mTargetContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
// Convert test to TAPL b/131116002
|
||||
public void testWidgetConfig() throws Throwable {
|
||||
runTest(false, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore // b/121280703
|
||||
public void testWidgetConfig_rotate() throws Throwable {
|
||||
runTest(true, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
// Convert test to TAPL b/131116002
|
||||
public void testConfigCancelled() throws Throwable {
|
||||
runTest(false, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore // b/121280703
|
||||
public void testConfigCancelled_rotate() throws Throwable {
|
||||
runTest(true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rotateConfig should the config screen be rotated
|
||||
* @param acceptConfig accept the config activity
|
||||
*/
|
||||
private void runTest(boolean rotateConfig, boolean acceptConfig) throws Throwable {
|
||||
lockRotation(true);
|
||||
|
||||
clearHomescreen();
|
||||
mActivityMonitor.startLauncher();
|
||||
|
||||
// Open widget tray and wait for load complete.
|
||||
final UiObject2 widgetContainer = TestViewHelpers.openWidgetsTray();
|
||||
Wait.atMost(null, Condition.minChildCount(widgetContainer, 2), DEFAULT_UI_TIMEOUT);
|
||||
|
||||
// Drag widget to homescreen
|
||||
WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor();
|
||||
UiObject2 widget = scrollAndFind(widgetContainer, By.clazz(WidgetCell.class)
|
||||
.hasDescendant(By.text(mWidgetInfo.getLabel(mTargetContext.getPackageManager()))));
|
||||
TestViewHelpers.dragToWorkspace(widget, false);
|
||||
// Widget id for which the config activity was opened
|
||||
mWidgetId = monitor.getWidgetId();
|
||||
|
||||
if (rotateConfig) {
|
||||
// Rotate the screen and verify that the config activity is recreated
|
||||
monitor = new WidgetConfigStartupMonitor();
|
||||
lockRotation(false);
|
||||
assertEquals(mWidgetId, monitor.getWidgetId());
|
||||
}
|
||||
|
||||
// Verify that the widget id is valid and bound
|
||||
assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
|
||||
|
||||
setResult(acceptConfig);
|
||||
if (acceptConfig) {
|
||||
Wait.atMost(null, new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT);
|
||||
assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
|
||||
} else {
|
||||
// Verify that the widget id is deleted.
|
||||
Wait.atMost(null, () -> mAppWidgetManager.getAppWidgetInfo(mWidgetId) == null,
|
||||
DEFAULT_ACTIVITY_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
private void setResult(boolean success) {
|
||||
getInstrumentation().getTargetContext().sendBroadcast(
|
||||
WidgetConfigActivity.getCommandIntent(WidgetConfigActivity.class,
|
||||
success ? "clickOK" : "clickCancel"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition for searching widget id
|
||||
*/
|
||||
private class WidgetSearchCondition implements Condition, Workspace.ItemOperator {
|
||||
|
||||
@Override
|
||||
public boolean isTrue() throws Throwable {
|
||||
return mMainThreadExecutor.submit(mActivityMonitor.itemExists(this)).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluate(ItemInfo info, View view) {
|
||||
return info instanceof LauncherAppWidgetInfo &&
|
||||
((LauncherAppWidgetInfo) info).providerName.getClassName().equals(
|
||||
mWidgetInfo.provider.getClassName()) &&
|
||||
((LauncherAppWidgetInfo) info).appWidgetId == mWidgetId;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast receiver for receiving widget config activity status.
|
||||
*/
|
||||
private class WidgetConfigStartupMonitor extends BlockingBroadcastReceiver {
|
||||
|
||||
public WidgetConfigStartupMonitor() {
|
||||
super(WidgetConfigActivity.class.getName());
|
||||
}
|
||||
|
||||
public int getWidgetId() throws InterruptedException {
|
||||
Intent intent = blockingGetExtraIntent();
|
||||
assertNotNull(intent);
|
||||
assertEquals(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE, intent.getAction());
|
||||
int widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
|
||||
LauncherAppWidgetInfo.NO_ID);
|
||||
assertNotSame(widgetId, LauncherAppWidgetInfo.NO_ID);
|
||||
return widgetId;
|
||||
}
|
||||
}
|
||||
}
|
||||
90
tests/src/com/aoleyun/os/ui/widget/AddWidgetTest.java
Normal file
90
tests/src/com/aoleyun/os/ui/widget/AddWidgetTest.java
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.aoleyun.os.ui.widget;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
import androidx.test.uiautomator.By;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
import android.view.View;
|
||||
|
||||
import com.aoleyun.os.ItemInfo;
|
||||
import com.aoleyun.os.LauncherAppWidgetInfo;
|
||||
import com.aoleyun.os.LauncherAppWidgetProviderInfo;
|
||||
import com.aoleyun.os.Workspace.ItemOperator;
|
||||
import com.aoleyun.os.ui.AbstractLauncherUiTest;
|
||||
import com.aoleyun.os.ui.TestViewHelpers;
|
||||
import com.aoleyun.os.util.Condition;
|
||||
import com.aoleyun.os.util.Wait;
|
||||
import com.aoleyun.os.util.rule.ShellCommandRule;
|
||||
import com.aoleyun.os.widget.WidgetCell;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Test to add widget from widget tray
|
||||
*/
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AddWidgetTest extends AbstractLauncherUiTest {
|
||||
|
||||
@Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
|
||||
|
||||
@Test
|
||||
public void testDragIcon_portrait() throws Throwable {
|
||||
lockRotation(true);
|
||||
performTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore // b/121280703
|
||||
public void testDragIcon_landscape() throws Throwable {
|
||||
lockRotation(false);
|
||||
performTest();
|
||||
}
|
||||
|
||||
// Convert to TAPL b/131116002
|
||||
private void performTest() throws Throwable {
|
||||
clearHomescreen();
|
||||
mActivityMonitor.startLauncher();
|
||||
|
||||
final LauncherAppWidgetProviderInfo widgetInfo =
|
||||
TestViewHelpers.findWidgetProvider(this, false /* hasConfigureScreen */);
|
||||
|
||||
// Open widget tray and wait for load complete.
|
||||
final UiObject2 widgetContainer = TestViewHelpers.openWidgetsTray();
|
||||
Wait.atMost(null, Condition.minChildCount(widgetContainer, 2), DEFAULT_UI_TIMEOUT);
|
||||
|
||||
// Drag widget to homescreen
|
||||
UiObject2 widget = scrollAndFind(widgetContainer, By.clazz(WidgetCell.class)
|
||||
.hasDescendant(By.text(widgetInfo.getLabel(mTargetContext.getPackageManager()))));
|
||||
TestViewHelpers.dragToWorkspace(widget, false);
|
||||
|
||||
assertTrue(mActivityMonitor.itemExists(new ItemOperator() {
|
||||
@Override
|
||||
public boolean evaluate(ItemInfo info, View view) {
|
||||
return info instanceof LauncherAppWidgetInfo &&
|
||||
((LauncherAppWidgetInfo) info).providerName.getClassName().equals(
|
||||
widgetInfo.provider.getClassName());
|
||||
}
|
||||
}).call());
|
||||
}
|
||||
}
|
||||
355
tests/src/com/aoleyun/os/ui/widget/BindWidgetTest.java
Normal file
355
tests/src/com/aoleyun/os/ui/widget/BindWidgetTest.java
Normal file
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.aoleyun.os.ui.widget;
|
||||
|
||||
import static com.aoleyun.os.WorkspaceLayoutManager.FIRST_SCREEN_ID;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.appwidget.AppWidgetHost;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.pm.PackageInstaller;
|
||||
import android.content.pm.PackageInstaller.SessionParams;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.aoleyun.os.LauncherAppWidgetHost;
|
||||
import com.aoleyun.os.LauncherAppWidgetInfo;
|
||||
import com.aoleyun.os.LauncherAppWidgetProviderInfo;
|
||||
import com.aoleyun.os.LauncherSettings;
|
||||
import com.aoleyun.os.compat.AppWidgetManagerCompat;
|
||||
import com.aoleyun.os.compat.PackageInstallerCompat;
|
||||
import com.aoleyun.os.tapl.Workspace;
|
||||
import com.aoleyun.os.ui.AbstractLauncherUiTest;
|
||||
import com.aoleyun.os.ui.TestViewHelpers;
|
||||
import com.aoleyun.os.util.ContentWriter;
|
||||
import com.aoleyun.os.util.rule.ShellCommandRule;
|
||||
import com.aoleyun.os.widget.PendingAddWidgetInfo;
|
||||
import com.aoleyun.os.widget.WidgetHostViewLoader;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Tests for bind widget flow.
|
||||
*
|
||||
* Note running these tests will clear the workspace on the device.
|
||||
*/
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class BindWidgetTest extends AbstractLauncherUiTest {
|
||||
|
||||
@Rule
|
||||
public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
|
||||
|
||||
private ContentResolver mResolver;
|
||||
private AppWidgetManagerCompat mWidgetManager;
|
||||
|
||||
// Objects created during test, which should be cleaned up in the end.
|
||||
private Cursor mCursor;
|
||||
// App install session id.
|
||||
private int mSessionId = -1;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
mResolver = mTargetContext.getContentResolver();
|
||||
mWidgetManager = AppWidgetManagerCompat.getInstance(mTargetContext);
|
||||
|
||||
// Clear all existing data
|
||||
LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
|
||||
LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if (mCursor != null) {
|
||||
mCursor.close();
|
||||
}
|
||||
|
||||
if (mSessionId > -1) {
|
||||
mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindNormalWidget_withConfig() {
|
||||
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, true);
|
||||
LauncherAppWidgetInfo item = createWidgetInfo(info, true);
|
||||
|
||||
setupContents(item);
|
||||
verifyWidgetPresent(info);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindNormalWidget_withoutConfig() {
|
||||
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
|
||||
LauncherAppWidgetInfo item = createWidgetInfo(info, true);
|
||||
|
||||
setupContents(item);
|
||||
verifyWidgetPresent(info);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnboundWidget_removed() {
|
||||
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
|
||||
LauncherAppWidgetInfo item = createWidgetInfo(info, false);
|
||||
item.appWidgetId = -33;
|
||||
|
||||
setupContents(item);
|
||||
|
||||
final Workspace workspace = mLauncher.getWorkspace();
|
||||
// Item deleted from db
|
||||
mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
|
||||
null, null, null, null, null);
|
||||
assertEquals(0, mCursor.getCount());
|
||||
|
||||
// The view does not exist
|
||||
assertTrue("Widget exists", workspace.tryGetWidget(info.label, 0) == null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPendingWidget_autoRestored() {
|
||||
// A non-restored widget with no config screen gets restored automatically.
|
||||
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
|
||||
|
||||
// Do not bind the widget
|
||||
LauncherAppWidgetInfo item = createWidgetInfo(info, false);
|
||||
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
|
||||
|
||||
setupContents(item);
|
||||
verifyWidgetPresent(info);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPendingWidget_withConfigScreen() {
|
||||
// A non-restored widget with config screen get bound and shows a 'Click to setup' UI.
|
||||
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, true);
|
||||
|
||||
// Do not bind the widget
|
||||
LauncherAppWidgetInfo item = createWidgetInfo(info, false);
|
||||
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
|
||||
|
||||
setupContents(item);
|
||||
verifyPendingWidgetPresent();
|
||||
|
||||
// Item deleted from db
|
||||
mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
|
||||
null, null, null, null, null);
|
||||
mCursor.moveToNext();
|
||||
|
||||
// Widget has a valid Id now.
|
||||
assertEquals(0, mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED))
|
||||
& LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
|
||||
assertNotNull(AppWidgetManager.getInstance(mTargetContext)
|
||||
.getAppWidgetInfo(mCursor.getInt(mCursor.getColumnIndex(
|
||||
LauncherSettings.Favorites.APPWIDGET_ID))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPendingWidget_notRestored_removed() {
|
||||
LauncherAppWidgetInfo item = getInvalidWidgetInfo();
|
||||
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
|
||||
| LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
|
||||
|
||||
setupContents(item);
|
||||
|
||||
assertTrue("Pending widget exists",
|
||||
mLauncher.getWorkspace().tryGetPendingWidget(0) == null);
|
||||
// Item deleted from db
|
||||
mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
|
||||
null, null, null, null, null);
|
||||
assertEquals(0, mCursor.getCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPendingWidget_notRestored_brokenInstall() {
|
||||
// A widget which is was being installed once, even if its not being
|
||||
// installed at the moment is not removed.
|
||||
LauncherAppWidgetInfo item = getInvalidWidgetInfo();
|
||||
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
|
||||
| LauncherAppWidgetInfo.FLAG_RESTORE_STARTED
|
||||
| LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
|
||||
|
||||
setupContents(item);
|
||||
verifyPendingWidgetPresent();
|
||||
|
||||
// Verify item still exists in db
|
||||
mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
|
||||
null, null, null, null, null);
|
||||
assertEquals(1, mCursor.getCount());
|
||||
|
||||
// Widget still has an invalid id.
|
||||
mCursor.moveToNext();
|
||||
assertEquals(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID,
|
||||
mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED))
|
||||
& LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPendingWidget_notRestored_activeInstall() throws Exception {
|
||||
// A widget which is being installed is not removed
|
||||
LauncherAppWidgetInfo item = getInvalidWidgetInfo();
|
||||
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
|
||||
| LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
|
||||
|
||||
// Create an active installer session
|
||||
SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
|
||||
params.setAppPackageName(item.providerName.getPackageName());
|
||||
PackageInstaller installer = mTargetContext.getPackageManager().getPackageInstaller();
|
||||
mSessionId = installer.createSession(params);
|
||||
|
||||
setupContents(item);
|
||||
verifyPendingWidgetPresent();
|
||||
|
||||
// Verify item still exists in db
|
||||
mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
|
||||
null, null, null, null, null);
|
||||
assertEquals(1, mCursor.getCount());
|
||||
|
||||
// Widget still has an invalid id.
|
||||
mCursor.moveToNext();
|
||||
assertEquals(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID,
|
||||
mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED))
|
||||
& LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds {@param item} on the homescreen on the 0th screen at 0,0, and verifies that the
|
||||
* widget class is displayed on the homescreen.
|
||||
*/
|
||||
private void setupContents(LauncherAppWidgetInfo item) {
|
||||
int screenId = FIRST_SCREEN_ID;
|
||||
// Update the screen id counter for the provider.
|
||||
LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
|
||||
|
||||
if (screenId > FIRST_SCREEN_ID) {
|
||||
screenId = FIRST_SCREEN_ID;
|
||||
}
|
||||
|
||||
// Insert the item
|
||||
ContentWriter writer = new ContentWriter(mTargetContext);
|
||||
item.id = LauncherSettings.Settings.call(
|
||||
mResolver, LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
|
||||
.getInt(LauncherSettings.Settings.EXTRA_VALUE);
|
||||
item.screenId = screenId;
|
||||
item.onAddToDatabase(writer);
|
||||
writer.put(LauncherSettings.Favorites._ID, item.id);
|
||||
mResolver.insert(LauncherSettings.Favorites.CONTENT_URI, writer.getValues(mTargetContext));
|
||||
resetLoaderState();
|
||||
|
||||
// Launch the home activity
|
||||
mActivityMonitor.startLauncher();
|
||||
waitForModelLoaded();
|
||||
}
|
||||
|
||||
private void verifyWidgetPresent(LauncherAppWidgetProviderInfo info) {
|
||||
assertTrue("Widget is not present",
|
||||
mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
|
||||
}
|
||||
|
||||
private void verifyPendingWidgetPresent() {
|
||||
assertTrue("Pending widget is not present",
|
||||
mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT) != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a LauncherAppWidgetInfo corresponding to {@param info}
|
||||
* @param bindWidget if true the info is bound and a valid widgetId is assigned to
|
||||
* the LauncherAppWidgetInfo
|
||||
*/
|
||||
private LauncherAppWidgetInfo createWidgetInfo(
|
||||
LauncherAppWidgetProviderInfo info, boolean bindWidget) {
|
||||
LauncherAppWidgetInfo item = new LauncherAppWidgetInfo(
|
||||
LauncherAppWidgetInfo.NO_ID, info.provider);
|
||||
item.spanX = info.minSpanX;
|
||||
item.spanY = info.minSpanY;
|
||||
item.minSpanX = info.minSpanX;
|
||||
item.minSpanY = info.minSpanY;
|
||||
item.user = info.getProfile();
|
||||
item.cellX = 0;
|
||||
item.cellY = 1;
|
||||
item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
||||
|
||||
if (bindWidget) {
|
||||
PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(info);
|
||||
pendingInfo.spanX = item.spanX;
|
||||
pendingInfo.spanY = item.spanY;
|
||||
pendingInfo.minSpanX = item.minSpanX;
|
||||
pendingInfo.minSpanY = item.minSpanY;
|
||||
Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(mTargetContext, pendingInfo);
|
||||
|
||||
AppWidgetHost host = new LauncherAppWidgetHost(mTargetContext);
|
||||
int widgetId = host.allocateAppWidgetId();
|
||||
if (!mWidgetManager.bindAppWidgetIdIfAllowed(widgetId, info, options)) {
|
||||
host.deleteAppWidgetId(widgetId);
|
||||
throw new IllegalArgumentException("Unable to bind widget id");
|
||||
}
|
||||
item.appWidgetId = widgetId;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a LauncherAppWidgetInfo with package name which is not present on the device
|
||||
*/
|
||||
private LauncherAppWidgetInfo getInvalidWidgetInfo() {
|
||||
String invalidPackage = "com.invalidpackage";
|
||||
int count = 0;
|
||||
String pkg = invalidPackage;
|
||||
|
||||
Set<String> activePackage = getOnUiThread(() ->
|
||||
PackageInstallerCompat.getInstance(mTargetContext)
|
||||
.updateAndGetActiveSessionCache().keySet());
|
||||
while(true) {
|
||||
try {
|
||||
mTargetContext.getPackageManager().getPackageInfo(
|
||||
pkg, PackageManager.GET_UNINSTALLED_PACKAGES);
|
||||
} catch (Exception e) {
|
||||
if (!activePackage.contains(pkg)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
pkg = invalidPackage + count;
|
||||
count ++;
|
||||
}
|
||||
LauncherAppWidgetInfo item = new LauncherAppWidgetInfo(10,
|
||||
new ComponentName(pkg, "com.test.widgetprovider"));
|
||||
item.spanX = 2;
|
||||
item.spanY = 2;
|
||||
item.minSpanX = 2;
|
||||
item.minSpanY = 2;
|
||||
item.cellX = 0;
|
||||
item.cellY = 1;
|
||||
item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
||||
return item;
|
||||
}
|
||||
}
|
||||
192
tests/src/com/aoleyun/os/ui/widget/RequestPinItemTest.java
Normal file
192
tests/src/com/aoleyun/os/ui/widget/RequestPinItemTest.java
Normal file
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.aoleyun.os.ui.widget;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.aoleyun.os.ItemInfo;
|
||||
import com.aoleyun.os.LauncherAppWidgetInfo;
|
||||
import com.aoleyun.os.LauncherSettings.Favorites;
|
||||
import com.aoleyun.os.Utilities;
|
||||
import com.aoleyun.os.Workspace.ItemOperator;
|
||||
import com.aoleyun.os.WorkspaceItemInfo;
|
||||
import com.aoleyun.os.shortcuts.ShortcutKey;
|
||||
import com.aoleyun.os.tapl.AddToHomeScreenPrompt;
|
||||
import com.aoleyun.os.testcomponent.AppWidgetNoConfig;
|
||||
import com.aoleyun.os.testcomponent.AppWidgetWithConfig;
|
||||
import com.aoleyun.os.testcomponent.RequestPinItemActivity;
|
||||
import com.aoleyun.os.ui.AbstractLauncherUiTest;
|
||||
import com.aoleyun.os.util.Condition;
|
||||
import com.aoleyun.os.util.Wait;
|
||||
import com.aoleyun.os.util.rule.ShellCommandRule;
|
||||
import com.aoleyun.os.ui.TaplTestsLauncher3;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Test to verify pin item request flow.
|
||||
*/
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class RequestPinItemTest extends AbstractLauncherUiTest {
|
||||
|
||||
@Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
|
||||
|
||||
private String mCallbackAction;
|
||||
private String mShortcutId;
|
||||
private int mAppWidgetId;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
mCallbackAction = UUID.randomUUID().toString();
|
||||
mShortcutId = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmpty() throws Throwable { /* needed while the broken tests are being fixed */ }
|
||||
|
||||
@Test
|
||||
public void testPinWidgetNoConfig() throws Throwable {
|
||||
runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo &&
|
||||
((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
|
||||
((LauncherAppWidgetInfo) info).providerName.getClassName()
|
||||
.equals(AppWidgetNoConfig.class.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPinWidgetNoConfig_customPreview() throws Throwable {
|
||||
// Command to set custom preview
|
||||
Intent command = RequestPinItemActivity.getCommandIntent(
|
||||
RequestPinItemActivity.class, "setRemoteViewColor").putExtra(
|
||||
RequestPinItemActivity.EXTRA_PARAM + "0", Color.RED);
|
||||
|
||||
runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo &&
|
||||
((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
|
||||
((LauncherAppWidgetInfo) info).providerName.getClassName()
|
||||
.equals(AppWidgetNoConfig.class.getName()), command);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPinWidgetWithConfig() throws Throwable {
|
||||
runTest("pinWidgetWithConfig", true,
|
||||
(info, view) -> info instanceof LauncherAppWidgetInfo &&
|
||||
((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
|
||||
((LauncherAppWidgetInfo) info).providerName.getClassName()
|
||||
.equals(AppWidgetWithConfig.class.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPinShortcut() throws Throwable {
|
||||
// Command to set the shortcut id
|
||||
Intent command = RequestPinItemActivity.getCommandIntent(
|
||||
RequestPinItemActivity.class, "setShortcutId").putExtra(
|
||||
RequestPinItemActivity.EXTRA_PARAM + "0", mShortcutId);
|
||||
|
||||
runTest("pinShortcut", false, new ItemOperator() {
|
||||
@Override
|
||||
public boolean evaluate(ItemInfo info, View view) {
|
||||
return info instanceof WorkspaceItemInfo &&
|
||||
info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
|
||||
ShortcutKey.fromItemInfo(info).getId().equals(mShortcutId);
|
||||
}
|
||||
}, command);
|
||||
}
|
||||
|
||||
private void runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher,
|
||||
Intent... commandIntents) throws Throwable {
|
||||
if (!Utilities.ATLEAST_OREO) {
|
||||
return;
|
||||
}
|
||||
lockRotation(true);
|
||||
|
||||
clearHomescreen();
|
||||
mActivityMonitor.startLauncher();
|
||||
|
||||
// Open Pin item activity
|
||||
BlockingBroadcastReceiver openMonitor = new BlockingBroadcastReceiver(
|
||||
RequestPinItemActivity.class.getName());
|
||||
mLauncher.
|
||||
getWorkspace().
|
||||
switchToAllApps().
|
||||
getAppIcon("Test Pin Item").
|
||||
launch(TaplTestsLauncher3.getAppPackageName());
|
||||
assertNotNull(openMonitor.blockingGetExtraIntent());
|
||||
|
||||
// Set callback
|
||||
PendingIntent callback = PendingIntent.getBroadcast(mTargetContext, 0,
|
||||
new Intent(mCallbackAction), PendingIntent.FLAG_ONE_SHOT);
|
||||
mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent(
|
||||
RequestPinItemActivity.class, "setCallback").putExtra(
|
||||
RequestPinItemActivity.EXTRA_PARAM + "0", callback));
|
||||
|
||||
for (Intent command : commandIntents) {
|
||||
mTargetContext.sendBroadcast(command);
|
||||
}
|
||||
|
||||
// call the requested method to start the flow
|
||||
mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent(
|
||||
RequestPinItemActivity.class, activityMethod));
|
||||
final AddToHomeScreenPrompt addToHomeScreenPrompt = mLauncher.getAddToHomeScreenPrompt();
|
||||
|
||||
// Accept confirmation:
|
||||
BlockingBroadcastReceiver resultReceiver = new BlockingBroadcastReceiver(mCallbackAction);
|
||||
addToHomeScreenPrompt.addAutomatically();
|
||||
Intent result = resultReceiver.blockingGetIntent();
|
||||
assertNotNull(result);
|
||||
mAppWidgetId = result.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
|
||||
if (isWidget) {
|
||||
assertNotSame(-1, mAppWidgetId);
|
||||
}
|
||||
|
||||
// Go back to home
|
||||
mLauncher.pressHome();
|
||||
Wait.atMost(null, new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition for for an item
|
||||
*/
|
||||
private class ItemSearchCondition implements Condition {
|
||||
|
||||
private final ItemOperator mOp;
|
||||
|
||||
ItemSearchCondition(ItemOperator op) {
|
||||
mOp = op;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrue() throws Throwable {
|
||||
return mMainThreadExecutor.submit(mActivityMonitor.itemExists(mOp)).get();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user