[automerger skipped] Merge "[DO NOT MERGE] Disable swipe down to pull noti shade for Q Bug: 136293958" into ub-launcher3-qt-dev
am: d59aefada0 -s ours
am skip reason: subject contains skip directive
Change-Id: Idca2ab4432d9df08c98f493db8bd19b8f790e653
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index a3c2e3c..820a8df 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -164,6 +164,12 @@
}
}
+ if (FeatureFlags.PULL_DOWN_STATUS_BAR
+ && !launcher.getDeviceProfile().isMultiWindowMode
+ && !launcher.getDeviceProfile().isVerticalBarLayout()) {
+ list.add(new StatusBarTouchController(launcher));
+ }
+
list.add(new LauncherTaskViewController(launcher));
return list.toArray(new TouchController[list.size()]);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 53da0f9..bf9d531 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -43,6 +43,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.Region;
@@ -74,6 +76,8 @@
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.logging.EventLogArray;
import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.UiThreadHelper;
import com.android.quickstep.SysUINavigationMode.Mode;
@@ -91,6 +95,7 @@
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.BackgroundExecutor;
import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -152,6 +157,7 @@
.asInterface(bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::initInputMonitor);
MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::onSystemUiProxySet);
+ MAIN_THREAD_EXECUTOR.execute(() -> preloadOverview(true /* fromInit */));
}
@Override
@@ -502,6 +508,9 @@
}
private void onInputEvent(InputEvent ev) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.EVENTS_TO_OVERVIEW_MISSING_TAG, "onInputEvent " + ev);
+ }
if (!(ev instanceof MotionEvent)) {
Log.e(TAG, "Unknown event " + ev);
return;
@@ -684,6 +693,54 @@
}
}
+ private void preloadOverview(boolean fromInit) {
+ if (!mIsUserUnlocked) {
+ return;
+ }
+
+ final ActivityControlHelper<BaseDraggingActivity> activityControl =
+ mOverviewComponentObserver.getActivityControlHelper();
+ if (activityControl.getCreatedActivity() == null) {
+ // Make sure that UI states will be initialized.
+ activityControl.createActivityInitListener((activity, wasVisible) -> {
+ AppLaunchTracker.INSTANCE.get(activity);
+ return false;
+ }).register();
+ } else if (fromInit) {
+ // The activity has been created before the initialization of overview service. It is
+ // usually happens when booting or launcher is the top activity, so we should already
+ // have the latest state.
+ return;
+ }
+
+ // Pass null animation handler to indicate this start is preload.
+ BackgroundExecutor.get().submit(
+ () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
+ mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState(),
+ null /* assistDataReceiver */, null /* animationHandler */,
+ null /* resultCallback */, null /* resultCallbackHandler */));
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ if (!mIsUserUnlocked) {
+ return;
+ }
+ final ActivityControlHelper activityControl =
+ mOverviewComponentObserver.getActivityControlHelper();
+ final BaseDraggingActivity activity = activityControl.getCreatedActivity();
+ if (activity == null || activity.isStarted()) {
+ // We only care about the existing background activity.
+ return;
+ }
+ if (mOverviewComponentObserver.canHandleConfigChanges(activity.getComponentName(),
+ activity.getResources().getConfiguration().diff(newConfig))) {
+ return;
+ }
+
+ preloadOverview(false /* fromInit */);
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] rawArgs) {
if (rawArgs.length > 0 && Utilities.IS_DEBUG_DEVICE) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index 56db209..f2e53bb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -19,6 +19,7 @@
import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -26,6 +27,7 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Utilities;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.OverviewCallbacks;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -80,6 +82,9 @@
@Override
public void onMotionEvent(MotionEvent ev) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.EVENTS_TO_OVERVIEW_MISSING_TAG, "onMotionEvent " + ev);
+ }
if (!mProxyTouch) {
return;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index a98df0f..6a9abd5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -473,6 +473,11 @@
}
@Override
+ protected boolean shouldBlockGestures(MotionEvent ev) {
+ return Utilities.shouldDisableGestures(ev);
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
final int x = (int) ev.getX();
@@ -783,6 +788,7 @@
unloadVisibleTaskData();
setCurrentPage(0);
mDwbToastShown = false;
+ mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, 0);
}
public @Nullable TaskView getRunningTaskView() {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
index fee1820..f5ba372 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
@@ -17,17 +17,22 @@
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
+import static android.view.MotionEvent.ACTION_CANCEL;
+import android.graphics.PointF;
import android.os.RemoteException;
import android.util.Log;
+import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
+import android.view.Window;
+import android.view.WindowManager;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.touch.TouchEventTranslator;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.RecentsModel;
import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -36,18 +41,29 @@
/**
* TouchController for handling touch events that get sent to the StatusBar. Once the
- * Once the event delta y passes the touch slop, the events start getting forwarded.
+ * Once the event delta mDownY passes the touch slop, the events start getting forwarded.
* All events are offset by initial Y value of the pointer.
*/
public class StatusBarTouchController implements TouchController {
private static final String TAG = "StatusBarController";
+ /**
+ * Window flag: Enable touches to slide out of a window into neighboring
+ * windows in mid-gesture instead of being captured for the duration of
+ * the gesture.
+ *
+ * This flag changes the behavior of touch focus for this window only.
+ * Touches can slide out of the window but they cannot necessarily slide
+ * back in (unless the other window with touch focus permits it).
+ */
+ private static final int FLAG_SLIPPERY = 0x20000000;
+
protected final Launcher mLauncher;
- protected final TouchEventTranslator mTranslator;
private final float mTouchSlop;
private ISystemUiProxy mSysUiProxy;
private int mLastAction;
+ private final SparseArray<PointF> mDownEvents;
/* If {@code false}, this controller should not handle the input {@link MotionEvent}.*/
private boolean mCanIntercept;
@@ -56,7 +72,7 @@
mLauncher = l;
// Guard against TAPs by increasing the touch slop.
mTouchSlop = 2 * ViewConfiguration.get(l).getScaledTouchSlop();
- mTranslator = new TouchEventTranslator((MotionEvent ev)-> dispatchTouchEvent(ev));
+ mDownEvents = new SparseArray<>();
}
@Override
@@ -64,7 +80,6 @@
writer.println(prefix + "mCanIntercept:" + mCanIntercept);
writer.println(prefix + "mLastAction:" + MotionEvent.actionToString(mLastAction));
writer.println(prefix + "mSysUiProxy available:" + (mSysUiProxy != null));
-
}
private void dispatchTouchEvent(MotionEvent ev) {
@@ -81,26 +96,31 @@
@Override
public final boolean onControllerInterceptTouchEvent(MotionEvent ev) {
int action = ev.getActionMasked();
+ int idx = ev.getActionIndex();
+ int pid = ev.getPointerId(idx);
if (action == ACTION_DOWN) {
mCanIntercept = canInterceptTouch(ev);
if (!mCanIntercept) {
return false;
}
- mTranslator.reset();
- mTranslator.setDownParameters(0, ev);
+ mDownEvents.put(pid, new PointF(ev.getX(), ev.getY()));
} else if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
- // Check!! should only set it only when threshold is not entered.
- mTranslator.setDownParameters(ev.getActionIndex(), ev);
+ // Check!! should only set it only when threshold is not entered.
+ mDownEvents.put(pid, new PointF(ev.getX(idx), ev.getY(idx)));
}
if (!mCanIntercept) {
return false;
}
if (action == ACTION_MOVE) {
- float dy = ev.getY() - mTranslator.getDownY();
- float dx = ev.getX() - mTranslator.getDownX();
- if (dy > mTouchSlop && dy > Math.abs(dx)) {
- mTranslator.dispatchDownEvents(ev);
- mTranslator.processMotionEvent(ev);
+ float dy = ev.getY(idx) - mDownEvents.get(pid).y;
+ float dx = ev.getX(idx) - mDownEvents.get(pid).x;
+ // Currently input dispatcher will not do touch transfer if there are more than
+ // one touch pointer. Hence, even if slope passed, only set the slippery flag
+ // when there is single touch event. (context: InputDispatcher.cpp line 1445)
+ if (dy > mTouchSlop && dy > Math.abs(dx) && ev.getPointerCount() == 1) {
+ ev.setAction(ACTION_DOWN);
+ dispatchTouchEvent(ev);
+ setWindowSlippery(true);
return true;
}
if (Math.abs(dx) > mTouchSlop) {
@@ -110,13 +130,27 @@
return false;
}
-
@Override
public final boolean onControllerTouchEvent(MotionEvent ev) {
- mTranslator.processMotionEvent(ev);
+ if (ev.getAction() == ACTION_UP || ev.getAction() == ACTION_CANCEL) {
+ dispatchTouchEvent(ev);
+ setWindowSlippery(false);
+ return true;
+ }
return true;
}
+ private void setWindowSlippery(boolean enable) {
+ Window w = mLauncher.getWindow();
+ WindowManager.LayoutParams wlp = w.getAttributes();
+ if (enable) {
+ wlp.flags |= FLAG_SLIPPERY;
+ } else {
+ wlp.flags &= ~FLAG_SLIPPERY;
+ }
+ w.setAttributes(wlp);
+ }
+
private boolean canInterceptTouch(MotionEvent ev) {
if (!mLauncher.isInState(LauncherState.NORMAL) ||
AbstractFloatingView.getTopOpenViewWithType(mLauncher,
@@ -132,4 +166,4 @@
mSysUiProxy = RecentsModel.INSTANCE.get(mLauncher).getSystemUiProxy();
return mSysUiProxy != null;
}
-}
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 0738aff..4a2ed3a 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -29,11 +29,15 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.util.SparseIntArray;
import com.android.systemui.shared.system.PackageManagerWrapper;
import java.util.ArrayList;
+import java.util.Objects;
/**
* Class to keep track of the current overview component based off user preferences and app updates
@@ -53,22 +57,41 @@
}
};
private final Context mContext;
- private final ComponentName mMyHomeComponent;
+ private final Intent mCurrentHomeIntent;
+ private final Intent mMyHomeIntent;
+ private final Intent mFallbackIntent;
+ private final SparseIntArray mConfigChangesMap = new SparseIntArray();
private String mUpdateRegisteredPackage;
private ActivityControlHelper mActivityControlHelper;
private Intent mOverviewIntent;
- private Intent mHomeIntent;
private int mSystemUiStateFlags;
private boolean mIsHomeAndOverviewSame;
+ private boolean mIsDefaultHome;
public OverviewComponentObserver(Context context) {
mContext = context;
- Intent myHomeIntent = new Intent(Intent.ACTION_MAIN)
+ mCurrentHomeIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
- .setPackage(mContext.getPackageName());
- ResolveInfo info = context.getPackageManager().resolveActivity(myHomeIntent, 0);
- mMyHomeComponent = new ComponentName(context.getPackageName(), info.activityInfo.name);
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mMyHomeIntent = new Intent(mCurrentHomeIntent).setPackage(mContext.getPackageName());
+ ResolveInfo info = context.getPackageManager().resolveActivity(mMyHomeIntent, 0);
+ ComponentName myHomeComponent =
+ new ComponentName(context.getPackageName(), info.activityInfo.name);
+ mMyHomeIntent.setComponent(myHomeComponent);
+ mConfigChangesMap.append(myHomeComponent.hashCode(), info.activityInfo.configChanges);
+
+ ComponentName fallbackComponent = new ComponentName(mContext, RecentsActivity.class);
+ mFallbackIntent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .setComponent(fallbackComponent)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ try {
+ ActivityInfo fallbackInfo = context.getPackageManager().getActivityInfo(
+ mFallbackIntent.getComponent(), 0 /* flags */);
+ mConfigChangesMap.append(fallbackComponent.hashCode(), fallbackInfo.configChanges);
+ } catch (PackageManager.NameNotFoundException ignored) { /* Impossible */ }
mContext.registerReceiver(mUserPreferenceChangeReceiver,
new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED));
@@ -92,17 +115,14 @@
ComponentName defaultHome = PackageManagerWrapper.getInstance()
.getHomeActivities(new ArrayList<>());
- final String overviewIntentCategory;
- ComponentName overviewComponent;
- mHomeIntent = null;
-
- if ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0 &&
- (defaultHome == null || mMyHomeComponent.equals(defaultHome))) {
+ mIsDefaultHome = Objects.equals(mMyHomeIntent.getComponent(), defaultHome);
+ if ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0
+ && (defaultHome == null || mIsDefaultHome)) {
// User default home is same as out home app. Use Overview integrated in Launcher.
- overviewComponent = mMyHomeComponent;
mActivityControlHelper = new LauncherActivityControllerHelper();
mIsHomeAndOverviewSame = true;
- overviewIntentCategory = Intent.CATEGORY_HOME;
+ mOverviewIntent = mMyHomeIntent;
+ mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent());
if (mUpdateRegisteredPackage != null) {
// Remove any update listener as we don't care about other packages.
@@ -111,14 +131,11 @@
}
} else {
// The default home app is a different launcher. Use the fallback Overview instead.
- overviewComponent = new ComponentName(mContext, RecentsActivity.class);
mActivityControlHelper = new FallbackActivityControllerHelper();
mIsHomeAndOverviewSame = false;
- overviewIntentCategory = Intent.CATEGORY_DEFAULT;
+ mOverviewIntent = mFallbackIntent;
+ mCurrentHomeIntent.setComponent(defaultHome);
- mHomeIntent = new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setComponent(defaultHome);
// User's default home app can change as a result of package updates of this app (such
// as uninstalling the app or removing the "Launcher" feature in an update).
// Listen for package updates of this app (and remove any previously attached
@@ -138,14 +155,6 @@
ACTION_PACKAGE_REMOVED));
}
}
-
- mOverviewIntent = new Intent(Intent.ACTION_MAIN)
- .addCategory(overviewIntentCategory)
- .setComponent(overviewComponent)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- if (mHomeIntent == null) {
- mHomeIntent = mOverviewIntent;
- }
}
/**
@@ -161,6 +170,32 @@
}
/**
+ * @return {@code true} if the overview component is able to handle the configuration changes.
+ */
+ boolean canHandleConfigChanges(ComponentName component, int changes) {
+ final int orientationChange =
+ ActivityInfo.CONFIG_ORIENTATION | ActivityInfo.CONFIG_SCREEN_SIZE;
+ if ((changes & orientationChange) == orientationChange) {
+ // This is just an approximate guess for simple orientation change because the changes
+ // may contain non-public bits (e.g. window configuration).
+ return true;
+ }
+
+ int configMask = mConfigChangesMap.get(component.hashCode());
+ return configMask != 0 && (~configMask & changes) == 0;
+ }
+
+ /**
+ * Get the intent for overview activity. It is used when lockscreen is shown and home was died
+ * in background, we still want to restart the one that will be used after unlock.
+ *
+ * @return the overview intent
+ */
+ Intent getOverviewIntentIgnoreSysUiState() {
+ return mIsDefaultHome ? mMyHomeIntent : mOverviewIntent;
+ }
+
+ /**
* Get the current intent for going to the overview activity.
*
* @return the overview intent
@@ -173,7 +208,7 @@
* Get the current intent for going to the home activity.
*/
public Intent getHomeIntent() {
- return mHomeIntent;
+ return mCurrentHomeIntent;
}
/**
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
index 7bfa9a0..befeee0 100644
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -15,7 +15,6 @@
*/
package com.android.quickstep;
-import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.UserManager;
@@ -23,29 +22,17 @@
import com.android.launcher3.BuildConfig;
import com.android.launcher3.MainProcessInitializer;
-import com.android.launcher3.Utilities;
import com.android.systemui.shared.system.ThreadedRendererCompat;
@SuppressWarnings("unused")
public class QuickstepProcessInitializer extends MainProcessInitializer {
private static final String TAG = "QuickstepProcessInitializer";
- private static final int HEAP_LIMIT_MB = 250;
public QuickstepProcessInitializer(Context context) { }
@Override
protected void init(Context context) {
- if (Utilities.IS_DEBUG_DEVICE) {
- try {
- // Trigger a heap dump if the PSS reaches beyond the target heap limit
- final ActivityManager am = context.getSystemService(ActivityManager.class);
- am.setWatchHeapLimit(HEAP_LIMIT_MB * 1024 * 1024);
- } catch (SecurityException e) {
- // Do nothing
- }
- }
-
// Workaround for b/120550382, an external app can cause the launcher process to start for
// a work profile user which we do not support. Disable the application immediately when we
// detect this to be the case.
diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
index 0c5a6f5..ec3d49a 100644
--- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
@@ -51,7 +51,7 @@
mLauncher.pressHome();
final DigitalWellBeingToast toast = getToast();
- assertTrue("Toast is not visible", toast.hasLimit());
+ waitForLauncherCondition("Toast is not visible", launcher -> toast.hasLimit());
assertEquals("Toast text: ", "5 minutes left today", toast.getText());
// Unset time limit for app.
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index 2111e2c..c5b560c 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -25,6 +25,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.Launcher;
+import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.util.RaceConditionReproducer;
import com.android.quickstep.NavigationModeSwitchRule.Mode;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
@@ -79,6 +80,8 @@
@Test
@NavigationModeSwitch
public void testStressPressHome() {
+ if (LauncherInstrumentation.isAvd()) return; // b/136278866
+
for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
// Destroy Launcher activity.
closeLauncherActivity();
@@ -91,6 +94,8 @@
@Test
@NavigationModeSwitch
public void testStressSwipeToOverview() {
+ if (LauncherInstrumentation.isAvd()) return; // b/136278866
+
for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
// Destroy Launcher activity.
closeLauncherActivity();
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 2eeb132..f8e4c9d 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -16,7 +16,6 @@
package com.android.launcher3;
-import static com.android.launcher3.Utilities.shouldDisableGestures;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
@@ -847,7 +846,7 @@
*/
// Skip touch handling if there are no pages to swipe
- if (getChildCount() <= 0 || shouldDisableGestures(ev)) return false;
+ if (getChildCount() <= 0 || shouldBlockGestures(ev)) return false;
acquireVelocityTrackerAndAddMovement(ev);
@@ -1092,10 +1091,14 @@
mAllowOverScroll = enable;
}
+ protected boolean shouldBlockGestures(MotionEvent ev) {
+ return false;
+ }
+
@Override
public boolean onTouchEvent(MotionEvent ev) {
// Skip touch handling if there are no pages to swipe
- if (getChildCount() <= 0 || shouldDisableGestures(ev)) return false;
+ if (getChildCount() <= 0 || shouldBlockGestures(ev)) return false;
acquireVelocityTrackerAndAddMovement(ev);
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 6ffc2d9..3774042 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -78,4 +78,5 @@
public static final String NO_START_TAG = "b/132900132";
public static final String NO_START_TASK_TAG = "b/133765434";
public static final String NO_OVERVIEW_EVENT_TAG = "b/134532571";
+ public static final String EVENTS_TO_OVERVIEW_MISSING_TAG = "b/133867119";
}
diff --git a/src/com/android/launcher3/touch/TouchEventTranslator.java b/src/com/android/launcher3/touch/TouchEventTranslator.java
deleted file mode 100644
index 3fcda90..0000000
--- a/src/com/android/launcher3/touch/TouchEventTranslator.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * 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.launcher3.touch;
-
-import android.graphics.PointF;
-import android.util.Log;
-import android.util.Pair;
-import android.util.SparseArray;
-import android.view.MotionEvent;
-import android.view.MotionEvent.PointerCoords;
-import android.view.MotionEvent.PointerProperties;
-
-import java.util.function.Consumer;
-
-/**
- * To minimize the size of the MotionEvent, historic events are not copied and passed via the
- * listener.
- */
-public class TouchEventTranslator {
-
- private static final String TAG = "TouchEventTranslator";
- private static final boolean DEBUG = false;
-
- private class DownState {
- long timeStamp;
- float downX;
- float downY;
- public DownState(long timeStamp, float downX, float downY) {
- this.timeStamp = timeStamp;
- this.downX = downX;
- this.downY = downY;
- }
- };
- private final DownState ZERO = new DownState(0, 0f, 0f);
-
- private final Consumer<MotionEvent> mListener;
-
- private final SparseArray<DownState> mDownEvents;
- private final SparseArray<PointF> mFingers;
-
- private final SparseArray<Pair<PointerProperties[], PointerCoords[]>> mCache;
-
- public TouchEventTranslator(Consumer<MotionEvent> listener) {
- mDownEvents = new SparseArray<>();
- mFingers = new SparseArray<>();
- mCache = new SparseArray<>();
-
- mListener = listener;
- }
-
- public void reset() {
- mDownEvents.clear();
- mFingers.clear();
- }
-
- public float getDownX() {
- return mDownEvents.get(0).downX;
- }
-
- public float getDownY() {
- return mDownEvents.get(0).downY;
- }
-
- public void setDownParameters(int idx, MotionEvent e) {
- DownState ev = new DownState(e.getEventTime(), e.getX(idx), e.getY(idx));
- mDownEvents.append(idx, ev);
- }
-
- public void dispatchDownEvents(MotionEvent ev) {
- for(int i = 0; i < ev.getPointerCount() && i < mDownEvents.size(); i++) {
- int pid = ev.getPointerId(i);
- put(pid, i, ev.getX(i), 0, mDownEvents.get(i).timeStamp, ev);
- }
- }
-
- public void processMotionEvent(MotionEvent ev) {
- if (DEBUG) {
- printSamples(TAG + " processMotionEvent", ev);
- }
- int index = ev.getActionIndex();
- float x = ev.getX(index);
- float y = ev.getY(index) - mDownEvents.get(index, ZERO).downY;
- switch (ev.getActionMasked()) {
- case MotionEvent.ACTION_POINTER_DOWN:
- int pid = ev.getPointerId(index);
- if(mFingers.get(pid, null) != null) {
- for(int i=0; i < ev.getPointerCount(); i++) {
- pid = ev.getPointerId(i);
- position(pid, x, y);
- }
- generateEvent(ev.getAction(), ev);
- } else {
- put(pid, index, x, y, ev);
- }
- break;
- case MotionEvent.ACTION_MOVE:
- for(int i=0; i < ev.getPointerCount(); i++) {
- pid = ev.getPointerId(i);
- position(pid, x, y);
- }
- generateEvent(ev.getAction(), ev);
- break;
- case MotionEvent.ACTION_POINTER_UP:
- case MotionEvent.ACTION_UP:
- pid = ev.getPointerId(index);
- lift(pid, index, x, y, ev);
- break;
- case MotionEvent.ACTION_CANCEL:
- cancel(ev);
- break;
- default:
- Log.v(TAG, "Didn't process ");
- printSamples(TAG, ev);
-
- }
- }
-
- private TouchEventTranslator put(int id, int index, float x, float y, MotionEvent ev) {
- return put(id, index, x, y, ev.getEventTime(), ev);
- }
-
- private TouchEventTranslator put(int id, int index, float x, float y, long ms, MotionEvent ev) {
- checkFingerExistence(id, false);
- boolean isInitialDown = (mFingers.size() == 0);
-
- mFingers.put(id, new PointF(x, y));
- int n = mFingers.size();
-
- if (mCache.get(n) == null) {
- PointerProperties[] properties = new PointerProperties[n];
- PointerCoords[] coords = new PointerCoords[n];
- for (int i = 0; i < n; i++) {
- properties[i] = new PointerProperties();
- coords[i] = new PointerCoords();
- }
- mCache.put(n, new Pair(properties, coords));
- }
-
- int action;
- if (isInitialDown) {
- action = MotionEvent.ACTION_DOWN;
- } else {
- action = MotionEvent.ACTION_POINTER_DOWN;
- // Set the id of the changed pointer.
- action |= index << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
- }
- generateEvent(action, ms, ev);
- return this;
- }
-
- public TouchEventTranslator position(int id, float x, float y) {
- checkFingerExistence(id, true);
- mFingers.get(id).set(x, y);
- return this;
- }
-
- private TouchEventTranslator lift(int id, int index, MotionEvent ev) {
- checkFingerExistence(id, true);
- boolean isFinalUp = (mFingers.size() == 1);
- int action;
- if (isFinalUp) {
- action = MotionEvent.ACTION_UP;
- } else {
- action = MotionEvent.ACTION_POINTER_UP;
- // Set the id of the changed pointer.
- action |= index << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
- }
- generateEvent(action, ev);
- mFingers.remove(id);
- return this;
- }
-
- private TouchEventTranslator lift(int id, int index, float x, float y, MotionEvent ev) {
- checkFingerExistence(id, true);
- mFingers.get(id).set(x, y);
- return lift(id, index, ev);
- }
-
- public TouchEventTranslator cancel(MotionEvent ev) {
- generateEvent(MotionEvent.ACTION_CANCEL, ev);
- mFingers.clear();
- return this;
- }
-
- private void checkFingerExistence(int id, boolean shouldExist) {
- if (shouldExist != (mFingers.get(id, null) != null)) {
- throw new IllegalArgumentException(
- shouldExist ? "Finger does not exist" : "Finger already exists");
- }
- }
-
-
- /**
- * Used to debug MotionEvents being sent/received.
- */
- public void printSamples(String msg, MotionEvent ev) {
- System.out.printf("%s %s", msg, MotionEvent.actionToString(ev.getActionMasked()));
- final int pointerCount = ev.getPointerCount();
- System.out.printf("#%d/%d", ev.getActionIndex(), pointerCount);
- System.out.printf(" t=%d:", ev.getEventTime());
- for (int p = 0; p < pointerCount; p++) {
- System.out.printf(" id=%d: (%f,%f)",
- ev.getPointerId(p), ev.getX(p), ev.getY(p));
- }
- System.out.println();
- }
-
- private void generateEvent(int action, MotionEvent ev) {
- generateEvent(action, ev.getEventTime(), ev);
- }
-
- private void generateEvent(int action, long ms, MotionEvent ev) {
- Pair<PointerProperties[], PointerCoords[]> state = getFingerState();
- MotionEvent event = MotionEvent.obtain(
- mDownEvents.get(0).timeStamp,
- ms,
- action,
- state.first.length,
- state.first,
- state.second,
- ev.getMetaState(),
- ev.getButtonState() /* buttonState */,
- ev.getXPrecision() /* xPrecision */,
- ev.getYPrecision() /* yPrecision */,
- ev.getDeviceId(),
- ev.getEdgeFlags(),
- ev.getSource(),
- ev.getFlags() /* flags */);
- if (DEBUG) {
- printSamples(TAG + " generateEvent", event);
- }
- if (event.getPointerId(event.getActionIndex()) < 0) {
- printSamples(TAG + "generateEvent", event);
- throw new IllegalStateException(event.getActionIndex() + " not found in MotionEvent");
- }
- mListener.accept(event);
- event.recycle();
- }
-
- /**
- * Returns the description of the fingers' state expected by MotionEvent.
- */
- private Pair<PointerProperties[], PointerCoords[]> getFingerState() {
- int nFingers = mFingers.size();
-
- Pair<PointerProperties[], PointerCoords[]> result = mCache.get(nFingers);
- PointerProperties[] properties = result.first;
- PointerCoords[] coordinates = result.second;
-
- int index = 0;
- for (int i = 0; i < mFingers.size(); i++) {
- int id = mFingers.keyAt(i);
- PointF location = mFingers.get(id);
-
- PointerProperties property = properties[i];
- property.id = id;
- property.toolType = MotionEvent.TOOL_TYPE_FINGER;
- properties[index] = property;
-
- PointerCoords coordinate = coordinates[i];
- coordinate.x = location.x;
- coordinate.y = location.y;
- coordinate.pressure = 1.0f;
- coordinates[index] = coordinate;
-
- index++;
- }
- return mCache.get(nFingers);
- }
-}
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index c1ba780..53686ca 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -152,6 +152,9 @@
}
private TouchController findControllerToHandleTouch(MotionEvent ev) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.EVENTS_TO_OVERVIEW_MISSING_TAG, "findControllerToHandleTouch " + ev);
+ }
if (shouldDisableGestures(ev)) return null;
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
@@ -314,6 +317,9 @@
* Proxies the touch events to the gesture handlers
*/
public boolean proxyTouchEvent(MotionEvent ev) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.EVENTS_TO_OVERVIEW_MISSING_TAG, "proxyTouchEvent " + ev);
+ }
boolean handled;
if (mProxyTouchController != null) {
handled = mProxyTouchController.onControllerTouchEvent(ev);
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 4a0ca5c..e39fc76 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -20,7 +20,6 @@
import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import static java.lang.System.exit;
@@ -38,10 +37,7 @@
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.android.launcher3.Launcher;
@@ -50,12 +46,12 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherState;
import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.ResourceUtils;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.tapl.TestHelpers;
+import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.FailureWatcher;
import com.android.launcher3.util.rule.LauncherActivityRule;
@@ -86,8 +82,7 @@
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;
+ public static final long DEFAULT_UI_TIMEOUT = 60000; // b/136278866
private static final String TAG = "AbstractLauncherUiTest";
protected MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
@@ -116,6 +111,23 @@
public ShellCommandRule mDisableHeadsUpNotification =
ShellCommandRule.disableHeadsUpNotification();
+ protected void clearPackageData(String pkg) throws IOException, InterruptedException {
+ final CountDownLatch count = new CountDownLatch(2);
+ final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ count.countDown();
+ }
+ };
+ mTargetContext.registerReceiver(broadcastReceiver,
+ PackageManagerHelper.getPackageFilter(pkg,
+ Intent.ACTION_PACKAGE_RESTARTED, Intent.ACTION_PACKAGE_DATA_CLEARED));
+
+ mDevice.executeShellCommand("pm clear " + pkg);
+ assertTrue(pkg + " didn't restart", count.await(10, TimeUnit.SECONDS));
+ mTargetContext.unregisterReceiver(broadcastReceiver);
+ }
+
// Annotation for tests that need to be run in portrait and landscape modes.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@@ -170,38 +182,13 @@
}
}
- protected void clearLauncherData() throws IOException {
+ protected void clearLauncherData() throws IOException, InterruptedException {
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);
+ clearPackageData(mDevice.getLauncherPackageName());
}
}
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index c3168f8..4dab44f 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -216,7 +216,8 @@
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 "
+ "Launcher activity is the top activity; expecting another activity to be the "
+ + "top "
+ "one",
test.isInBackground(launcher)));
} finally {
@@ -304,11 +305,8 @@
switchToAllApps();
allApps.freeze();
try {
- allApps.
- getAppIcon(APP_NAME).
- dragToWorkspace().
- getWorkspaceAppIcon(APP_NAME).
- launch(getAppPackageName());
+ allApps.getAppIcon(APP_NAME).dragToWorkspace();
+ mLauncher.getWorkspace().getWorkspaceAppIcon(APP_NAME).launch(getAppPackageName());
} finally {
allApps.unfreeze();
}
@@ -335,13 +333,8 @@
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());
+ menuItem.dragToWorkspace();
+ mLauncher.getWorkspace().getWorkspaceAppIcon(shortcutName).launch(getAppPackageName());
} finally {
allApps.unfreeze();
}
diff --git a/tests/src/com/android/launcher3/ui/TestViewHelpers.java b/tests/src/com/android/launcher3/ui/TestViewHelpers.java
index d13d319..d0df664 100644
--- a/tests/src/com/android/launcher3/ui/TestViewHelpers.java
+++ b/tests/src/com/android/launcher3/ui/TestViewHelpers.java
@@ -18,28 +18,15 @@
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.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.R;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.testcomponent.AppWidgetNoConfig;
import com.android.launcher3.testcomponent.AppWidgetWithConfig;
@@ -50,21 +37,6 @@
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.
*
@@ -91,92 +63,6 @@
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);
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 5eb5f19..dc72bda 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -27,20 +27,18 @@
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.UiObject2;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.Workspace;
+import com.android.launcher3.tapl.Widgets;
import com.android.launcher3.testcomponent.WidgetConfigActivity;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TestViewHelpers;
import com.android.launcher3.util.Condition;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.ShellCommandRule;
-import com.android.launcher3.widget.WidgetCell;
import org.junit.Before;
import org.junit.Ignore;
@@ -71,7 +69,6 @@
}
@Test
- // Convert test to TAPL b/131116002
public void testWidgetConfig() throws Throwable {
runTest(false, true);
}
@@ -83,7 +80,6 @@
}
@Test
- // Convert test to TAPL b/131116002
public void testConfigCancelled() throws Throwable {
runTest(false, false);
}
@@ -104,15 +100,13 @@
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);
+ final Widgets widgets = mLauncher.getWorkspace().openAllWidgets();
// 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);
+ widgets.
+ getWidget(mWidgetInfo.getLabel(mTargetContext.getPackageManager())).
+ dragToWorkspace();
// Widget id for which the config activity was opened
mWidgetId = monitor.getWidgetId();
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 0061568..4529a80 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -19,20 +19,12 @@
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.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TestViewHelpers;
-import com.android.launcher3.util.Condition;
-import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.ShellCommandRule;
-import com.android.launcher3.widget.WidgetCell;
import org.junit.Ignore;
import org.junit.Rule;
@@ -61,7 +53,6 @@
performTest();
}
- // Convert to TAPL b/131116002
private void performTest() throws Throwable {
clearHomescreen();
mActivityMonitor.startLauncher();
@@ -69,22 +60,15 @@
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);
+ mLauncher.
+ getWorkspace().
+ openAllWidgets().
+ getWidget(widgetInfo.getLabel(mTargetContext.getPackageManager())).
+ dragToWorkspace();
- // 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 &&
+ assertTrue(mActivityMonitor.itemExists(
+ (info, view) -> info instanceof LauncherAppWidgetInfo &&
((LauncherAppWidgetInfo) info).providerName.getClassName().equals(
- widgetInfo.provider.getClassName());
- }
- }).call());
+ widgetInfo.provider.getClassName())).call());
}
}
diff --git a/tests/tapl/AndroidManifest.xml b/tests/tapl/AndroidManifest.xml
index 0207e2b..1065446 100644
--- a/tests/tapl/AndroidManifest.xml
+++ b/tests/tapl/AndroidManifest.xml
@@ -23,4 +23,6 @@
>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <uses-permission android:name="android.permission.READ_LOGS"/>
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
</manifest>
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 9ff354a..0ac5e9f 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -181,6 +181,7 @@
* Flings backward (up) and waits the fling's end.
*/
public void flingBackward() {
+ mLauncher.getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
try (LauncherInstrumentation.Closable c =
mLauncher.addContextLayer("want to fling backward in all apps")) {
final UiObject2 allAppsContainer = verifyActiveContainer();
@@ -189,6 +190,7 @@
allAppsContainer, Direction.UP, 1, new Rect(0, mHeight / 2, 0, 0), 10);
verifyActiveContainer();
}
+ mLauncher.getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
}
/**
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index d4bdafa..04b8019 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -68,7 +68,7 @@
/**
* Drags an object to the center of homescreen.
*/
- public Workspace dragToWorkspace() {
+ public void dragToWorkspace() {
final Point launchableCenter = getObject().getVisibleCenter();
final Point displaySize = mLauncher.getRealDisplaySize();
final int width = displaySize.x / 2;
@@ -80,10 +80,6 @@
launchableCenter.x - width / 2 : launchableCenter.x + width / 2,
displaySize.y / 2),
getLongPressIndicator());
- try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
- "dragged launchable to workspace")) {
- return new Workspace(mLauncher);
- }
}
protected abstract String getLongPressIndicator();
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index a7e6336..11b0665 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -39,6 +39,7 @@
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.os.DropBoxManager;
import android.os.Parcelable;
import android.os.SystemClock;
import android.text.TextUtils;
@@ -263,10 +264,122 @@
}
}
+ private String getAnomalyMessage() {
+ final UiObject2 object = mDevice.findObject(By.res("android", "alertTitle"));
+ if (object != null) {
+ return "System alert popup is visible: " + object.getText();
+ }
+
+ if (hasSystemUiObject("keyguard_status_view")) return "Phone is locked";
+
+ if (!mDevice.hasObject(By.textStartsWith(""))) return "Screen is empty";
+
+ return null;
+ }
+
+ private String getVisibleStateMessage() {
+ if (hasLauncherObject(WIDGETS_RES_ID)) return "Widgets";
+ if (hasLauncherObject(OVERVIEW_RES_ID)) return "Overview";
+ if (hasLauncherObject(WORKSPACE_RES_ID)) return "Workspace";
+ if (hasLauncherObject(APPS_RES_ID)) return "AllApps";
+ return "Background";
+ }
+
+ private static String truncateCrash(String text, int maxLines) {
+ String[] lines = text.split("\\r?\\n");
+ StringBuilder ret = new StringBuilder();
+ for (int i = 0; i < maxLines && i < lines.length; i++) {
+ ret.append(lines[i]);
+ ret.append('\n');
+ }
+ if (lines.length > maxLines) {
+ ret.append("... ");
+ ret.append(lines.length - maxLines);
+ ret.append(" more lines truncated ...\n");
+ }
+ return ret.toString();
+ }
+
+ private String checkCrash(String label) {
+ DropBoxManager dropbox = (DropBoxManager) getContext().getSystemService(
+ Context.DROPBOX_SERVICE);
+ Assert.assertNotNull("Unable access the DropBoxManager service", dropbox);
+
+ long timestamp = 0;
+ DropBoxManager.Entry entry;
+ int crashCount = 0;
+ StringBuilder errorDetails = new StringBuilder();
+ while (null != (entry = dropbox.getNextEntry(label, timestamp))) {
+ String dropboxSnippet;
+ try {
+ dropboxSnippet = entry.getText(4096);
+ } finally {
+ entry.close();
+ }
+
+ crashCount++;
+ errorDetails.append(label);
+ errorDetails.append(": ");
+ errorDetails.append(truncateCrash(dropboxSnippet, 40));
+ errorDetails.append(" ...\n");
+
+ timestamp = entry.getTimeMillis();
+ }
+ Assert.assertEquals(errorDetails.toString(), 0, crashCount);
+ return crashCount > 0 ? errorDetails.toString() : null;
+ }
+
+ private String getSystemHealthMessage() {
+ try {
+ StringBuilder errors = new StringBuilder();
+
+ final String testPackage = getContext().getPackageName();
+ try {
+ mDevice.executeShellCommand("pm grant " + testPackage +
+ " android.permission.READ_LOGS");
+ mDevice.executeShellCommand("pm grant " + testPackage +
+ " android.permission.PACKAGE_USAGE_STATS");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ final String[] labels = {
+ "system_server_crash",
+ "system_server_native_crash",
+ "system_server_anr",
+ };
+
+ for (String label : labels) {
+ final String crash = checkCrash(label);
+ if (crash != null) errors.append(crash);
+ }
+
+ return errors.length() != 0 ? errors.toString() : null;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
private void fail(String message) {
- log("Hierarchy dump for: " + getContextDescription() + message);
+ message = "http://go/tapl : " + getContextDescription() + message;
+
+ final String anomaly = getAnomalyMessage();
+ if (anomaly != null) {
+ message = anomaly + ", which causes:\n" + message;
+ } else {
+ message = message + " (visible state: " + getVisibleStateMessage() + ")";
+ }
+
+ final String systemHealth = getSystemHealthMessage();
+ if (systemHealth != null) {
+ message = message + ", which might be a consequence of system health problems:\n<<<\n"
+ + systemHealth + "\n>>>";
+ }
+
+ log("Hierarchy dump for: " + message);
dumpViewHierarchy();
- Assert.fail("http://go/tapl : " + getContextDescription() + message);
+
+ Assert.fail(message);
}
private String getContextDescription() {
@@ -333,6 +446,17 @@
private UiObject2 verifyContainerType(ContainerType containerType) {
assertEquals("Unexpected display rotation",
mExpectedRotation, mDevice.getDisplayRotation());
+
+ // b/136278866
+ for (int i = 0; i != 100; ++i) {
+ if (getNavigationModeMismatchError() == null) break;
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
final String error = getNavigationModeMismatchError();
assertTrue(error, error == null);
log("verifyContainerType: " + containerType);
@@ -614,12 +738,12 @@
@NonNull
UiObject2 waitForLauncherObject(BySelector selector) {
- return waitForObjectBySelector(selector.pkg(getLauncherPackageName()));
+ return waitForObjectBySelector(By.copy(selector).pkg(getLauncherPackageName()));
}
@NonNull
UiObject2 tryWaitForLauncherObject(BySelector selector, long timeout) {
- return tryWaitForObjectBySelector(selector.pkg(getLauncherPackageName()), timeout);
+ return tryWaitForObjectBySelector(By.copy(selector).pkg(getLauncherPackageName()), timeout);
}
@NonNull
diff --git a/tests/tapl/com/android/launcher3/tapl/Widget.java b/tests/tapl/com/android/launcher3/tapl/Widget.java
index 128789d..1b6d8c4 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widget.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widget.java
@@ -18,7 +18,16 @@
import androidx.test.uiautomator.UiObject2;
-public class Widget {
- Widget(LauncherInstrumentation launcher, UiObject2 widget) {
+/**
+ * Widget in workspace or a widget list.
+ */
+public final class Widget extends Launchable {
+ Widget(LauncherInstrumentation launcher, UiObject2 icon) {
+ super(launcher, icon);
+ }
+
+ @Override
+ protected String getLongPressIndicator() {
+ return "drop_target_bar";
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 94003be..2495933 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -16,6 +16,12 @@
package com.android.launcher3.tapl;
+import static org.junit.Assert.fail;
+
+import android.graphics.Point;
+
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.Direction;
import androidx.test.uiautomator.UiObject2;
@@ -75,4 +81,27 @@
protected LauncherInstrumentation.ContainerType getContainerType() {
return LauncherInstrumentation.ContainerType.WIDGETS;
}
+
+ public Widget getWidget(String label) {
+ final int margin = ResourceUtils.getNavbarSize(
+ ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1;
+ final UiObject2 widgetsContainer = verifyActiveContainer();
+ widgetsContainer.setGestureMargins(0, 0, 0, margin);
+
+ final Point displaySize = mLauncher.getRealDisplaySize();
+
+ int i = 0;
+ final BySelector selector = By.
+ clazz("com.android.launcher3.widget.WidgetCell").
+ hasDescendant(By.text(label));
+
+ for (; ; ) {
+ final UiObject2 widget = mLauncher.tryWaitForLauncherObject(selector, 300);
+ if (widget != null && widget.getVisibleBounds().bottom <= displaySize.y - margin) {
+ return new Widget(mLauncher, widget);
+ }
+ if (++i > 40) fail("Too many attempts");
+ widgetsContainer.scroll(Direction.DOWN, 1f);
+ }
+ }
}