From bb7553017c8f272040d99bbe09a312c1178b2bcb Mon Sep 17 00:00:00 2001 From: Jon Spivack Date: Tue, 10 Aug 2021 18:19:24 -0700 Subject: [PATCH 01/10] Add callback to ImageActionUtils for unresolved intents Adding an optional callback to ImageActionUtils.persistBitmapAndStartActivity allows for external handling of the case in which the given intent cannot be resolved. The main use case is for NIU Actions when the user has chosen an assistant that does not support the feature. TaskOverlayFactoryGo provides a callback that shows a dialog in this case. Various parts of TaskOverlayFactoryGo were also made public for better testability. Bug: 196125662 Bug: 192406446 Bug: 195681795 Test: m -j RunLauncherGoGoogleRoboTests ROBOTEST_FILTER=TaskOverlayFactoryGoTest Change-Id: I64f3a1274bc942a64e964dca20bd4245e336ad9d --- .../quickstep/TaskOverlayFactoryGo.java | 29 ++++++++++++------- .../android/quickstep/ImageActionsApi.java | 13 +++++---- .../quickstep/util/ImageActionUtils.java | 15 ++++++++++ 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java index 6502526dcc..bc38739d57 100644 --- a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java +++ b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java @@ -50,7 +50,6 @@ import androidx.annotation.IntDef; import androidx.annotation.VisibleForTesting; import com.android.launcher3.BaseActivity; -import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.views.ArrowTipView; @@ -76,7 +75,7 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory { public static final String ACTIONS_ERROR_CODE = "niu_actions_app_error_code"; public static final int ERROR_PERMISSIONS_STRUCTURE = 1; public static final int ERROR_PERMISSIONS_SCREENSHOT = 2; - private static final String NIU_ACTIONS_CONFIRMED = "launcher_go.niu_actions_confirmed"; + public static final String NIU_ACTIONS_CONFIRMED = "launcher_go.niu_actions_confirmed"; private static final String ASSIST_SETTINGS_ARGS_BUNDLE = ":settings:show_fragment_args"; private static final String ASSIST_SETTINGS_ARGS_KEY = ":settings:fragment_args_key"; private static final String ASSIST_SETTINGS_PREFERENCE_KEY = "default_assist"; @@ -87,10 +86,11 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory { @Retention(SOURCE) @IntDef({PRIVACY_CONFIRMATION, ASSISTANT_NOT_SELECTED, ASSISTANT_NOT_SUPPORTED}) - private @interface DialogType{} - private static final int PRIVACY_CONFIRMATION = 0; - private static final int ASSISTANT_NOT_SELECTED = 1; - private static final int ASSISTANT_NOT_SUPPORTED = 2; + @VisibleForTesting + public @interface DialogType{} + public static final int PRIVACY_CONFIRMATION = 0; + public static final int ASSISTANT_NOT_SELECTED = 1; + public static final int ASSISTANT_NOT_SUPPORTED = 2; private AssistContentRequester mContentRequester; @@ -211,7 +211,8 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory { Intent intent = createNIUIntent(actionType); // Only add and send the image if the appropriate permissions are held if (mAssistStructurePermitted && mAssistScreenshotPermitted) { - mImageApi.shareAsDataWithExplicitIntent(/* crop */ null, intent); + mImageApi.shareAsDataWithExplicitIntent(/* crop */ null, intent, + () -> showDialog(actionType, ASSISTANT_NOT_SUPPORTED)); } else { // If both permissions are disabled, the structure error code takes priority // The user must enable that one before they can enable screenshots @@ -301,7 +302,6 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory { mImageApi = imageActionsApi; } - // TODO (b/192406446): Test that these dialogs are shown at the appropriate times private void showDialog(String action, @DialogType int type) { switch (type) { case PRIVACY_CONFIRMATION: @@ -334,7 +334,7 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory { int bodyTextID, int button1TextID, View.OnClickListener button1Callback, int button2TextID, View.OnClickListener button2Callback) { - BaseDraggingActivity activity = BaseActivity.fromContext(getActionsView().getContext()); + BaseActivity activity = BaseActivity.fromContext(getActionsView().getContext()); LayoutInflater inflater = LayoutInflater.from(activity); View view = inflater.inflate(R.layout.niu_actions_dialog, /* root */ null); @@ -368,6 +368,11 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory { mDialog.cancel(); } + @VisibleForTesting + public OverlayDialogGo getDialog() { + return mDialog; + } + private void onDialogClickSettings(View v) { mDialog.dismiss(); @@ -401,7 +406,11 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory { } } - private static final class OverlayDialogGo extends AlertDialog { + /** + * Basic modal dialog for various user prompts + */ + @VisibleForTesting + public static final class OverlayDialogGo extends AlertDialog { private final String mAction; private final @DialogType int mType; diff --git a/quickstep/src/com/android/quickstep/ImageActionsApi.java b/quickstep/src/com/android/quickstep/ImageActionsApi.java index 8cb64c24b8..154848d224 100644 --- a/quickstep/src/com/android/quickstep/ImageActionsApi.java +++ b/quickstep/src/com/android/quickstep/ImageActionsApi.java @@ -64,20 +64,23 @@ public class ImageActionsApi { */ @UiThread public void shareWithExplicitIntent(@Nullable Rect crop, Intent intent) { - addImageAndSendIntent(crop, intent, false); + addImageAndSendIntent(crop, intent, false, null /* exceptionCallback */); } /** * Share the image this api was constructed with using the provided intent. The implementation * should set the intent's data field to the URI pointing to the image. + * @param exceptionCallback An optional callback to be called when the intent can't be resolved */ @UiThread - public void shareAsDataWithExplicitIntent(@Nullable Rect crop, Intent intent) { - addImageAndSendIntent(crop, intent, true); + public void shareAsDataWithExplicitIntent(@Nullable Rect crop, Intent intent, + @Nullable Runnable exceptionCallback) { + addImageAndSendIntent(crop, intent, true, exceptionCallback); } @UiThread - private void addImageAndSendIntent(@Nullable Rect crop, Intent intent, boolean setData) { + private void addImageAndSendIntent(@Nullable Rect crop, Intent intent, boolean setData, + @Nullable Runnable exceptionCallback) { if (mBitmapSupplier.get() == null) { Log.e(TAG, "No snapshot available, not starting share."); return; @@ -92,7 +95,7 @@ public class ImageActionsApi { intentForUri.putExtra(EXTRA_STREAM, uri); } return new Intent[]{intentForUri}; - }, TAG)); + }, TAG, exceptionCallback)); } /** diff --git a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java index de7dbd64f5..51a9915523 100644 --- a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java +++ b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java @@ -154,6 +154,18 @@ public class ImageActionUtils { @WorkerThread public static void persistBitmapAndStartActivity(Context context, Bitmap bitmap, Rect crop, Intent intent, BiFunction uriToIntentMap, String tag) { + persistBitmapAndStartActivity(context, bitmap, crop, intent, uriToIntentMap, tag, + (Runnable) null); + } + + /** + * Starts activity based on given intent created from image uri. + * @param exceptionCallback An optional callback to be called when the intent can't be resolved + */ + @WorkerThread + public static void persistBitmapAndStartActivity(Context context, Bitmap bitmap, Rect crop, + Intent intent, BiFunction uriToIntentMap, String tag, + Runnable exceptionCallback) { Intent[] intents = uriToIntentMap.apply(getImageUri(bitmap, crop, context, tag), intent); try { @@ -165,6 +177,9 @@ public class ImageActionUtils { } } catch (ActivityNotFoundException e) { Log.e(TAG, "No activity found to receive image intent"); + if (exceptionCallback != null) { + exceptionCallback.run(); + } } } From 272d622bc4c572fbfc8e2b9362f810cf411def9d Mon Sep 17 00:00:00 2001 From: vadimt Date: Tue, 17 Aug 2021 18:47:12 -0700 Subject: [PATCH 02/10] Improving diags for containers disappearing while getting children Bug: 184609576 Test: presubmit Change-Id: I7f527a9991a58fd148b17783078b5ac80979a5b3 --- .../android/launcher3/tapl/LauncherInstrumentation.java | 9 +++++++++ tests/tapl/com/android/launcher3/tapl/Widgets.java | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 2ca40d8229..efd8a551d4 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -1016,6 +1016,15 @@ public final class LauncherInstrumentation { } } + List getChildren(UiObject2 container) { + try { + return container.getChildren(); + } catch (StaleObjectException e) { + fail("The container disappeared from screen"); + return null; + } + } + private boolean hasLauncherObject(String resId) { return mDevice.hasObject(getLauncherObjectSelector(resId)); } diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java index 99d988926a..6e7264ac0b 100644 --- a/tests/tapl/com/android/launcher3/tapl/Widgets.java +++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java @@ -116,9 +116,9 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer { "widget_preview"); int i = 0; for (; ; ) { - final Collection tableRows = widgetsContainer.getChildren(); + final Collection tableRows = mLauncher.getChildren(widgetsContainer); for (UiObject2 row : tableRows) { - final Collection widgetCells = row.getChildren(); + final Collection widgetCells = mLauncher.getChildren(row); for (UiObject2 widget : widgetCells) { final UiObject2 label = mLauncher.findObjectInContainer(widget, labelSelector); From 39aa2e0f1da9012da0e0080d8448664eaecbf2fd Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 19 Aug 2021 12:02:57 -0700 Subject: [PATCH 03/10] Moving OrientationTouchTransformerTest to instrumentation tests Bug: 196825541 Test: Presubmit Change-Id: Ifb0b00f789214a8dde246ab13703211d536364af --- .../quickstep/OrientationTouchTransformer.java | 4 ++-- .../quickstep/OrientationTouchTransformerTest.java | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) rename quickstep/{robolectric_tests => tests}/src/com/android/quickstep/OrientationTouchTransformerTest.java (98%) diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java index 35a851ab65..81e69176c2 100644 --- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java +++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java @@ -462,7 +462,7 @@ class OrientationTouchTransformer { + "mRotation: " + mRotation + " this: " + this); } - event.transform(mTmpMatrix); + event.applyTransform(mTmpMatrix); return true; } mTmpPoint[0] = event.getX(); @@ -478,7 +478,7 @@ class OrientationTouchTransformer { } if (contains(mTmpPoint[0], mTmpPoint[1])) { - event.transform(mTmpMatrix); + event.applyTransform(mTmpMatrix); return true; } return false; diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java similarity index 98% rename from quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java rename to quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java index 2b1b57c450..159a51f733 100644 --- a/quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java +++ b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java @@ -19,6 +19,8 @@ package com.android.quickstep; import static android.view.Display.DEFAULT_DISPLAY; +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; + import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; import static org.junit.Assert.assertFalse; @@ -40,6 +42,9 @@ import android.view.Display; import android.view.MotionEvent; import android.view.Surface; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + import com.android.launcher3.ResourceUtils; import com.android.launcher3.util.DisplayController; @@ -47,10 +52,9 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; -@RunWith(RobolectricTestRunner.class) +@SmallTest +@RunWith(AndroidJUnit4.class) public class OrientationTouchTransformerTest { static class ScreenSize { int mHeight; @@ -293,7 +297,7 @@ public class OrientationTouchTransformerTest { } private DisplayController.Info createDisplayInfo(ScreenSize screenSize, int rotation) { - Context context = RuntimeEnvironment.application; + Context context = getApplicationContext(); Display display = spy(context.getSystemService(DisplayManager.class) .getDisplay(DEFAULT_DISPLAY)); From bea7e2ee6e7a7c457f83260015d439c0e1631b4f Mon Sep 17 00:00:00 2001 From: vadimt Date: Mon, 23 Aug 2021 13:57:51 -0700 Subject: [PATCH 04/10] More logging for for Nexus home activity appearing after to-home gesture in L3 Bug: 192018189 Test: presubmit Change-Id: I92cfa9e40f71773e636c700ab7f84df1d9feac19 --- .../android/quickstep/AbsSwipeUpHandler.java | 17 +++++++++++++++++ .../OtherActivityInputConsumer.java | 7 +++++++ 2 files changed, 24 insertions(+) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index fe16b33069..46d58577cd 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -65,6 +65,7 @@ import android.graphics.RectF; import android.os.Build; import android.os.IBinder; import android.os.SystemClock; +import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.View.OnApplyWindowInsetsListener; @@ -87,6 +88,7 @@ import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogManager.StatsLogger; import com.android.launcher3.statemanager.BaseState; import com.android.launcher3.statemanager.StatefulActivity; +import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.tracing.InputConsumerProto; import com.android.launcher3.tracing.SwipeHandlerProto; import com.android.launcher3.util.TraceHelper; @@ -876,6 +878,9 @@ public abstract class AbsSwipeUpHandler, */ @UiThread public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) { + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.L3_SWIPE_TO_HOME, "3"); + } float flingThreshold = mContext.getResources() .getDimension(R.dimen.quickstep_fling_threshold_speed); boolean isFling = mGestureStarted && !mIsMotionPaused @@ -1037,6 +1042,9 @@ public abstract class AbsSwipeUpHandler, @UiThread private void handleNormalGestureEnd(float endVelocity, boolean isFling, PointF velocity, boolean isCancel) { + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.L3_SWIPE_TO_HOME, "4"); + } long duration = MAX_SWIPE_DURATION; float currentShift = mCurrentShift.value; final GestureEndTarget endTarget = calculateEndTarget(velocity, endVelocity, @@ -1155,6 +1163,9 @@ public abstract class AbsSwipeUpHandler, @UiThread private void animateToProgress(float start, float end, long duration, Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) { + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.L3_SWIPE_TO_HOME, "5"); + } runOnRecentsAnimationStart(() -> animateToProgressInternal(start, end, duration, interpolator, target, velocityPxPerMs)); } @@ -1183,6 +1194,9 @@ public abstract class AbsSwipeUpHandler, @UiThread private void animateToProgressInternal(float start, float end, long duration, Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) { + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.L3_SWIPE_TO_HOME, "7"); + } maybeUpdateRecentsAttachedState(); // If we are transitioning to launcher, then listen for the activity to be restarted while @@ -1812,6 +1826,9 @@ public abstract class AbsSwipeUpHandler, * be run when it is next started. */ protected void runOnRecentsAnimationStart(Runnable action) { + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.L3_SWIPE_TO_HOME, "6"); + } if (mRecentsAnimationTargets == null) { mRecentsAnimationStartCallbacks.add(action); } else { diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java index 725c7c45a5..e6285f2902 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java @@ -50,6 +50,7 @@ import android.view.ViewConfiguration; import androidx.annotation.UiThread; import com.android.launcher3.R; +import com.android.launcher3.Utilities; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.tracing.InputConsumerProto; @@ -356,6 +357,9 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC } case ACTION_CANCEL: case ACTION_UP: { + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.L3_SWIPE_TO_HOME, "1"); + } if (DEBUG_FAILED_QUICKSWITCH && !mPassedWindowMoveSlop) { float displacementX = mLastPos.x - mDownPos.x; float displacementY = mLastPos.y - mDownPos.y; @@ -409,6 +413,9 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC * the animation can still be running. */ private void finishTouchTracking(MotionEvent ev) { + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.L3_SWIPE_TO_HOME, "2"); + } Object traceToken = TraceHelper.INSTANCE.beginSection(UP_EVT, FLAG_CHECK_FOR_RACE_CONDITIONS); From 98314d0d4be52c93975ac6c8c2eb4e14a0ea6d09 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Mon, 23 Aug 2021 16:29:42 -0700 Subject: [PATCH 05/10] Rely on presense of divider target to determine split screen state * Don't rely on the number of leashes, since an app with assistant invoked returns multiple remote app targets even though the device isn't in split screen Fixes: 197293347 Test: Repro steps in bug don't cause crash. Less fatal bugs need to be addressed, TODO(b/197568823) Change-Id: I3432e3d8c54a433dd38d297db73ea3d46bad7ba9 --- .../com/android/quickstep/SwipeUpAnimationLogic.java | 2 ++ .../src/com/android/quickstep/views/RecentsView.java | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java index ec51599fbf..1f57e9962a 100644 --- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java +++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java @@ -278,6 +278,8 @@ public abstract class SwipeUpAnimationLogic implements RemoteAnimationTargetCompat primaryTaskTarget; RemoteAnimationTargetCompat secondaryTaskTarget; + // TODO(b/197568823) Determine if we need to exclude assistant as one of the targets we + // animate if (!mIsSwipeForStagedSplit) { primaryTaskTarget = targets.findTask(mGestureState.getRunningTaskId()); mRemoteTargetHandles[0].mTransformParams.setTargetSet(targets); diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index abeadfd29b..6094c2f760 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -4019,12 +4019,14 @@ public abstract class RecentsView Date: Wed, 18 Aug 2021 15:17:54 -1000 Subject: [PATCH 06/10] Adds additional pages to Taskbar Edu. Currently there are pages for the following: - Splitscreen - Customize (add apps/predicted apps) - Docking (long press to hide) At the moment they all use the same placeholder image from before. Button states: Page 1: Close and Next Page 2: Back and Next Page 3: Back and Done You can also swipe left and right between the pages. Demo: https://drive.google.com/file/d/1_D3i-jZxCRRVHV92p6hG5cm3VGcJ_bhK/view?usp=sharing&resourcekey=0-KHLHTTx67JlmVv-UZoAUAw Bug: 180605356 Test: Manual Change-Id: Ibbb81610a611f6ca412e53ed90dc1c67764f417e --- quickstep/res/layout/taskbar_edu.xml | 120 ++++++++++++++---- quickstep/res/values/strings.xml | 24 +++- quickstep/res/values/styles.xml | 2 + .../taskbar/TaskbarEduController.java | 28 +++- .../taskbar/TaskbarEduPagedView.java | 79 ++++++++++++ .../launcher3/taskbar/TaskbarEduView.java | 38 +++++- 6 files changed, 260 insertions(+), 31 deletions(-) create mode 100644 quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java diff --git a/quickstep/res/layout/taskbar_edu.xml b/quickstep/res/layout/taskbar_edu.xml index b7717b71be..ef57a53690 100644 --- a/quickstep/res/layout/taskbar_edu.xml +++ b/quickstep/res/layout/taskbar_edu.xml @@ -17,6 +17,7 @@ - + launcher:pageIndicator="@+id/content_page_indicator"> - + + + + + + + + + + + + + + + + + + + + +