diff --git a/quickstep/res/layout/overview_actions_container.xml b/quickstep/res/layout/overview_actions_container.xml index 258f24a54e..d15a2d2ab6 100644 --- a/quickstep/res/layout/overview_actions_container.xml +++ b/quickstep/res/layout/overview_actions_container.xml @@ -56,7 +56,7 @@ android:visibility="gone" /> 600dp - - 500dp - 250dp - 0.0285dp 0.15dp 0.285dp 1.4dp 36dp + 0.5dp 50dp diff --git a/quickstep/res/values/override.xml b/quickstep/res/values/override.xml index 8f4ce4343b..605774dd9c 100644 --- a/quickstep/res/values/override.xml +++ b/quickstep/res/values/override.xml @@ -25,8 +25,6 @@ com.android.quickstep.QuickstepProcessInitializer - com.android.quickstep.logging.UserEventDispatcherExtension - com.android.launcher3.model.QuickstepModelDelegate diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java index 2543148099..b53e834db4 100644 --- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java @@ -57,6 +57,7 @@ import android.graphics.drawable.Drawable; import android.os.CancellationSignal; import android.os.Handler; import android.os.Looper; +import android.os.Trace; import android.util.Pair; import android.view.View; @@ -137,6 +138,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans // Progress = 0: All apps is fully pulled up, Progress = 1: All apps is fully pulled down. public static final float ALL_APPS_PROGRESS_OFF_SCREEN = 1.3059858f; + public static final String TRANSITION_OPEN_LAUNCHER = "transition:OpenLauncher"; protected final BaseQuickstepLauncher mLauncher; @@ -857,6 +859,21 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans // is initialized. if (launcherIsATargetWithMode(appTargets, MODE_OPENING) || mLauncher.isForceInvisible()) { + if (Trace.isEnabled()) { + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + Trace.beginAsyncSection(TRANSITION_OPEN_LAUNCHER, 0); + super.onAnimationStart(animation); + } + + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + Trace.endAsyncSection(TRANSITION_OPEN_LAUNCHER, 0); + } + }); + } // Only register the content animation for cancellation when state changes mLauncher.getStateManager().setCurrentAnimation(anim); @@ -891,6 +908,9 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans */ private class AppLaunchAnimationRunner implements WrappedAnimationRunnerImpl { + private static final String TRANSITION_LAUNCH_FROM_RECENTS = "transition:LaunchFromRecents"; + private static final String TRANSITION_LAUNCH_FROM_ICON = "transition:LaunchFromIcon"; + private final Handler mHandler; private final View mV; @@ -913,7 +933,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans boolean launcherClosing = launcherIsATargetWithMode(appTargets, MODE_CLOSING); - if (isLaunchingFromRecents(mV, appTargets)) { + final boolean launchingFromRecents = isLaunchingFromRecents(mV, appTargets); + if (launchingFromRecents) { composeRecentsLaunchAnimator(anim, mV, appTargets, wallpaperTargets, launcherClosing); } else { @@ -921,6 +942,26 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans launcherClosing); } + if (Trace.isEnabled()) { + final String section = + launchingFromRecents + ? TRANSITION_LAUNCH_FROM_RECENTS : TRANSITION_LAUNCH_FROM_ICON; + + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + Trace.beginAsyncSection(section, 0); + super.onAnimationStart(animation); + } + + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + Trace.endAsyncSection(section, 0); + } + }); + } + if (launcherClosing) { anim.addListener(mForceInvisibleListener); } diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java index 94af134de5..77fd103a6c 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java @@ -36,13 +36,10 @@ import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7; import static com.android.launcher3.anim.Interpolators.clampToProgress; import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH; -import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE; -import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y; -import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS; import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE; @@ -64,7 +61,6 @@ import com.android.launcher3.LauncherState.ScaleAndTranslation; import com.android.launcher3.Workspace; import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsTransitionController; -import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.quickstep.SysUINavigationMode; @@ -81,10 +77,8 @@ public class QuickstepAtomicAnimationFactory extends private static final float RECENTS_PREPARE_SCALE = 1.33f; public static final int INDEX_SHELF_ANIM = RecentsAtomicAnimationFactory.NEXT_INDEX + 0; - public static final int INDEX_PAUSE_TO_OVERVIEW_ANIM = - RecentsAtomicAnimationFactory.NEXT_INDEX + 1; - private static final int MY_ANIM_COUNT = 2; + private static final int MY_ANIM_COUNT = 1; protected static final int NEXT_INDEX = RecentsAtomicAnimationFactory.NEXT_INDEX + MY_ANIM_COUNT; @@ -92,8 +86,6 @@ public class QuickstepAtomicAnimationFactory extends // cache the value. private int mHintToNormalDuration = -1; - public static final long ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW = 300; - public QuickstepAtomicAnimationFactory(QuickstepLauncher activity) { super(activity, MY_ANIM_COUNT); } @@ -132,21 +124,6 @@ public class QuickstepAtomicAnimationFactory extends return springAnim; } - case INDEX_PAUSE_TO_OVERVIEW_ANIM: { - StateAnimationConfig config = new StateAnimationConfig(); - config.duration = ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW; - - config.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2); - config.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_3); - if ((OVERVIEW.getVisibleElements(mActivity) & HOTSEAT_ICONS) != 0) { - config.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2); - config.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2); - } - - StateManager stateManager = mActivity.getStateManager(); - return stateManager.createAtomicAnimation( - stateManager.getCurrentStableState(), OVERVIEW, config); - } default: return super.createStateElementAnimation(index, values); } diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java index 591d3ca1f9..c78d4741d8 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java @@ -75,6 +75,7 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch private final float mMotionPauseMinDisplacement; private boolean mDidTouchStartInNavBar; + private boolean mStartedOverview; private boolean mReachedOverview; // The last recorded displacement before we reached overview. private PointF mStartDisplacement = new PointF(); @@ -128,7 +129,7 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch mMotionPauseDetector.clear(); if (handlingOverviewAnim()) { - mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseChanged); + mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseDetected); } if (mFromState == NORMAL && mToState == HINT_STATE) { @@ -138,6 +139,7 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch mFromState.getOverviewScrimAlpha(mLauncher), mToState.getOverviewScrimAlpha(mLauncher)); } + mStartedOverview = false; mReachedOverview = false; mOverviewResistYAnim = null; } @@ -152,7 +154,7 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch @Override public void onDragEnd(float velocity) { - if (mMotionPauseDetector.isPaused() && handlingOverviewAnim()) { + if (mStartedOverview) { goToOverviewOrHomeOnDragEnd(velocity); } else { super.onDragEnd(velocity); @@ -185,7 +187,7 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch } } - private void onMotionPauseChanged(boolean isPaused) { + private void onMotionPauseDetected() { if (mCurrentAnimation == null) { return; } @@ -199,6 +201,7 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch maybeSwipeInteractionToOverviewComplete(); }); }); + mStartedOverview = true; VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC); } @@ -219,7 +222,7 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch if (TestProtocol.sDebugTracing) { Log.d(TestProtocol.PAUSE_NOT_DETECTED, "NoButtonNavbarToOverviewTouchController"); } - if (mMotionPauseDetector.isPaused()) { + if (mStartedOverview) { if (!mReachedOverview) { mStartDisplacement.set(xDisplacement, yDisplacement); mStartY = event.getY(); @@ -234,8 +237,6 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch * OVERVIEW_MOVEMENT_FACTOR); } } - // Stay in Overview. - return true; } float upDisplacement = -yDisplacement; @@ -243,13 +244,12 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch || upDisplacement < mMotionPauseMinDisplacement); mMotionPauseDetector.addPosition(event); - return super.onDrag(yDisplacement, xDisplacement, event); + // Stay in Overview. + return mStartedOverview || super.onDrag(yDisplacement, xDisplacement, event); } private void goToOverviewOrHomeOnDragEnd(float velocity) { - float velocityDp = dpiFromPx(velocity); - boolean isFling = Math.abs(velocityDp) > 1; - boolean goToHomeInsteadOfOverview = isFling; + boolean goToHomeInsteadOfOverview = !mMotionPauseDetector.isPaused(); if (goToHomeInsteadOfOverview) { new OverviewToHomeAnim(mLauncher, ()-> onSwipeInteractionCompleted(NORMAL, Touch.FLING)) .animateWithVelocity(velocity); diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java index 4b0642fab7..a6a3497f04 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java @@ -35,7 +35,6 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_T import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW; import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT; import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP; -import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_PAUSE_TO_OVERVIEW_ANIM; import static com.android.launcher3.util.DisplayController.getSingleFrameMs; import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET; @@ -80,13 +79,14 @@ import com.android.quickstep.views.LauncherRecentsView; * the user as possible, also handles swipe up and hold to go to overview and swiping back home. */ public class NoButtonQuickSwitchTouchController implements TouchController, - BothAxesSwipeDetector.Listener, MotionPauseDetector.OnMotionPauseListener { + BothAxesSwipeDetector.Listener { /** The minimum progress of the scale/translationY animation until drag end. */ private static final float Y_ANIM_MIN_PROGRESS = 0.25f; private static final Interpolator FADE_OUT_INTERPOLATOR = DEACCEL_5; private static final Interpolator TRANSLATE_OUT_INTERPOLATOR = ACCEL_0_75; private static final Interpolator SCALE_DOWN_INTERPOLATOR = LINEAR; + private static final long ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW = 300; private final BaseQuickstepLauncher mLauncher; private final BothAxesSwipeDetector mSwipeDetector; @@ -167,7 +167,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController, if (start) { mStartState = mLauncher.getStateManager().getState(); - mMotionPauseDetector.setOnMotionPauseListener(this); + mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseDetected); // We have detected horizontal drag start, now allow swipe up as well. mSwipeDetector.setDetectableScrollConditions(DIRECTION_RIGHT | DIRECTION_UP, @@ -177,8 +177,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController, } } - @Override - public void onMotionPauseChanged(boolean isPaused) { + private void onMotionPauseDetected() { VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC); } @@ -271,11 +270,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController, mIsHomeScreenVisible = FADE_OUT_INTERPOLATOR.getInterpolation(xProgress) <= 1 - ALPHA_CUTOFF_THRESHOLD; - - // Only allow motion pause if the home screen is invisible, since some - // home screen elements will appear in the shelf on motion pause. - mMotionPauseDetector.setDisallowPause(mIsHomeScreenVisible - || -displacement.y < mMotionPauseMinDisplacement); + mMotionPauseDetector.setDisallowPause(-displacement.y < mMotionPauseMinDisplacement); mMotionPauseDetector.addPosition(ev); if (mXOverviewAnim != null) { @@ -296,8 +291,10 @@ public class NoButtonQuickSwitchTouchController implements TouchController, if (mMotionPauseDetector.isPaused() && noFling) { cancelAnimations(); - Animator overviewAnim = mLauncher.createAtomicAnimationFactory() - .createStateElementAnimation(INDEX_PAUSE_TO_OVERVIEW_ANIM); + StateAnimationConfig config = new StateAnimationConfig(); + config.duration = ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW; + Animator overviewAnim = mLauncher.getStateManager().createAtomicAnimation( + mStartState, OVERVIEW, config); overviewAnim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java index 1208c6c79b..059a703075 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java @@ -89,13 +89,16 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr @Override protected boolean canInterceptTouch(MotionEvent ev) { + // If we are swiping to all apps instead of overview, allow it from anywhere. + boolean interceptAnywhere = mLauncher.isInState(NORMAL) && !mAllowDragToOverview; if (mCurrentAnimation != null) { if (mFinishFastOnSecondTouch) { mCurrentAnimation.getAnimationPlayer().end(); } AllAppsTransitionController allAppsController = mLauncher.getAllAppsController(); - if (ev.getY() >= allAppsController.getShiftRange() * allAppsController.getProgress()) { + if (ev.getY() >= allAppsController.getShiftRange() * allAppsController.getProgress() + || interceptAnywhere) { // If we are already animating from a previous state, we can intercept as long as // the touch is below the current all apps progress (to allow for double swipe). return true; @@ -117,9 +120,7 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr return false; } } else { - // If we are swiping to all apps instead of overview, allow it from anywhere. - boolean interceptAnywhere = mLauncher.isInState(NORMAL) && !mAllowDragToOverview; - // For all other states, only listen if the event originated below the hotseat height + // For non-normal states, only listen if the event originated below the hotseat height if (!interceptAnywhere && !isTouchOverHotseat(mLauncher, ev)) { return false; } diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index aaa2720047..26ad3778a7 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -19,6 +19,7 @@ import static android.widget.Toast.LENGTH_SHORT; import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER; import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; +import static com.android.launcher3.QuickstepAppTransitionManagerImpl.TRANSITION_OPEN_LAUNCHER; import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; @@ -53,6 +54,7 @@ import android.graphics.PointF; import android.graphics.Rect; import android.os.Build; import android.os.SystemClock; +import android.os.Trace; import android.util.Log; import android.view.MotionEvent; import android.view.View; @@ -89,6 +91,7 @@ import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.ActivityInitListener; import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.InputConsumerProxy; +import com.android.quickstep.util.MotionPauseDetector; import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.TransformParams; @@ -201,6 +204,7 @@ public abstract class AbsSwipeUpHandler, Q extends // Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise private RunningWindowAnim mRunningWindowAnim; private boolean mIsMotionPaused; + private boolean mHasMotionEverBeenPaused; private boolean mContinuingLastGesture; @@ -482,13 +486,20 @@ public abstract class AbsSwipeUpHandler, Q extends .getHighResLoadingState().setVisible(true); } - /** - * Called when motion pause is detected - */ - public void onMotionPauseChanged(boolean isPaused) { - mIsMotionPaused = isPaused; - maybeUpdateRecentsAttachedState(); - performHapticFeedback(); + public MotionPauseDetector.OnMotionPauseListener getMotionPauseListener() { + return new MotionPauseDetector.OnMotionPauseListener() { + @Override + public void onMotionPauseDetected() { + mHasMotionEverBeenPaused = true; + maybeUpdateRecentsAttachedState(); + performHapticFeedback(); + } + + @Override + public void onMotionPauseChanged(boolean isPaused) { + mIsMotionPaused = isPaused; + } + }; } public void maybeUpdateRecentsAttachedState() { @@ -519,7 +530,7 @@ public abstract class AbsSwipeUpHandler, Q extends // The window is going away so make sure recents is always visible in this case. recentsAttachedToAppWindow = true; } else { - recentsAttachedToAppWindow = mIsMotionPaused || mIsLikelyToStartNewTask; + recentsAttachedToAppWindow = mHasMotionEverBeenPaused || mIsLikelyToStartNewTask; } mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate); @@ -742,8 +753,9 @@ public abstract class AbsSwipeUpHandler, Q extends @UiThread public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) { float flingThreshold = mContext.getResources() - .getDimension(R.dimen.quickstep_fling_threshold_velocity); - boolean isFling = mGestureStarted && Math.abs(endVelocity) > flingThreshold; + .getDimension(R.dimen.quickstep_fling_threshold_speed); + boolean isFling = mGestureStarted && !mIsMotionPaused + && Math.abs(endVelocity) > flingThreshold; mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED); mLogAction = isFling ? Touch.FLING : Touch.SWIPE; @@ -858,7 +870,7 @@ public abstract class AbsSwipeUpHandler, Q extends if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !willGoToNewTaskOnSwipeUp) { endTarget = HOME; - } else if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !mIsMotionPaused) { + } else if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp) { // If swiping at a diagonal, base end target on the faster velocity. endTarget = NEW_TASK; } else if (isSwipeUp) { @@ -878,7 +890,6 @@ public abstract class AbsSwipeUpHandler, Q extends @UiThread private void handleNormalGestureEnd(float endVelocity, boolean isFling, PointF velocity, boolean isCancel) { - PointF velocityPxPerMs = new PointF(velocity.x / 1000, velocity.y / 1000); long duration = MAX_SWIPE_DURATION; float currentShift = mCurrentShift.value; final GestureEndTarget endTarget = calculateEndTarget(velocity, endVelocity, @@ -893,14 +904,12 @@ public abstract class AbsSwipeUpHandler, Q extends startShift = currentShift; interpolator = endTarget == RECENTS ? OVERSHOOT_1_2 : DEACCEL; } else { - startShift = Utilities.boundToRange(currentShift - velocityPxPerMs.y + startShift = Utilities.boundToRange(currentShift - velocity.y * getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor); - float minFlingVelocity = mContext.getResources() - .getDimension(R.dimen.quickstep_fling_min_velocity); - if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) { + if (mTransitionDragLength > 0) { if (endTarget == RECENTS && !mDeviceState.isFullyGesturalNavMode()) { Interpolators.OvershootParams overshoot = new Interpolators.OvershootParams( - startShift, endShift, endShift, endVelocity / 1000, + startShift, endShift, endShift, endVelocity, mTransitionDragLength, mContext); endShift = overshoot.end; interpolator = overshoot.interpolator; @@ -912,7 +921,7 @@ public abstract class AbsSwipeUpHandler, Q extends // we want the page's snap velocity to approximately match the velocity at // which the user flings, so we scale the duration by a value near to the // derivative of the scroll interpolator at zero, ie. 2. - long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs.y)); + long baseDuration = Math.round(Math.abs(distanceToTravel / velocity.y)); duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration); if (endTarget == RECENTS) { @@ -952,7 +961,7 @@ public abstract class AbsSwipeUpHandler, Q extends mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED); } - animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs); + animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocity); } private void doLogGesture(GestureEndTarget endTarget, @Nullable TaskView targetTask) { @@ -1135,6 +1144,7 @@ public abstract class AbsSwipeUpHandler, Q extends anim.addAnimatorListener(new AnimationSuccessListener() { @Override public void onAnimationStart(Animator animation) { + Trace.beginAsyncSection(TRANSITION_OPEN_LAUNCHER, 0); if (mActivity != null) { removeLiveTileOverlay(); } @@ -1149,6 +1159,12 @@ public abstract class AbsSwipeUpHandler, Q extends maybeUpdateRecentsAttachedState(false); mActivityInterface.onSwipeUpToHomeComplete(mDeviceState); } + + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + Trace.endAsyncSection(TRANSITION_OPEN_LAUNCHER, 0); + } }); if (mRecentsAnimationTargets != null) { mRecentsAnimationTargets.addReleaseCheck(anim); diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java index e4b8ce2df5..39af0db8ff 100644 --- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java +++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java @@ -58,6 +58,12 @@ public class QuickstepTestInformationHandler extends TestInformationHandler { FeatureFlags.ENABLE_OVERVIEW_SHARE.get()); return response; } + + case TestProtocol.REQUEST_OVERVIEW_CONTENT_PUSH_ENABLED: { + response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, + FeatureFlags.ENABLE_OVERVIEW_CONTENT_PUSH.get()); + return response; + } } return super.call(method); diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index e9fc42328b..d8064a2153 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -569,6 +569,7 @@ public class RecentsAnimationDeviceState implements pw.println(" isUserUnlocked=" + mIsUserUnlocked); pw.println(" isOneHandedModeEnabled=" + mIsOneHandedModeEnabled); pw.println(" isSwipeToNotificationEnabled=" + mIsSwipeToNotificationEnabled); + pw.println(" deferredGestureRegion=" + mDeferredGestureRegion); mRotationTouchHelper.dump(pw); } } diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index f29a5c8ed2..df93e0b6a2 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -64,8 +64,8 @@ import com.android.launcher3.provider.RestoreDbTask; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.TestProtocol; -import com.android.launcher3.tracing.nano.LauncherTraceProto; -import com.android.launcher3.tracing.nano.TouchInteractionServiceProto; +import com.android.launcher3.tracing.LauncherTraceProto; +import com.android.launcher3.tracing.TouchInteractionServiceProto; import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.util.OnboardingPrefs; import com.android.launcher3.util.TraceHelper; @@ -105,7 +105,7 @@ import java.util.LinkedList; */ @TargetApi(Build.VERSION_CODES.R) public class TouchInteractionService extends Service implements PluginListener, - ProtoTraceable { + ProtoTraceable { private static final String TAG = "TouchInteractionService"; @@ -368,9 +368,12 @@ public class TouchInteractionService extends Service implements PluginListener mProtoTracer; + private final FrameProtoTracer mProtoTracer; public ProtoTracer(Context context) { mContext = context; @@ -65,40 +66,47 @@ public class ProtoTracer implements ProtoTraceParams> traceables) { - LauncherTraceEntryProto proto = reuseObj != null - ? reuseObj - : new LauncherTraceEntryProto(); - proto.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos(); - proto.launcher = proto.launcher != null ? proto.launcher : new LauncherTraceProto(); + public LauncherTraceEntryProto.Builder updateBufferProto( + LauncherTraceEntryProto.Builder reuseObj, + ArrayList> traceables) { + Trace.beginSection("ProtoTracer.updateBufferProto"); + LauncherTraceEntryProto.Builder proto = LauncherTraceEntryProto.newBuilder(); + proto.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); + LauncherTraceProto.Builder launcherProto = LauncherTraceProto.newBuilder(); for (ProtoTraceable t : traceables) { - t.writeToProto(proto.launcher); + t.writeToProto(launcherProto); } + proto.setLauncher(launcherProto); + Trace.endSection(); return proto; } @Override - public byte[] serializeEncapsulatingProto(LauncherTraceFileProto encapsulatingProto, - Queue buffer) { - encapsulatingProto.magicNumber = MAGIC_NUMBER_VALUE; - encapsulatingProto.entry = buffer.toArray(new LauncherTraceEntryProto[0]); - return MessageNano.toByteArray(encapsulatingProto); + public byte[] serializeEncapsulatingProto(LauncherTraceFileProto.Builder encapsulatingProto, + Queue buffer) { + Trace.beginSection("ProtoTracer.serializeEncapsulatingProto"); + encapsulatingProto.setMagicNumber(MAGIC_NUMBER_VALUE); + for (LauncherTraceEntryProto.Builder entry : buffer) { + encapsulatingProto.addEntry(entry); + } + byte[] bytes = encapsulatingProto.build().toByteArray(); + Trace.endSection(); + return bytes; } @Override - public byte[] getProtoBytes(MessageNano proto) { - return MessageNano.toByteArray(proto); + public byte[] getProtoBytes(MessageLite.Builder proto) { + return proto.build().toByteArray(); } @Override - public int getProtoSize(MessageNano proto) { - return proto.getCachedSize(); + public int getProtoSize(MessageLite.Builder proto) { + return proto.build().getSerializedSize(); } public void start() { @@ -109,11 +117,11 @@ public class ProtoTracer implements ProtoTraceParams traceable) { + public void add(ProtoTraceable traceable) { mProtoTracer.add(traceable); } - public void remove(ProtoTraceable traceable) { + public void remove(ProtoTraceable traceable) { mProtoTracer.remove(traceable); } diff --git a/quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java b/quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java index 29b95589f3..7bbde30df8 100644 --- a/quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java +++ b/quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java @@ -21,13 +21,14 @@ import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.Utilities.squaredHypot; +import static com.android.launcher3.util.VelocityUtils.PX_PER_MS; import android.content.Context; import android.graphics.PointF; import android.view.MotionEvent; import android.view.VelocityTracker; -import android.view.ViewConfiguration; +import com.android.launcher3.R; import com.android.launcher3.Utilities; /** @@ -50,7 +51,8 @@ public class TriggerSwipeUpTouchTracker { NavBarPosition navBarPosition, Runnable onInterceptTouch, OnSwipeUpListener onSwipeUp) { mSquaredTouchSlop = Utilities.squaredTouchSlop(context); - mMinFlingVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity(); + mMinFlingVelocity = context.getResources().getDimension( + R.dimen.quickstep_fling_threshold_speed); mNavBarPosition = navBarPosition; mDisableHorizontalSwipe = disableHorizontalSwipe; mOnInterceptTouch = onInterceptTouch; @@ -130,7 +132,7 @@ public class TriggerSwipeUpTouchTracker { } private void onGestureEnd(MotionEvent ev) { - mVelocityTracker.computeCurrentVelocity(1000); + mVelocityTracker.computeCurrentVelocity(PX_PER_MS); float velocityX = mVelocityTracker.getXVelocity(); float velocityY = mVelocityTracker.getYVelocity(); float velocity = mNavBarPosition.isRightEdge() diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java index 8f609919f5..2a6c9e92db 100644 --- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java +++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java @@ -113,7 +113,7 @@ public class OverviewActionsView extends FrameLayo findViewById(R.id.action_screenshot).setOnClickListener(this); if (ENABLE_OVERVIEW_SHARE.get()) { share.setVisibility(VISIBLE); - findViewById(R.id.share_space).setVisibility(VISIBLE); + findViewById(R.id.oav_three_button_space).setVisibility(VISIBLE); } } diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 982a7c432c..0362377399 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -79,7 +79,6 @@ import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; -import android.view.TouchDelegate; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; @@ -706,12 +705,9 @@ public abstract class RecentsView extends PagedView super.onTouchEvent(ev); TaskView taskView = getCurrentPageTaskView(); - if (taskView != null) { - TouchDelegate mChildTouchDelegate = taskView.getIconTouchDelegate(ev); - if (mChildTouchDelegate != null && mChildTouchDelegate.onTouchEvent(ev)) { - // Keep consuming events to pass to delegate - return true; - } + if (taskView != null && taskView.offerTouchToChildren(ev)) { + // Keep consuming events to pass to delegate + return true; } final int x = (int) ev.getX(); @@ -2255,7 +2251,7 @@ public abstract class RecentsView extends PagedView public void setRecentsAnimationTargets(RecentsAnimationController recentsAnimationController, RecentsAnimationTargets recentsAnimationTargets) { mRecentsAnimationController = recentsAnimationController; - if (recentsAnimationTargets != null) { + if (recentsAnimationTargets != null && recentsAnimationTargets.apps.length > 0) { mLiveTileTaskViewSimulator.setPreview( recentsAnimationTargets.apps[recentsAnimationTargets.apps.length - 1]); mLiveTileParams.setTargetSet(recentsAnimationTargets); diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index 259cd4dbfd..321821b573 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -135,6 +135,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { * delegated bounds only to be updated. */ private TransformingTouchDelegate mIconTouchDelegate; + private TransformingTouchDelegate mChipTouchDelegate; private static final List SYSTEM_GESTURE_EXCLUSION_RECT = Collections.singletonList(new Rect()); @@ -200,6 +201,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { private View mContextualChipWrapper; private View mContextualChip; private final float[] mIconCenterCoords = new float[2]; + private final float[] mChipCenterCoords = new float[2]; public TaskView(Context context) { this(context, null); @@ -259,11 +261,22 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { mIconTouchDelegate = new TransformingTouchDelegate(mIconView); } - public TouchDelegate getIconTouchDelegate(MotionEvent event) { + /** + * Whether the taskview should take the touch event from parent. Events passed to children + * that might require special handling. + */ + public boolean offerTouchToChildren(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { computeAndSetIconTouchDelegate(); + computeAndSetChipTouchDelegate(); } - return mIconTouchDelegate; + if (mIconTouchDelegate != null && mIconTouchDelegate.onTouchEvent(event)) { + return true; + } + if (mChipTouchDelegate != null && mChipTouchDelegate.onTouchEvent(event)) { + return true; + } + return false; } private void computeAndSetIconTouchDelegate() { @@ -278,6 +291,23 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { (int) (mIconCenterCoords[1] + iconHalfSize)); } + private void computeAndSetChipTouchDelegate() { + if (mContextualChipWrapper != null) { + float chipHalfWidth = mContextualChipWrapper.getWidth() / 2f; + float chipHalfHeight = mContextualChipWrapper.getHeight() / 2f; + mChipCenterCoords[0] = chipHalfWidth; + mChipCenterCoords[1] = chipHalfHeight; + getDescendantCoordRelativeToAncestor(mContextualChipWrapper, mActivity.getDragLayer(), + mChipCenterCoords, + false); + mChipTouchDelegate.setBounds( + (int) (mChipCenterCoords[0] - chipHalfWidth), + (int) (mChipCenterCoords[1] - chipHalfHeight), + (int) (mChipCenterCoords[0] + chipHalfWidth), + (int) (mChipCenterCoords[1] + chipHalfHeight)); + } + } + /** * The modalness of this view is how it should be displayed when it is shown on its own in the * modal state of overview. @@ -712,6 +742,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { mContextualChip.animate().scaleX(1f).scaleY(1f).setDuration(50); } if (mContextualChipWrapper != null) { + mChipTouchDelegate = new TransformingTouchDelegate(mContextualChipWrapper); mContextualChipWrapper.animate().alpha(1f).setDuration(50); } } @@ -733,6 +764,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { View oldContextualChipWrapper = mContextualChipWrapper; mContextualChipWrapper = null; mContextualChip = null; + mChipTouchDelegate = null; return oldContextualChipWrapper; } diff --git a/res/layout/search_result_people_item.xml b/res/layout/search_result_people_item.xml new file mode 100644 index 0000000000..a603941dfa --- /dev/null +++ b/res/layout/search_result_people_item.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/search_result_shortcut.xml b/res/layout/search_result_shortcut.xml new file mode 100644 index 0000000000..c350c973da --- /dev/null +++ b/res/layout/search_result_shortcut.xml @@ -0,0 +1,42 @@ + + + + + + + + \ No newline at end of file diff --git a/res/values/config.xml b/res/values/config.xml index fc0a5e10dc..325b62f9dc 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -59,7 +59,6 @@ true - @@ -187,4 +186,6 @@ + + diff --git a/res/values/strings.xml b/res/values/strings.xml index ad3e2b79f4..6ab81501f4 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -58,6 +58,8 @@ Search apps + + Search this phone and more... Loading apps… diff --git a/src/com/android/launcher3/AppFilter.java b/src/com/android/launcher3/AppFilter.java index 9b6166ffcf..3db456cbdb 100644 --- a/src/com/android/launcher3/AppFilter.java +++ b/src/com/android/launcher3/AppFilter.java @@ -3,15 +3,25 @@ package com.android.launcher3; import android.content.ComponentName; import android.content.Context; -import com.android.launcher3.util.ResourceBasedOverride; +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; -public class AppFilter implements ResourceBasedOverride { +/** + * Utility class to filter out components from various lists + */ +public class AppFilter { - public static AppFilter newInstance(Context context) { - return Overrides.getObject(AppFilter.class, context, R.string.app_filter_class); + private final Set mFilteredComponents; + + public AppFilter(Context context) { + mFilteredComponents = Arrays.stream( + context.getResources().getStringArray(R.array.filtered_components)) + .map(ComponentName::unflattenFromString) + .collect(Collectors.toSet()); } public boolean shouldShowApp(ComponentName app) { - return true; + return !mFilteredComponents.contains(app); } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index cacd3fbe7f..90566f3ce8 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -88,14 +88,19 @@ import android.view.Menu; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.animation.OvershootInterpolator; import android.widget.Toast; import androidx.annotation.CallSuper; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LifecycleRegistry; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; @@ -205,7 +210,8 @@ import java.util.stream.Stream; * Default launcher application. */ public class Launcher extends StatefulActivity implements LauncherExterns, - Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener { + Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener, + LifecycleOwner { public static final String TAG = "Launcher"; public static final ActivityTracker ACTIVITY_TRACKER = new ActivityTracker<>(); @@ -266,6 +272,8 @@ public class Launcher extends StatefulActivity implements Launche private LauncherAppTransitionManager mAppTransitionManager; private Configuration mOldConfig; + private LifecycleRegistry mLifecycleRegistry; + @Thunk Workspace mWorkspace; @Thunk @@ -466,6 +474,19 @@ public class Launcher extends StatefulActivity implements Launche mUserChangedCallbackCloseable = UserCache.INSTANCE.get(this).addUserChangeListener( () -> getStateManager().goToState(NORMAL)); + + if (Utilities.ATLEAST_R) { + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING); + } + + mLifecycleRegistry = new LifecycleRegistry(this); + mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED); + } + + @NonNull + @Override + public Lifecycle getLifecycle() { + return mLifecycleRegistry; } protected LauncherOverlayManager getDefaultOverlay() { @@ -881,6 +902,7 @@ public class Launcher extends StatefulActivity implements Launche @Override protected void onStop() { + mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED); super.onStop(); if (mDeferOverlayCallbacks) { checkIfOverlayStillDeferred(); @@ -904,6 +926,7 @@ public class Launcher extends StatefulActivity implements Launche mAppWidgetHost.setListenIfResumed(true); TraceHelper.INSTANCE.endSection(traceToken); + mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED); } @Override @@ -1063,6 +1086,7 @@ public class Launcher extends StatefulActivity implements Launche } TraceHelper.INSTANCE.endSection(traceToken); + mLifecycleRegistry.setCurrentState(Lifecycle.State.RESUMED); } @Override @@ -1070,6 +1094,7 @@ public class Launcher extends StatefulActivity implements Launche // Ensure that items added to Launcher are queued until Launcher returns ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_ACTIVITY_PAUSED); + mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED); super.onPause(); mDragController.cancelDrag(); mLastTouchUpTime = -1; @@ -1552,6 +1577,7 @@ public class Launcher extends StatefulActivity implements Launche mOverlayManager.onActivityDestroyed(this); mAppTransitionManager.unregisterRemoteAnimations(); mUserChangedCallbackCloseable.close(); + mLifecycleRegistry.setCurrentState(Lifecycle.State.DESTROYED); } public LauncherAccessibilityDelegate getAccessibilityDelegate() { diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index b278e81655..bfe327e40d 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -122,7 +122,7 @@ public class LauncherAppState { mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context); mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName); mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache); - mModel = new LauncherModel(context, this, mIconCache, AppFilter.newInstance(mContext)); + mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext)); } protected void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) { diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 8da81ac2d2..45aaa1b673 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -93,6 +93,7 @@ import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.pageindicators.WorkspacePageIndicator; import com.android.launcher3.popup.PopupContainerWithArrow; +import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.statemanager.StateManager.StateHandler; import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.touch.WorkspaceTouchListener; @@ -436,10 +437,6 @@ public class Workspace extends PagedView enforceDragParity("onDragEnd", 0, 0); } - if (!mDeferRemoveExtraEmptyScreen) { - removeExtraEmptyScreen(mDragSourceInternal != null); - } - updateChildrenLayersEnabled(); mDragInfo = null; mOutlineProvider = null; @@ -657,6 +654,7 @@ public class Workspace extends PagedView convertFinalScreenToEmptyScreenIfNecessary(); if (hasExtraEmptyScreen()) { removeView(mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID)); + setCurrentPage(getNextPage()); mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID); mScreenOrder.removeValue(EXTRA_EMPTY_SCREEN_ID); @@ -1873,6 +1871,18 @@ public class Workspace extends PagedView }; } } + StateManager stateManager = mLauncher.getStateManager(); + stateManager.addStateListener(new StateManager.StateListener() { + @Override + public void onStateTransitionComplete(LauncherState finalState) { + if (finalState == NORMAL) { + if (!mDeferRemoveExtraEmptyScreen) { + removeExtraEmptyScreen(true /* stripEmptyScreens */); + } + stateManager.removeStateListener(this); + } + } + }); mLauncher.getModelWriter().modifyItemInDatabase(info, container, screenId, lp.cellX, lp.cellY, item.spanX, item.spanY); @@ -2453,7 +2463,7 @@ public class Workspace extends PagedView Runnable onAnimationCompleteRunnable = new Runnable() { @Override public void run() { - // Normally removeExtraEmptyScreen is called in Workspace#onDragEnd, but when + // Normally removeExtraEmptyScreen is called in Workspace#onDrop, but when // adding an item that may not be dropped right away (due to a config activity) // we defer the removal until the activity returns. deferRemoveExtraEmptyScreen(); diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java index c3d4aeb745..ea887cc5eb 100644 --- a/src/com/android/launcher3/WorkspaceLayoutManager.java +++ b/src/com/android/launcher3/WorkspaceLayoutManager.java @@ -28,7 +28,7 @@ public interface WorkspaceLayoutManager { String TAG = "Launcher.Workspace"; - // The screen id used for the empty screen always present to the right. + // The screen id used for the empty screen always present at the end. int EXTRA_EMPTY_SCREEN_ID = -201; // The is the first screen. It is always present, even if its empty. int FIRST_SCREEN_ID = 0; diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 1f515669a6..5079469f4a 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -67,6 +67,9 @@ import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; import com.android.launcher3.util.Themes; import com.android.launcher3.views.RecyclerViewFastScroller; import com.android.launcher3.views.SpringRelativeLayout; +import com.android.systemui.plugins.shared.SearchTargetEvent; + +import java.util.function.IntConsumer; /** * The all apps view container. @@ -538,34 +541,35 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo * Handles selection on focused view and returns success */ public boolean selectFocusedView(View v) { - ItemInfo itemInfo = getHighlightedItemInfo(); - if (itemInfo != null) { - return mLauncher.startActivitySafely(v, itemInfo.getIntent(), itemInfo); + ItemInfo headerItem = getHighlightedItemFromHeader(); + if (headerItem != null) { + return mLauncher.startActivitySafely(v, headerItem.getIntent(), headerItem); } AdapterItem focusedItem = getActiveRecyclerView().getApps().getFocusedChild(); if (focusedItem instanceof AdapterItemWithPayload) { - Runnable onSelection = ((AdapterItemWithPayload) focusedItem).getSelectionHandler(); + IntConsumer onSelection = + ((AdapterItemWithPayload) focusedItem).getSelectionHandler(); if (onSelection != null) { - onSelection.run(); + onSelection.accept(SearchTargetEvent.QUICK_SELECT); return true; } } + if (focusedItem.appInfo != null) { + ItemInfo itemInfo = focusedItem.appInfo; + return mLauncher.startActivitySafely(v, itemInfo.getIntent(), itemInfo); + } return false; } /** - * Returns the ItemInfo of a view that is in focus, ready to be launched by an IME. + * Returns the ItemInfo of a focused view inside {@link FloatingHeaderView} */ - public ItemInfo getHighlightedItemInfo() { + public ItemInfo getHighlightedItemFromHeader() { View view = getFloatingHeaderView().getFocusedChild(); if (view != null && view.getTag() instanceof ItemInfo) { return ((ItemInfo) view.getTag()); } - if (getActiveRecyclerView().getApps().getFocusedChild() != null) { - // TODO: when new pipelines are included, getSearchResults - // should be supported at recycler view level and not apps list level. - return getActiveRecyclerView().getApps().getFocusedChild().appInfo; - } + return null; } diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index da161ac1cc..d1340faf23 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.net.Uri; +import android.os.Bundle; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -36,9 +37,6 @@ import androidx.annotation.Nullable; import androidx.core.view.accessibility.AccessibilityEventCompat; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import androidx.core.view.accessibility.AccessibilityRecordCompat; -import androidx.lifecycle.Lifecycle; -import androidx.lifecycle.LifecycleOwner; -import androidx.lifecycle.LifecycleRegistry; import androidx.lifecycle.LiveData; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -48,21 +46,26 @@ import androidx.slice.widget.SliceView; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.BubbleTextView; +import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.allapps.search.AllAppsSearchBarController.PayloadResultHandler; import com.android.launcher3.allapps.search.SearchSectionInfo; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.util.PackageManagerHelper; +import com.android.launcher3.views.HeroSearchResultView; +import com.android.systemui.plugins.AllAppsSearchPlugin; +import com.android.systemui.plugins.shared.SearchTarget; +import com.android.systemui.plugins.shared.SearchTargetEvent; import java.util.List; +import java.util.function.IntConsumer; /** * The grid view adapter of all the apps. */ public class AllAppsGridAdapter extends - RecyclerView.Adapter implements - LifecycleOwner { + RecyclerView.Adapter { public static final String TAG = "AppsGridAdapter"; @@ -89,12 +92,14 @@ public class AllAppsGridAdapter extends public static final int VIEW_TYPE_SEARCH_SLICE = 1 << 9; + public static final int VIEW_TYPE_SEARCH_SHORTCUT = 1 << 10; + + public static final int VIEW_TYPE_SEARCH_PEOPLE = 1 << 11; + // Common view type masks public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER; public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON; - private final LifecycleRegistry mLifecycleRegistry; - /** * ViewHolder for each icon. */ @@ -179,7 +184,9 @@ public class AllAppsGridAdapter extends || viewType == VIEW_TYPE_SEARCH_HERO_APP || viewType == VIEW_TYPE_SEARCH_ROW_WITH_BUTTON || viewType == VIEW_TYPE_SEARCH_SLICE - || viewType == VIEW_TYPE_SEARCH_ROW; + || viewType == VIEW_TYPE_SEARCH_ROW + || viewType == VIEW_TYPE_SEARCH_PEOPLE + || viewType == VIEW_TYPE_SEARCH_SHORTCUT; } } @@ -190,18 +197,28 @@ public class AllAppsGridAdapter extends */ public static class AdapterItemWithPayload extends AdapterItem { private T mPayload; - private Runnable mSelectionHandler; + private AllAppsSearchPlugin mPlugin; + private IntConsumer mSelectionHandler; - public AdapterItemWithPayload(T payload, int type) { - mPayload = payload; - viewType = type; + public AllAppsSearchPlugin getPlugin() { + return mPlugin; } - public void setSelectionHandler(Runnable runnable) { + public void setPlugin(AllAppsSearchPlugin plugin) { + mPlugin = plugin; + } + + public AdapterItemWithPayload(T payload, int type, AllAppsSearchPlugin plugin) { + mPayload = payload; + viewType = type; + mPlugin = plugin; + } + + public void setSelectionHandler(IntConsumer runnable) { mSelectionHandler = runnable; } - public Runnable getSelectionHandler() { + public IntConsumer getSelectionHandler() { return mSelectionHandler; } @@ -331,12 +348,6 @@ public class AllAppsGridAdapter extends mOnIconClickListener = launcher.getItemOnClickListener(); setAppsPerRow(mLauncher.getDeviceProfile().inv.numAllAppsColumns); - if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) { - mLifecycleRegistry = new LifecycleRegistry(this); - mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED); - } else { - mLifecycleRegistry = null; - } } public void setAppsPerRow(int appsPerRow) { @@ -390,10 +401,12 @@ public class AllAppsGridAdapter extends case VIEW_TYPE_ICON: BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate( R.layout.all_apps_icon, parent, false); - icon.setOnClickListener(mOnIconClickListener); - icon.setOnLongClickListener(mOnIconLongClickListener); icon.setLongPressTimeoutFactor(1f); icon.setOnFocusChangeListener(mIconFocusListener); + if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) { + icon.setOnClickListener(mOnIconClickListener); + icon.setOnLongClickListener(mOnIconLongClickListener); + } // Ensure the all apps icon height matches the workspace icons in portrait mode. icon.getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx; @@ -413,7 +426,6 @@ public class AllAppsGridAdapter extends case VIEW_TYPE_SEARCH_CORPUS_TITLE: return new ViewHolder( mLayoutInflater.inflate(R.layout.search_section_title, parent, false)); - case VIEW_TYPE_SEARCH_HERO_APP: return new ViewHolder(mLayoutInflater.inflate( R.layout.search_result_hero_app, parent, false)); @@ -426,6 +438,12 @@ public class AllAppsGridAdapter extends case VIEW_TYPE_SEARCH_SLICE: return new ViewHolder(mLayoutInflater.inflate( R.layout.search_result_slice, parent, false)); + case VIEW_TYPE_SEARCH_SHORTCUT: + return new ViewHolder(mLayoutInflater.inflate( + R.layout.search_result_shortcut, parent, false)); + case VIEW_TYPE_SEARCH_PEOPLE: + return new ViewHolder(mLayoutInflater.inflate( + R.layout.search_result_people_item, parent, false)); default: throw new RuntimeException("Unexpected view type"); } @@ -435,10 +453,36 @@ public class AllAppsGridAdapter extends public void onBindViewHolder(ViewHolder holder, int position) { switch (holder.getItemViewType()) { case VIEW_TYPE_ICON: - AppInfo info = mApps.getAdapterItems().get(position).appInfo; + AdapterItem adapterItem = mApps.getAdapterItems().get(position); + AppInfo info = adapterItem.appInfo; BubbleTextView icon = (BubbleTextView) holder.itemView; icon.reset(); icon.applyFromApplicationInfo(info); + if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) { + break; + } + //TODO: replace with custom TopHitBubbleTextView with support for both shortcut + // and apps + if (adapterItem instanceof AdapterItemWithPayload) { + AdapterItemWithPayload withPayload = (AdapterItemWithPayload) adapterItem; + IntConsumer selectionHandler = type -> { + SearchTargetEvent e = new SearchTargetEvent(SearchTarget.ItemType.APP, + type); + e.bundle = HeroSearchResultView.getAppBundle(info); + if (withPayload.getPlugin() != null) { + withPayload.getPlugin().notifySearchTargetEvent(e); + } + }; + icon.setOnClickListener(view -> { + selectionHandler.accept(SearchTargetEvent.SELECT); + mOnIconClickListener.onClick(view); + }); + icon.setOnLongClickListener(view -> { + selectionHandler.accept(SearchTargetEvent.LONG_PRESS); + return mOnIconLongClickListener.onLongClick(view); + }); + withPayload.setSelectionHandler(selectionHandler); + } break; case VIEW_TYPE_EMPTY_SEARCH: TextView emptyViewText = (TextView) holder.itemView; @@ -456,11 +500,22 @@ public class AllAppsGridAdapter extends break; case VIEW_TYPE_SEARCH_SLICE: SliceView sliceView = (SliceView) holder.itemView; - Uri uri = ((AdapterItemWithPayload) mApps.getAdapterItems().get(position)) - .getPayload(); + AdapterItemWithPayload item = + (AdapterItemWithPayload) mApps.getAdapterItems().get(position); + sliceView.setOnSliceActionListener((info1, s) -> { + if (item.getPlugin() != null) { + SearchTargetEvent searchTargetEvent = new SearchTargetEvent( + SearchTarget.ItemType.SETTINGS_SLICE, + SearchTargetEvent.CHILD_SELECT); + searchTargetEvent.bundle = new Bundle(); + searchTargetEvent.bundle.putParcelable("uri", item.getPayload()); + item.getPlugin().notifySearchTargetEvent(searchTargetEvent); + } + }); try { - LiveData liveData = SliceLiveData.fromUri(mLauncher, uri); - liveData.observe(this::getLifecycle, sliceView); + LiveData liveData = SliceLiveData.fromUri(mLauncher, item.getPayload()); + liveData.observe((Launcher) mLauncher, sliceView); + sliceView.setTag(liveData); } catch (Exception ignored) { } break; @@ -468,6 +523,8 @@ public class AllAppsGridAdapter extends case VIEW_TYPE_SEARCH_ROW_WITH_BUTTON: case VIEW_TYPE_SEARCH_HERO_APP: case VIEW_TYPE_SEARCH_ROW: + case VIEW_TYPE_SEARCH_SHORTCUT: + case VIEW_TYPE_SEARCH_PEOPLE: PayloadResultHandler payloadResultView = (PayloadResultHandler) holder.itemView; payloadResultView.applyAdapterInfo( (AdapterItemWithPayload) mApps.getAdapterItems().get(position)); @@ -478,6 +535,25 @@ public class AllAppsGridAdapter extends } } + @Override + public void onViewRecycled(@NonNull ViewHolder holder) { + super.onViewRecycled(holder); + if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return; + if (holder.itemView instanceof BubbleTextView) { + BubbleTextView icon = (BubbleTextView) holder.itemView; + icon.setOnClickListener(mOnIconClickListener); + icon.setOnLongClickListener(mOnIconLongClickListener); + } else if (holder.itemView instanceof SliceView) { + SliceView sliceView = (SliceView) holder.itemView; + sliceView.setOnSliceActionListener(null); + if (sliceView.getTag() instanceof LiveData) { + LiveData sliceLiveData = (LiveData) sliceView.getTag(); + sliceLiveData.removeObservers((Launcher) mLauncher); + } + } + } + + @Override public boolean onFailedToRecycleView(ViewHolder holder) { // Always recycle and we will reset the view when it is bound @@ -494,10 +570,4 @@ public class AllAppsGridAdapter extends AdapterItem item = mApps.getAdapterItems().get(position); return item.viewType; } - - @NonNull - @Override - public Lifecycle getLifecycle() { - return mLifecycleRegistry; - } } diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 0268b9638f..4195a056fe 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -270,11 +270,7 @@ public class AllAppsTransitionController implements StateHandler, editText.requestFocus(); } } - if (Float.compare(mProgress, 1f) == 0) { - // Called when home gesture closes all apps container. - // TODO: should make the controller hide synchronously - mInsetController.hide(); - } + // TODO: should make the controller hide synchronously } } } diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java index 2e5ed3ec35..3320189a1d 100644 --- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java +++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java @@ -79,7 +79,7 @@ public class AllAppsSearchBarController public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { if (mSearchAlgorithm instanceof PluginWrapper) { ((PluginWrapper) mSearchAlgorithm).runOnPluginIfConnected( - AllAppsSearchPlugin::startedTyping); + AllAppsSearchPlugin::startedSearchSession); } } @@ -113,6 +113,7 @@ public class AllAppsSearchBarController public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) { if (actionId == EditorInfo.IME_ACTION_SEARCH) { + // selectFocusedView should return SearchTargetEvent that is passed onto onClick if (Launcher.getLauncher(mLauncher).getAppsView().selectFocusedView(v)) { return true; } diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index d8fca1da5e..2627149167 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -149,6 +149,9 @@ public final class FeatureFlags { public static final BooleanFlag ENABLE_OVERVIEW_SHARE = getDebugFlag( "ENABLE_OVERVIEW_SHARE", false, "Show Share button in Overview Actions"); + public static final BooleanFlag ENABLE_OVERVIEW_CONTENT_PUSH = getDebugFlag( + "ENABLE_OVERVIEW_CONTENT_PUSH", false, "Show Content Push button in Overview Actions"); + public static final BooleanFlag ENABLE_DATABASE_RESTORE = getDebugFlag( "ENABLE_DATABASE_RESTORE", true, "Enable database restore when new restore session is created"); diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index f1606ea747..c841b7b0f3 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -17,6 +17,7 @@ package com.android.launcher3.folder; import static android.text.TextUtils.isEmpty; +import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; import static com.android.launcher3.LauncherState.NORMAL; @@ -34,8 +35,10 @@ import android.annotation.SuppressLint; import android.appwidget.AppWidgetHostView; import android.content.Context; import android.graphics.Canvas; +import android.graphics.Insets; import android.graphics.Path; import android.graphics.Rect; +import android.os.Build; import android.text.InputType; import android.text.Selection; import android.text.TextUtils; @@ -47,11 +50,16 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewDebug; +import android.view.WindowInsets; +import android.view.WindowInsetsAnimation; import android.view.accessibility.AccessibilityEvent; import android.view.animation.AnimationUtils; import android.view.inputmethod.EditorInfo; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Alarm; import com.android.launcher3.BubbleTextView; @@ -252,6 +260,13 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo int measureSpec = MeasureSpec.UNSPECIFIED; mFooter.measure(measureSpec, measureSpec); mFooterHeight = mFooter.getMeasuredHeight(); + + if (Utilities.ATLEAST_R) { + WindowInsetsAnimation.Callback cb = new FolderWindowInsetsAnimationCallback( + DISPATCH_MODE_STOP, this); + + setWindowInsetsAnimationCallback(cb); + } } public boolean onLongClick(View v) { @@ -373,6 +388,26 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo return false; } + @Override + public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) { + if (Utilities.ATLEAST_R) { + this.setTranslationY(0); + + if (windowInsets.isVisible(WindowInsets.Type.ime())) { + Insets keyboardInsets = windowInsets.getInsets(WindowInsets.Type.ime()); + int folderHeightFromBottom = getHeightFromBottom(); + + if (keyboardInsets.bottom > folderHeightFromBottom) { + // Translate this folder above the keyboard, then add the folder name's padding + this.setTranslationY(folderHeightFromBottom - keyboardInsets.bottom + - mFolderName.getPaddingBottom()); + } + } + } + + return windowInsets; + } + public FolderIcon getFolderIcon() { return mFolderIcon; } @@ -1646,4 +1681,64 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo mLauncher.getUserEventDispatcher() .logLauncherEvent(mInfo.getFolderLabelStateLauncherEvent(fromState, toState)); } + + /** Returns the height of the current folder's bottom edge from the bottom of the screen. */ + private int getHeightFromBottom() { + DragLayer.LayoutParams layoutParams = (DragLayer.LayoutParams) getLayoutParams(); + int folderBottomPx = layoutParams.y + layoutParams.height; + int windowBottomPx = mLauncher.getDeviceProfile().heightPx; + + return windowBottomPx - folderBottomPx; + } + + /** Callback that animates a folder sliding up above the ime. */ + @RequiresApi(api = Build.VERSION_CODES.R) + private static class FolderWindowInsetsAnimationCallback + extends WindowInsetsAnimation.Callback { + + private final Folder mFolder; + float mFolderTranslationStart; + float mFolderTranslationEnd; + + FolderWindowInsetsAnimationCallback(int dispatchMode, Folder folder) { + super(dispatchMode); + + mFolder = folder; + } + + @Override + public void onPrepare(@NonNull WindowInsetsAnimation animation) { + mFolderTranslationStart = mFolder.getTranslationY(); + } + + @NonNull + @Override + public WindowInsetsAnimation.Bounds onStart( + @NonNull WindowInsetsAnimation animation, + @NonNull WindowInsetsAnimation.Bounds bounds) { + mFolderTranslationEnd = mFolder.getTranslationY(); + + mFolder.setTranslationY(mFolderTranslationStart); + + return super.onStart(animation, bounds); + } + + @NonNull + @Override + public WindowInsets onProgress(@NonNull WindowInsets windowInsets, + @NonNull List list) { + if (list.size() == 0) { + mFolder.setTranslationY(0); + + return windowInsets; + } + float progress = list.get(0).getInterpolatedFraction(); + + mFolder.setTranslationY( + Utilities.mapRange(progress, mFolderTranslationStart, mFolderTranslationEnd)); + + return windowInsets; + } + + } } diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java index 30f8fb02a0..b2d008119e 100644 --- a/src/com/android/launcher3/testing/TestProtocol.java +++ b/src/com/android/launcher3/testing/TestProtocol.java @@ -96,6 +96,8 @@ public final class TestProtocol { public static final String REQUEST_DISABLE_DEBUG_TRACING = "disable-debug-tracing"; public static final String REQUEST_OVERVIEW_SHARE_ENABLED = "overview-share-enabled"; + public static final String REQUEST_OVERVIEW_CONTENT_PUSH_ENABLED = + "overview-content-push-enabled"; public static boolean sDisableSensorRotation; public static final String REQUEST_MOCK_SENSOR_ROTATION = "mock-sensor-rotation"; diff --git a/src/com/android/launcher3/util/VelocityUtils.java b/src/com/android/launcher3/util/VelocityUtils.java new file mode 100644 index 0000000000..d5962ed0dc --- /dev/null +++ b/src/com/android/launcher3/util/VelocityUtils.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2020 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.util; + +/** + * Contains some constants and functions to standardize velocity usage. + */ +public class VelocityUtils { + + /** + * Unit to pass to {@link android.view.VelocityTracker#computeCurrentVelocity(int)}. + */ + public static final int PX_PER_MS = 1; + +} diff --git a/src/com/android/launcher3/views/HeroSearchResultView.java b/src/com/android/launcher3/views/HeroSearchResultView.java index 761ef0d639..a8e1c6b30c 100644 --- a/src/com/android/launcher3/views/HeroSearchResultView.java +++ b/src/com/android/launcher3/views/HeroSearchResultView.java @@ -18,7 +18,9 @@ package com.android.launcher3.views; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS; import android.content.Context; +import android.content.Intent; import android.graphics.Point; +import android.os.Bundle; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; @@ -41,6 +43,9 @@ import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider; import com.android.launcher3.touch.ItemLongClickListener; +import com.android.systemui.plugins.AllAppsSearchPlugin; +import com.android.systemui.plugins.shared.SearchTarget; +import com.android.systemui.plugins.shared.SearchTargetEvent; import java.util.List; @@ -54,6 +59,7 @@ public class HeroSearchResultView extends LinearLayout implements DragSource, BubbleTextView mBubbleTextView; View mIconView; BubbleTextView[] mDeepShortcutTextViews = new BubbleTextView[2]; + AllAppsSearchPlugin mPlugin; public HeroSearchResultView(Context context) { super(context); @@ -79,7 +85,10 @@ public class HeroSearchResultView extends LinearLayout implements DragSource, mBubbleTextView = findViewById(R.id.bubble_text); - mBubbleTextView.setOnClickListener(launcher.getItemOnClickListener()); + mBubbleTextView.setOnClickListener(view -> { + handleSelection(SearchTargetEvent.SELECT); + launcher.getItemOnClickListener().onClick(view); + }); mBubbleTextView.setOnLongClickListener(new HeroItemDragHandler(getContext(), this)); setLayoutParams( new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, grid.allAppsCellHeightPx)); @@ -91,7 +100,18 @@ public class HeroSearchResultView extends LinearLayout implements DragSource, bubbleTextView.setLayoutParams( new LinearLayout.LayoutParams(grid.allAppsIconSizePx, grid.allAppsIconSizePx)); - bubbleTextView.setOnClickListener(launcher.getItemOnClickListener()); + bubbleTextView.setOnClickListener(view -> { + WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) bubbleTextView.getTag(); + SearchTargetEvent event = new SearchTargetEvent( + SearchTarget.ItemType.APP_HERO, + SearchTargetEvent.CHILD_SELECT); + event.bundle = getAppBundle(itemInfo); + event.bundle.putString("shortcut_id", itemInfo.getDeepShortcutId()); + if (mPlugin != null) { + mPlugin.notifySearchTargetEvent(event); + } + launcher.getItemOnClickListener().onClick(view); + }); } } @@ -110,6 +130,8 @@ public class HeroSearchResultView extends LinearLayout implements DragSource, mDeepShortcutTextViews[i].applyFromItemInfoWithIcon(shorcutInfos.get(i)); } } + mPlugin = adapterItem.getPlugin(); + adapterItem.setSelectionHandler(this::handleSelection); } @Override @@ -147,7 +169,38 @@ public class HeroSearchResultView extends LinearLayout implements DragSource, mLauncher.getWorkspace().beginDragShared(mContainer.mBubbleTextView, draggableView, mContainer, itemInfo, previewProvider, new DragOptions()); + SearchTargetEvent event = new SearchTargetEvent( + SearchTarget.ItemType.APP_HERO, SearchTargetEvent.LONG_PRESS); + event.bundle = getAppBundle(itemInfo); + if (mContainer.mPlugin != null) { + mContainer.mPlugin.notifySearchTargetEvent(event); + } + return false; } } + + private void handleSelection(int eventType) { + ItemInfo itemInfo = (ItemInfo) mBubbleTextView.getTag(); + if (itemInfo == null) return; + Launcher launcher = Launcher.getLauncher(getContext()); + launcher.startActivitySafely(this, itemInfo.getIntent(), itemInfo); + + SearchTargetEvent event = new SearchTargetEvent( + SearchTarget.ItemType.APP_HERO, eventType); + event.bundle = getAppBundle(itemInfo); + if (mPlugin != null) { + mPlugin.notifySearchTargetEvent(event); + } + } + + /** + * Helper method to generate {@link SearchTargetEvent} bundle from {@link ItemInfo} + */ + public static Bundle getAppBundle(ItemInfo itemInfo) { + Bundle b = new Bundle(); + b.putParcelable(Intent.EXTRA_COMPONENT_NAME, itemInfo.getTargetComponent()); + b.putParcelable(Intent.EXTRA_USER, itemInfo.user); + return b; + } } diff --git a/src/com/android/launcher3/views/SearchResultPeopleView.java b/src/com/android/launcher3/views/SearchResultPeopleView.java new file mode 100644 index 0000000000..6e45e88b32 --- /dev/null +++ b/src/com/android/launcher3/views/SearchResultPeopleView.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2020 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.views; + +import static android.content.Intent.URI_ALLOW_UNSAFE; +import static android.content.Intent.URI_ANDROID_APP_SCHEME; + +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.Nullable; +import androidx.core.graphics.drawable.RoundedBitmapDrawable; +import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Launcher; +import com.android.launcher3.R; +import com.android.launcher3.allapps.AllAppsGridAdapter; +import com.android.launcher3.allapps.search.AllAppsSearchBarController; +import com.android.launcher3.util.Themes; +import com.android.systemui.plugins.AllAppsSearchPlugin; +import com.android.systemui.plugins.shared.SearchTarget; +import com.android.systemui.plugins.shared.SearchTargetEvent; + +import java.net.URISyntaxException; +import java.util.ArrayList; + +/** + * A view representing a single people search result in all apps + */ +public class SearchResultPeopleView extends LinearLayout implements + AllAppsSearchBarController.PayloadResultHandler { + + private final int mIconSize; + private final int mButtonSize; + private final PackageManager mPackageManager; + private View mIconView; + private TextView mTitleView; + private ImageButton[] mProviderButtons = new ImageButton[3]; + private AllAppsSearchPlugin mPlugin; + private Uri mContactUri; + + + public SearchResultPeopleView(Context context) { + this(context, null, 0); + } + + public SearchResultPeopleView(Context context, + @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public SearchResultPeopleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + DeviceProfile deviceProfile = Launcher.getLauncher(getContext()).getDeviceProfile(); + mPackageManager = getContext().getPackageManager(); + mIconSize = deviceProfile.iconSizePx; + mButtonSize = (int) (deviceProfile.iconSizePx / 1.5f); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mIconView = findViewById(R.id.icon); + mIconView.getLayoutParams().height = mIconSize; + mIconView.getLayoutParams().width = mIconSize; + mTitleView = findViewById(R.id.title); + mProviderButtons[0] = findViewById(R.id.provider_0); + mProviderButtons[1] = findViewById(R.id.provider_1); + mProviderButtons[2] = findViewById(R.id.provider_2); + for (ImageButton button : mProviderButtons) { + button.getLayoutParams().width = mButtonSize; + button.getLayoutParams().height = mButtonSize; + } + setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT)); + } + + @Override + public void applyAdapterInfo( + AllAppsGridAdapter.AdapterItemWithPayload adapterItemWithPayload) { + Bundle payload = adapterItemWithPayload.getPayload(); + mPlugin = adapterItemWithPayload.getPlugin(); + mTitleView.setText(payload.getString("title")); + mContactUri = payload.getParcelable("contact_uri"); + Bitmap icon = payload.getParcelable("icon"); + if (icon != null) { + RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon); + float radius = Themes.getDialogCornerRadius(getContext()); + d.setCornerRadius(radius); + d.setBounds(0, 0, mIconSize, mIconSize); + BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), + Bitmap.createScaledBitmap(icon, mIconSize, mIconSize, false)); + mIconView.setBackground(d); + } + + ArrayList providers = payload.getParcelableArrayList("providers"); + for (int i = 0; i < mProviderButtons.length; i++) { + ImageButton button = mProviderButtons[i]; + if (providers != null && i < providers.size()) { + try { + Bundle provider = providers.get(i); + Intent intent = Intent.parseUri(provider.getString("intent_uri_str"), + URI_ANDROID_APP_SCHEME | URI_ALLOW_UNSAFE); + setupProviderButton(button, provider, intent); + String pkg = provider.getString("package_name"); + UI_HELPER_EXECUTOR.post(() -> { + try { + ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo( + pkg, 0); + Drawable appIcon = applicationInfo.loadIcon(mPackageManager); + MAIN_EXECUTOR.post(()-> button.setImageDrawable(appIcon)); + } catch (PackageManager.NameNotFoundException ignored) { + } + + }); + } catch (URISyntaxException ex) { + button.setVisibility(GONE); + } + } else { + button.setVisibility(GONE); + } + } + adapterItemWithPayload.setSelectionHandler(this::handleSelection); + } + + private void setupProviderButton(ImageButton button, Bundle provider, Intent intent) { + Launcher launcher = Launcher.getLauncher(getContext()); + button.setOnClickListener(b -> { + launcher.startActivitySafely(b, intent, null); + SearchTargetEvent searchTargetEvent = new SearchTargetEvent( + SearchTarget.ItemType.PEOPLE, + SearchTargetEvent.CHILD_SELECT); + searchTargetEvent.bundle = new Bundle(); + searchTargetEvent.bundle.putParcelable("contact_uri", mContactUri); + searchTargetEvent.bundle.putBundle("provider", provider); + if (mPlugin != null) { + mPlugin.notifySearchTargetEvent(searchTargetEvent); + } + }); + } + + + private void handleSelection(int eventType) { + if (mContactUri != null) { + Launcher launcher = Launcher.getLauncher(getContext()); + launcher.startActivitySafely(this, new Intent(Intent.ACTION_VIEW, mContactUri).setFlags( + Intent.FLAG_ACTIVITY_NEW_TASK), null); + SearchTargetEvent searchTargetEvent = new SearchTargetEvent( + SearchTarget.ItemType.PEOPLE, eventType); + searchTargetEvent.bundle = new Bundle(); + searchTargetEvent.bundle.putParcelable("contact_uri", mContactUri); + if (mPlugin != null) { + mPlugin.notifySearchTargetEvent(searchTargetEvent); + } + } + } +} diff --git a/src/com/android/launcher3/views/SearchResultPlayItem.java b/src/com/android/launcher3/views/SearchResultPlayItem.java index 19a4c5d84c..8624609f80 100644 --- a/src/com/android/launcher3/views/SearchResultPlayItem.java +++ b/src/com/android/launcher3/views/SearchResultPlayItem.java @@ -38,6 +38,9 @@ import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload; import com.android.launcher3.allapps.search.AllAppsSearchBarController; +import com.android.systemui.plugins.AllAppsSearchPlugin; +import com.android.systemui.plugins.shared.SearchTarget; +import com.android.systemui.plugins.shared.SearchTargetEvent; import java.io.IOException; import java.net.URL; @@ -54,6 +57,7 @@ public class SearchResultPlayItem extends LinearLayout implements private Button mPreviewButton; private String mPackageName; private boolean mIsInstantGame; + private AllAppsSearchPlugin mPlugin; public SearchResultPlayItem(Context context) { this(context, null, 0); @@ -84,13 +88,14 @@ public class SearchResultPlayItem extends LinearLayout implements ViewGroup.LayoutParams iconParams = mIconView.getLayoutParams(); iconParams.height = mDeviceProfile.allAppsIconSizePx; iconParams.width = mDeviceProfile.allAppsIconSizePx; - setOnClickListener(view -> handleSelection()); + setOnClickListener(view -> handleSelection(SearchTargetEvent.SELECT)); } @Override public void applyAdapterInfo(AdapterItemWithPayload adapterItemWithPayload) { Bundle bundle = adapterItemWithPayload.getPayload(); + mPlugin = adapterItemWithPayload.getPlugin(); adapterItemWithPayload.setSelectionHandler(this::handleSelection); if (bundle.getString("package", "").equals(mPackageName)) { return; @@ -129,13 +134,14 @@ public class SearchResultPlayItem extends LinearLayout implements } } - private void handleSelection() { + private void handleSelection(int eventType) { if (mPackageName == null) return; Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse( "https://play.google.com/store/apps/details?id=" + mPackageName)); i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); getContext().startActivity(i); + logSearchEvent(eventType); } private void launchInstantGame() { @@ -150,5 +156,16 @@ public class SearchResultPlayItem extends LinearLayout implements intent.putExtra("callerId", getContext().getPackageName()); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); getContext().startActivity(intent); + logSearchEvent(SearchTargetEvent.CHILD_SELECT); + } + + private void logSearchEvent(int eventType) { + SearchTargetEvent searchTargetEvent = new SearchTargetEvent( + SearchTarget.ItemType.PLAY_RESULTS, eventType); + searchTargetEvent.bundle = new Bundle(); + searchTargetEvent.bundle.putString("package_name", mPackageName); + if (mPlugin != null) { + mPlugin.notifySearchTargetEvent(searchTargetEvent); + } } } diff --git a/src/com/android/launcher3/views/SearchResultShortcut.java b/src/com/android/launcher3/views/SearchResultShortcut.java new file mode 100644 index 0000000000..307cf34c7d --- /dev/null +++ b/src/com/android/launcher3/views/SearchResultShortcut.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2020 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.views; + +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; + +import android.content.Context; +import android.content.pm.ShortcutInfo; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.R; +import com.android.launcher3.allapps.AllAppsGridAdapter; +import com.android.launcher3.allapps.search.AllAppsSearchBarController; +import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.touch.ItemClickHandler; +import com.android.systemui.plugins.AllAppsSearchPlugin; +import com.android.systemui.plugins.shared.SearchTarget; +import com.android.systemui.plugins.shared.SearchTargetEvent; + +/** + * A view representing a stand alone shortcut search result + */ +public class SearchResultShortcut extends FrameLayout implements + AllAppsSearchBarController.PayloadResultHandler { + + private BubbleTextView mBubbleTextView; + private View mIconView; + private ShortcutInfo mShortcutInfo; + private AllAppsSearchPlugin mPlugin; + + public SearchResultShortcut(@NonNull Context context) { + super(context); + } + + public SearchResultShortcut(@NonNull Context context, + @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public SearchResultShortcut(@NonNull Context context, @Nullable AttributeSet attrs, + int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + Launcher launcher = Launcher.getLauncher(getContext()); + DeviceProfile grid = launcher.getDeviceProfile(); + mIconView = findViewById(R.id.icon); + ViewGroup.LayoutParams iconParams = mIconView.getLayoutParams(); + iconParams.height = grid.allAppsIconSizePx; + iconParams.width = grid.allAppsIconSizePx; + mBubbleTextView = findViewById(R.id.bubble_text); + setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT)); + } + + @Override + public void applyAdapterInfo( + AllAppsGridAdapter.AdapterItemWithPayload adapterItemWithPayload) { + SearchTarget payload = adapterItemWithPayload.getPayload(); + mPlugin = adapterItemWithPayload.getPlugin(); + mShortcutInfo = payload.shortcuts.get(0); + WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(mShortcutInfo, getContext()); + mBubbleTextView.applyFromWorkspaceItem(workspaceItemInfo); + mIconView.setBackground(mBubbleTextView.getIcon()); + LauncherAppState launcherAppState = LauncherAppState.getInstance(getContext()); + MODEL_EXECUTOR.execute(() -> { + launcherAppState.getIconCache().getShortcutIcon(workspaceItemInfo, mShortcutInfo); + mBubbleTextView.applyFromWorkspaceItem(workspaceItemInfo); + mIconView.setBackground(mBubbleTextView.getIcon()); + }); + adapterItemWithPayload.setSelectionHandler(this::handleSelection); + } + + private void handleSelection(int eventType) { + WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) mBubbleTextView.getTag(); + ItemClickHandler.onClickAppShortcut(this, itemInfo, Launcher.getLauncher(getContext())); + + SearchTargetEvent searchTargetEvent = new SearchTargetEvent( + SearchTarget.ItemType.SHORTCUT, eventType); + searchTargetEvent.shortcut = mShortcutInfo; + if (mPlugin != null) { + mPlugin.notifySearchTargetEvent(searchTargetEvent); + } + } +} diff --git a/src/com/android/launcher3/views/SearchSettingsRowView.java b/src/com/android/launcher3/views/SearchSettingsRowView.java index 08c78ffc3a..93bcee2585 100644 --- a/src/com/android/launcher3/views/SearchSettingsRowView.java +++ b/src/com/android/launcher3/views/SearchSettingsRowView.java @@ -31,6 +31,9 @@ import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.allapps.AllAppsGridAdapter; import com.android.launcher3.allapps.search.AllAppsSearchBarController; +import com.android.systemui.plugins.AllAppsSearchPlugin; +import com.android.systemui.plugins.shared.SearchTarget; +import com.android.systemui.plugins.shared.SearchTargetEvent; import java.util.ArrayList; @@ -44,6 +47,7 @@ public class SearchSettingsRowView extends LinearLayout implements private TextView mDescriptionView; private TextView mBreadcrumbsView; private Intent mIntent; + private AllAppsSearchPlugin mPlugin; public SearchSettingsRowView(@NonNull Context context) { super(context); @@ -72,6 +76,7 @@ public class SearchSettingsRowView extends LinearLayout implements public void applyAdapterInfo( AllAppsGridAdapter.AdapterItemWithPayload adapterItemWithPayload) { Bundle bundle = adapterItemWithPayload.getPayload(); + mPlugin = adapterItemWithPayload.getPlugin(); mIntent = bundle.getParcelable("intent"); showIfAvailable(mTitleView, bundle.getString("title")); showIfAvailable(mDescriptionView, bundle.getString("description")); @@ -79,7 +84,7 @@ public class SearchSettingsRowView extends LinearLayout implements //TODO: implement RTL friendly breadcrumbs view showIfAvailable(mBreadcrumbsView, breadcrumbs != null ? String.join(" > ", breadcrumbs) : null); - adapterItemWithPayload.setSelectionHandler(() -> onClick(this)); + adapterItemWithPayload.setSelectionHandler(this::handleSelection); } private void showIfAvailable(TextView view, @Nullable String string) { @@ -93,10 +98,22 @@ public class SearchSettingsRowView extends LinearLayout implements @Override public void onClick(View view) { + handleSelection(SearchTargetEvent.SELECT); + } + + private void handleSelection(int eventType) { if (mIntent == null) return; // TODO: create ItemInfo object and then use it to call startActivityForResult for proper // WW logging - Launcher launcher = Launcher.getLauncher(view.getContext()); + Launcher launcher = Launcher.getLauncher(getContext()); launcher.startActivityForResult(mIntent, 0); + + SearchTargetEvent searchTargetEvent = new SearchTargetEvent( + SearchTarget.ItemType.SETTINGS_ROW, eventType); + searchTargetEvent.bundle = new Bundle(); + searchTargetEvent.bundle.putParcelable("intent", mIntent); + if (mPlugin != null) { + mPlugin.notifySearchTargetEvent(searchTargetEvent); + } } } diff --git a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java b/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java index be20e2dbe6..437cf3cfe5 100644 --- a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java +++ b/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java @@ -16,9 +16,9 @@ package com.android.systemui.plugins; -import android.os.Bundle; - import com.android.systemui.plugins.annotations.ProvidesInterface; +import com.android.systemui.plugins.shared.SearchTarget; +import com.android.systemui.plugins.shared.SearchTargetEvent; import java.util.List; import java.util.function.Consumer; @@ -29,15 +29,31 @@ import java.util.function.Consumer; @ProvidesInterface(action = AllAppsSearchPlugin.ACTION, version = AllAppsSearchPlugin.VERSION) public interface AllAppsSearchPlugin extends Plugin { String ACTION = "com.android.systemui.action.PLUGIN_ALL_APPS_SEARCH_ACTIONS"; - int VERSION = 4; + int VERSION = 5; + + + /** + * Send signal when user enters all apps. + */ + void startAllAppsSession(); /** * Send signal when user starts typing. */ - void startedTyping(); + void startedSearchSession(); /** * Send over the query and get the search results. */ - void performSearch(String query, Consumer> results); -} + void performSearch(String query, Consumer> results); + + /** + * Send over search target interaction events to Plugin + */ + void notifySearchTargetEvent(SearchTargetEvent event); + + /** + * Send signal when user exits all apps. + */ + void endAllAppsSession(); +} \ No newline at end of file diff --git a/src_plugins/com/android/systemui/plugins/OWNERS b/src_plugins/com/android/systemui/plugins/OWNERS deleted file mode 100644 index 0514999392..0000000000 --- a/src_plugins/com/android/systemui/plugins/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -# When changing interface for this plugin OR when increasing version code, please add Alex -# Only add other owners if Alex is not available -per-file AllAppsSearchPlugin.java, globs = set noparent -per-file AllAppsSearchPlugin.java = alexmang@google.com, hyunyoungs@google.com, sunnygoyal@google.com, twickham@google.com diff --git a/src_plugins/com/android/systemui/plugins/shared/SearchTarget.java b/src_plugins/com/android/systemui/plugins/shared/SearchTarget.java new file mode 100644 index 0000000000..c6b8300499 --- /dev/null +++ b/src_plugins/com/android/systemui/plugins/shared/SearchTarget.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2020 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.systemui.plugins.shared; + +import android.content.pm.ShortcutInfo; +import android.os.Bundle; + +import java.util.List; + +/** + * Used to return all apps search targets. + */ +public class SearchTarget implements Comparable { + + public enum ViewType { + TOP_HIT(0), + HERO(1), + DETAIL(2), + ROW(3), + ROW_WITH_BUTTON(4), + SLICE(5), + SHORTCUT(6), + PEOPLE(7); + + private final int mId; + ViewType(int id) { + mId = id; + } + + public int get() { + return mId; + } + } + + public enum ItemType { + PLAY_RESULTS(0, "Play Store", ViewType.DETAIL), + SETTINGS_ROW(1, "Settings", ViewType.ROW), + SETTINGS_SLICE(2, "Settings", ViewType.SLICE), + APP(3, "", ViewType.TOP_HIT), + APP_HERO(4, "", ViewType.HERO), + SHORTCUT(5, "Shortcuts", ViewType.SHORTCUT), + PEOPLE(6, "People", ViewType.PEOPLE); + + private final int mId; + private final String mTitle; + private final ViewType mViewType; + + ItemType(int id, String title, ViewType type) { + mId = id; + mTitle = title; + mViewType = type; + } + + public ViewType getViewType() { + return mViewType; + } + + public String getTitle() { + return mTitle; + } + + public int getId() { + return mId; + } + } + + public ItemType type; + public List shortcuts; + public Bundle bundle; + public float score; + + /** + * Constructor to create the search target. Bundle is currently temporary to hold + * search target primitives that cannot be expressed as java primitive objects + * or AOSP native objects. + * + */ + public SearchTarget(ItemType itemType, List shortcuts, + Bundle bundle, float score) { + this.type = itemType; + this.shortcuts = shortcuts; + this.bundle = bundle; + this.score = score; + } + + @Override + public int compareTo(SearchTarget o) { + return Float.compare(o.score, score); + } +} diff --git a/src_plugins/com/android/systemui/plugins/shared/SearchTargetEvent.java b/src_plugins/com/android/systemui/plugins/shared/SearchTargetEvent.java new file mode 100644 index 0000000000..ac4bc333c6 --- /dev/null +++ b/src_plugins/com/android/systemui/plugins/shared/SearchTargetEvent.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 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.systemui.plugins.shared; + +import android.content.pm.ShortcutInfo; +import android.os.Bundle; + +/** + * Event used for the feedback loop to the plugin. (and future aiai) + */ +public class SearchTargetEvent { + public static final int SELECT = 0; + public static final int QUICK_SELECT = 1; + public static final int LONG_PRESS = 2; + public static final int CHILD_SELECT = 3; + + public SearchTarget.ItemType type; + public ShortcutInfo shortcut; + public int eventType; + public Bundle bundle; + public float score; + + public SearchTargetEvent(SearchTarget.ItemType itemType, int eventType) { + this.type = itemType; + this.eventType = eventType; + } +} diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java index 34ebbac59a..b4e45f835a 100644 --- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java +++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java @@ -234,7 +234,7 @@ public class WidgetsModel { WidgetValidityCheck(LauncherAppState app) { mIdp = app.getInvariantDeviceProfile(); - mAppFilter = AppFilter.newInstance(app.getContext()); + mAppFilter = new AppFilter(app.getContext()); } @Override diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 80adf05c2b..22833ec16d 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -1313,6 +1313,11 @@ public final class LauncherInstrumentation { TestProtocol.TEST_INFO_RESPONSE_FIELD); } + boolean overviewContentPushEnabled() { + return getTestInfo(TestProtocol.REQUEST_OVERVIEW_CONTENT_PUSH_ENABLED).getBoolean( + TestProtocol.TEST_INFO_RESPONSE_FIELD); + } + private void disableSensorRotation() { getTestInfo(TestProtocol.REQUEST_MOCK_SENSOR_ROTATION); } diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java index a30a404764..e3e0f4209d 100644 --- a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java +++ b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java @@ -33,6 +33,27 @@ public class OverviewActions { this.mLauncher = launcherInstrumentation; } + /** + * Clicks content push button. + */ + @NonNull + public Overview clickAndDismissContentPush() { + if (mLauncher.overviewContentPushEnabled()) { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "want to click content push button and exit screenshot ui")) { + UiObject2 exo = mLauncher.waitForObjectInContainer(mOverviewActions, + "action_content_push"); + mLauncher.clickLauncherObject(exo); + try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( + "clicked content push button")) { + return new Overview(mLauncher); + } + } + } + return new Overview(mLauncher); + } + /** * Clicks screenshot button and closes screenshot ui. */