diff options
author | Hyunyoung Song <hyunyoungs@google.com> | 2019-09-13 15:54:36 -0700 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2019-09-13 15:54:36 -0700 |
commit | 1d43b107b3004ac6557fff550f34b7e30a93541e (patch) | |
tree | 71acb03e62bb1ba508ba241fb2e52a586fbe9ed9 | |
parent | 0fb01f7cf9c27f269e8fe7a3c2e5a14971d5a30d (diff) | |
parent | a7a1b57bc8b211ca64598aa42e83eb21789056b9 (diff) | |
download | Launcher3-1d43b107b3004ac6557fff550f34b7e30a93541e.tar.gz |
Merging ub-launcher3-qt-qpr1-dev, build 5872416
am: a7a1b57bc8
Change-Id: I60dd1710d7c54e2e47b926453fd79a2c2031edd4
54 files changed, 879 insertions, 306 deletions
@@ -10,3 +10,6 @@ mrcasey@google.com sunnygoyal@google.com twickham@google.com winsonc@google.com + +per-file FeatureFlags.java = sunnygoyal@google.com, adamcohen@google.com +per-file BaseFlags.java = sunnygoyal@google.com, adamcohen@google.com diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java index d0cfcf97a5..212ce9bef6 100644 --- a/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java +++ b/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java @@ -32,6 +32,7 @@ import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7; import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE; +import android.content.Context; import android.view.View; import com.android.launcher3.DeviceProfile; @@ -115,10 +116,10 @@ public class OverviewState extends LauncherState { } public static float getDefaultSwipeHeight(Launcher launcher) { - return getDefaultSwipeHeight(launcher.getDeviceProfile()); + return getDefaultSwipeHeight(launcher, launcher.getDeviceProfile()); } - public static float getDefaultSwipeHeight(DeviceProfile dp) { + public static float getDefaultSwipeHeight(Context context, DeviceProfile dp) { return dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx; } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java index 371161ebdc..711594386f 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java @@ -18,16 +18,11 @@ package com.android.launcher3; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; import static com.android.launcher3.LauncherState.NORMAL; -import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch; import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator; -import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_PIXELS; -import static androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY; -import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM; - import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -35,18 +30,17 @@ import android.animation.ObjectAnimator; import android.content.Context; import android.view.View; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; -import com.android.launcher3.anim.SpringObjectAnimator; +import com.android.launcher3.anim.SpringAnimationBuilder; import com.android.quickstep.util.ClipAnimationHelper; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + /** * A {@link QuickstepAppTransitionManagerImpl} that also implements recents transitions from * {@link RecentsView}. @@ -156,8 +150,11 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti return ObjectAnimator.ofFloat(mLauncher.getOverviewPanel(), RecentsView.CONTENT_ALPHA, values); case INDEX_RECENTS_TRANSLATE_X_ANIM: - return new SpringObjectAnimator<>(mLauncher.getOverviewPanel(), - VIEW_TRANSLATE_X, MIN_VISIBLE_CHANGE_PIXELS, 0.8f, 250, values); + return new SpringAnimationBuilder<>(mLauncher.getOverviewPanel(), VIEW_TRANSLATE_X) + .setDampingRatio(0.8f) + .setStiffness(250) + .setValues(values) + .build(mLauncher); default: return super.createStateElementAnimation(index, values); } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java index 085bbc4a5e..1a59770a02 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java @@ -24,7 +24,6 @@ import android.app.prediction.AppPredictor; import android.app.prediction.AppTarget; import android.content.ComponentName; import android.content.Context; -import android.view.ViewTreeObserver.OnGlobalLayoutListener; import com.android.launcher3.AppInfo; import com.android.launcher3.InvariantDeviceProfile; @@ -32,6 +31,8 @@ import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener; import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherState; +import com.android.launcher3.LauncherStateManager.StateListener; import com.android.launcher3.Utilities; import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener; @@ -58,7 +59,7 @@ import java.util.List; * 4) Maintains the current active client id (for the predictions) and all updates are performed on * that client id. */ -public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInfoUpdateReceiver, +public class PredictionUiStateManager implements StateListener, ItemInfoUpdateReceiver, OnIDPChangeListener, OnUpdateListener { public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state"; @@ -153,7 +154,10 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf public void reapplyItemInfo(ItemInfoWithIcon info) { } @Override - public void onGlobalLayout() { + public void onStateTransitionStart(LauncherState toState) { } + + @Override + public void onStateTransitionComplete(LauncherState state) { if (mAppsView == null) { return; } @@ -162,7 +166,8 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf mPendingState = null; } if (mPendingState == null) { - mAppsView.getViewTreeObserver().removeOnGlobalLayoutListener(this); + Launcher.getLauncher(mAppsView.getContext()).getStateManager() + .removeStateListener(this); } } @@ -170,9 +175,8 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf boolean registerListener = mPendingState == null; mPendingState = state; if (registerListener) { - // OnGlobalLayoutListener is called whenever a view in the view tree changes - // visibility. Add a listener and wait until appsView is invisible again. - mAppsView.getViewTreeObserver().addOnGlobalLayoutListener(this); + // Add a listener and wait until appsView is invisible again. + Launcher.getLauncher(mAppsView.getContext()).getStateManager().addStateListener(this); } } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java index 151ceb8347..93d4de17de 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java @@ -32,6 +32,7 @@ import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7; import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE; +import android.content.Context; import android.graphics.Rect; import android.view.View; @@ -159,11 +160,15 @@ public class OverviewState extends LauncherState { } public static float getDefaultSwipeHeight(Launcher launcher) { - return getDefaultSwipeHeight(launcher.getDeviceProfile()); + return getDefaultSwipeHeight(launcher, launcher.getDeviceProfile()); } - public static float getDefaultSwipeHeight(DeviceProfile dp) { - return dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx; + public static float getDefaultSwipeHeight(Context context, DeviceProfile dp) { + float swipeHeight = dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx; + if (SysUINavigationMode.getMode(context) == SysUINavigationMode.Mode.NO_BUTTON) { + swipeHeight -= dp.getInsets().bottom; + } + return swipeHeight; } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java index 295585e002..25cc33df01 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java @@ -40,7 +40,6 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.os.UserHandle; -import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.animation.Interpolator; @@ -58,7 +57,6 @@ import com.android.launcher3.LauncherStateManager; import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorSetBuilder; -import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.uioverrides.states.OverviewState; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.views.FloatingIconView; @@ -202,9 +200,6 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe // This ensures then the next swipe up to all-apps starts from scroll 0. activity.getAppsView().reset(false /* animate */); - // Optimization, hide the all apps view to prevent layout while initializing - activity.getAppsView().getContentView().setVisibility(View.GONE); - return new AnimationFactory() { private ShelfAnimState mShelfState; private boolean mIsAttachedToWindow; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java index 4eb9df2cbd..da4642636d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java @@ -24,7 +24,7 @@ public class QuickstepTestInformationHandler extends TestInformationHandler { switch (method) { case TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT: { final float swipeHeight = - OverviewState.getDefaultSwipeHeight(mDeviceProfile); + OverviewState.getDefaultSwipeHeight(mContext, mDeviceProfile); response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight); return response; } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java index 38b5a137c4..346969e592 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java @@ -1,3 +1,4 @@ + /* * Copyright (C) 2019 The Android Open Source Project * @@ -82,7 +83,8 @@ public class AssistantTouchConsumer extends DelegateInputConsumer { private int mDirection; private ActivityControlHelper mActivityControlHelper; - private final float mDistThreshold; + private final float mDragDistThreshold; + private final float mFlingDistThreshold; private final long mTimeThreshold; private final int mAngleThreshold; private final float mSquaredSlop; @@ -97,7 +99,8 @@ public class AssistantTouchConsumer extends DelegateInputConsumer { final Resources res = context.getResources(); mContext = context; mSysUiProxy = systemUiProxy; - mDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold); + mDragDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold); + mFlingDistThreshold = res.getDimension(R.dimen.gestures_assistant_fling_threshold); mTimeThreshold = res.getInteger(R.integer.assistant_gesture_min_time_threshold); mAngleThreshold = res.getInteger(R.integer.assistant_gesture_corner_deg_threshold); @@ -117,8 +120,6 @@ public class AssistantTouchConsumer extends DelegateInputConsumer { @Override public void onMotionEvent(MotionEvent ev) { // TODO add logging - mGestureDetector.onTouchEvent(ev); - switch (ev.getActionMasked()) { case ACTION_DOWN: { mActivePointerId = ev.getPointerId(0); @@ -213,6 +214,8 @@ public class AssistantTouchConsumer extends DelegateInputConsumer { break; } + mGestureDetector.onTouchEvent(ev); + if (mState != STATE_ACTIVE) { mDelegate.onMotionEvent(ev); } @@ -220,9 +223,9 @@ public class AssistantTouchConsumer extends DelegateInputConsumer { private void updateAssistantProgress() { if (!mLaunchedAssistant) { - mLastProgress = Math.min(mDistance * 1f / mDistThreshold, 1) * mTimeFraction; + mLastProgress = Math.min(mDistance * 1f / mDragDistThreshold, 1) * mTimeFraction; try { - if (mDistance >= mDistThreshold && mTimeFraction >= 1) { + if (mDistance >= mDragDistThreshold && mTimeFraction >= 1) { mSysUiProxy.onAssistantGestureCompletion(0); startAssistantInternal(SWIPE); @@ -271,7 +274,9 @@ public class AssistantTouchConsumer extends DelegateInputConsumer { @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { if (isValidAssistantGestureAngle(velocityX, -velocityY) - && !mLaunchedAssistant && mState != STATE_DELEGATE_ACTIVE) { + && mDistance >= mFlingDistThreshold + && !mLaunchedAssistant + && mState != STATE_DELEGATE_ACTIVE) { mLastProgress = 1; try { mSysUiProxy.onAssistantGestureCompletion( diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java index 1069bed592..1aa5365fd2 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java @@ -29,6 +29,7 @@ import android.view.ViewGroup; import androidx.annotation.Nullable; +import com.android.launcher3.BubbleTextView; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; @@ -40,7 +41,9 @@ import com.android.launcher3.Workspace; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.anim.SpringObjectAnimator; +import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.graphics.OverviewScrim; +import com.android.launcher3.views.IconLabelDotView; import java.util.ArrayList; import java.util.List; @@ -62,7 +65,9 @@ public class StaggeredWorkspaceAnim { private final float mVelocity; private final float mSpringTransY; - private final View mViewToIgnore; + + // The original view of the {@link FloatingIconView}. + private final View mOriginalView; private final List<Animator> mAnimators = new ArrayList<>(); @@ -72,9 +77,7 @@ public class StaggeredWorkspaceAnim { public StaggeredWorkspaceAnim(Launcher launcher, @Nullable View floatingViewOriginalView, float velocity) { mVelocity = velocity; - // We ignore this view since it's visibility and position is controlled by - // the FloatingIconView. - mViewToIgnore = floatingViewOriginalView; + mOriginalView = floatingViewOriginalView; // Scale the translationY based on the initial velocity to better sync the workspace items // with the floating view. @@ -86,16 +89,21 @@ public class StaggeredWorkspaceAnim { Workspace workspace = launcher.getWorkspace(); CellLayout cellLayout = (CellLayout) workspace.getChildAt(workspace.getCurrentPage()); ShortcutAndWidgetContainer currentPage = cellLayout.getShortcutsAndWidgets(); + ViewGroup hotseat = launcher.getHotseat(); boolean workspaceClipChildren = workspace.getClipChildren(); boolean workspaceClipToPadding = workspace.getClipToPadding(); boolean cellLayoutClipChildren = cellLayout.getClipChildren(); boolean cellLayoutClipToPadding = cellLayout.getClipToPadding(); + boolean hotseatClipChildren = hotseat.getClipChildren(); + boolean hotseatClipToPadding = hotseat.getClipToPadding(); workspace.setClipChildren(false); workspace.setClipToPadding(false); cellLayout.setClipChildren(false); cellLayout.setClipToPadding(false); + hotseat.setClipChildren(false); + hotseat.setClipToPadding(false); // Hotseat and QSB takes up two additional rows. int totalRows = grid.inv.numRows + (grid.isVerticalBarLayout() ? 0 : 2); @@ -108,16 +116,18 @@ public class StaggeredWorkspaceAnim { } // Set up springs for the hotseat and qsb. + ViewGroup hotseatChild = (ViewGroup) hotseat.getChildAt(0); if (grid.isVerticalBarLayout()) { - ViewGroup hotseat = (ViewGroup) launcher.getHotseat().getChildAt(0); - for (int i = hotseat.getChildCount() - 1; i >= 0; i--) { - View child = hotseat.getChildAt(i); + for (int i = hotseatChild.getChildCount() - 1; i >= 0; i--) { + View child = hotseatChild.getChildAt(i); CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams()); addStaggeredAnimationForView(child, lp.cellY + 1, totalRows); } } else { - View hotseat = launcher.getHotseat().getChildAt(0); - addStaggeredAnimationForView(hotseat, grid.inv.numRows + 1, totalRows); + for (int i = hotseatChild.getChildCount() - 1; i >= 0; i--) { + View child = hotseatChild.getChildAt(i); + addStaggeredAnimationForView(child, grid.inv.numRows + 1, totalRows); + } View qsb = launcher.findViewById(R.id.search_container_all_apps); addStaggeredAnimationForView(qsb, grid.inv.numRows + 2, totalRows); @@ -140,6 +150,8 @@ public class StaggeredWorkspaceAnim { workspace.setClipToPadding(workspaceClipToPadding); cellLayout.setClipChildren(cellLayoutClipChildren); cellLayout.setClipToPadding(cellLayoutClipToPadding); + hotseat.setClipChildren(hotseatClipChildren); + hotseat.setClipToPadding(hotseatClipToPadding); } }; @@ -180,16 +192,35 @@ public class StaggeredWorkspaceAnim { springTransY.setStartDelay(startDelay); mAnimators.add(springTransY); - if (v == mViewToIgnore) { - return; + ObjectAnimator alpha = getAlphaAnimator(v, startDelay); + if (v == mOriginalView) { + // For IconLabelDotViews, we just want the label to fade in. + // Icon, badge, and dots will animate in separately (controlled via FloatingIconView) + if (v instanceof IconLabelDotView) { + alpha.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + IconLabelDotView view = (IconLabelDotView) v; + view.setIconVisible(false); + view.setForceHideDot(true); + } + }); + } else { + return; + } } v.setAlpha(0); + mAnimators.add(alpha); + } + + private ObjectAnimator getAlphaAnimator(View v, long startDelay) { ObjectAnimator alpha = ObjectAnimator.ofFloat(v, View.ALPHA, 0f, 1f); alpha.setInterpolator(LINEAR); alpha.setDuration(ALPHA_DURATION_MS); alpha.setStartDelay(startDelay); - mAnimators.add(alpha); + return alpha; + } private void addScrimAnimationForState(Launcher launcher, LauncherState state, long duration) { diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index b0968f94c0..78424ca516 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -69,6 +69,7 @@ <!-- Distance from the vertical edges of the screen in which assist gestures are recognized --> <dimen name="gestures_assistant_width">48dp</dimen> <dimen name="gestures_assistant_drag_threshold">55dp</dimen> + <dimen name="gestures_assistant_fling_threshold">55dp</dimen> <!-- Distance to move elements when swiping up to go home from launcher --> <dimen name="home_pullback_distance">28dp</dimen> diff --git a/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java b/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java index e425088147..853a1c6acb 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java +++ b/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java @@ -16,17 +16,34 @@ package com.android.launcher3.uioverrides; +import android.content.Context; import android.provider.DeviceConfig; import com.android.launcher3.config.BaseFlags.BaseTogglableFlag; public class TogglableFlag extends BaseTogglableFlag { + public static final String NAMESPACE_LAUNCHER = "launcher"; + public static final String TAG = "TogglableFlag"; public TogglableFlag(String key, boolean defaultValue, String description) { super(key, defaultValue, description); } @Override - public boolean getInitialValue(boolean value) { - return DeviceConfig.getBoolean("launcher", getKey(), value); + public boolean getOverridenDefaultValue(boolean value) { + return DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, getKey(), value); + } + + @Override + public void addChangeListener(Context context, Runnable r) { + DeviceConfig.addOnPropertiesChangedListener( + NAMESPACE_LAUNCHER, + context.getMainExecutor(), + (properties) -> { + if (!NAMESPACE_LAUNCHER.equals(properties.getNamespace())) { + return; + } + initialize(context); + r.run(); + }); } } diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java index 3747f9a8b1..dc6b56eecf 100644 --- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java +++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java @@ -156,12 +156,14 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis mDragHandleProgress = 1; mMidAlpha = 0; } else { - mMidAlpha = Themes.getAttrInteger(getContext(), R.attr.allAppsInterimScrimAlpha); + Context context = getContext(); + mMidAlpha = Themes.getAttrInteger(context, R.attr.allAppsInterimScrimAlpha); mMidProgress = OVERVIEW.getVerticalProgress(mLauncher); Rect hotseatPadding = dp.getHotseatLayoutPadding(); int hotseatSize = dp.hotseatBarSizePx + dp.getInsets().bottom - hotseatPadding.bottom - hotseatPadding.top; - float dragHandleTop = Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(dp)); + float dragHandleTop = + Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(context, dp)); mDragHandleProgress = 1 - (dragHandleTop / mShiftRange); } mTopOffset = dp.getInsets().top - mShelfOffset; diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java index ec3d49afad..a7c33a9549 100644 --- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java +++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java @@ -69,10 +69,9 @@ public class DigitalWellBeingToastTest extends AbstractQuickStepTest { private DigitalWellBeingToast getToast() { executeOnLauncher(launcher -> launcher.getStateManager().goToState(OVERVIEW)); waitForState("Launcher internal state didn't switch to Overview", OVERVIEW); - waitForLauncherCondition("No latest task", launcher -> getLatestTask(launcher) != null); + final TaskView task = getOnceNotNull("No latest task", launcher -> getLatestTask(launcher)); return getFromLauncher(launcher -> { - final TaskView task = getLatestTask(launcher); assertTrue("Latest task is not Calculator", CALCULATOR_PACKAGE.equals(task.getTask().getTopComponent().getPackageName())); return task.getDigitalWellBeingToast(); diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java index edd5011f23..e29552713d 100644 --- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java +++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java @@ -118,7 +118,6 @@ public class NavigationModeSwitchRule implements TestRule { assertTrue("Couldn't set overlay", setActiveOverlay(prevOverlayPkg, originalMode)); } - mLauncher.disableDebugTracing(); } private void evaluateWithThreeButtons() throws Throwable { diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java index be8506d83e..885fdbf423 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java @@ -97,7 +97,6 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @Test @PortraitLandscape public void testOverview() throws Exception { - mLauncher.enableDebugTracing(); startTestApps(); Overview overview = mLauncher.pressHome().switchToOverview(); assertTrue("Launcher internal state didn't switch to Overview", @@ -177,7 +176,6 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { executeOnLauncher( launcher -> assertEquals("Still have tasks after dismissing all", 0, getTaskCount(launcher))); - mLauncher.disableDebugTracing(); } private int getCurrentOverviewPage(Launcher launcher) { diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java index c84be4dacd..864fa6e8a6 100644 --- a/src/com/android/launcher3/BaseRecyclerView.java +++ b/src/com/android/launcher3/BaseRecyclerView.java @@ -22,6 +22,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.views.RecyclerViewFastScroller; import androidx.recyclerview.widget.RecyclerView; @@ -171,4 +172,13 @@ public abstract class BaseRecyclerView extends RecyclerView { * <p>Override in each subclass of this base class. */ public void onFastScrollCompleted() {} + + @Override + public void onScrollStateChanged(int state) { + super.onScrollStateChanged(state); + + if (state == SCROLL_STATE_IDLE) { + AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext()); + } + } }
\ No newline at end of file diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 22c69f59a5..b1132494a2 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -32,7 +32,6 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.text.TextUtils.TruncateAt; import android.util.AttributeSet; -import android.util.Log; import android.util.Property; import android.util.TypedValue; import android.view.KeyEvent; @@ -54,8 +53,8 @@ import com.android.launcher3.icons.DotRenderer; import com.android.launcher3.icons.IconCache.IconLoadRequest; import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver; import com.android.launcher3.model.PackageItemInfo; -import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.views.ActivityContext; +import com.android.launcher3.views.IconLabelDotView; import java.text.NumberFormat; @@ -64,7 +63,8 @@ import java.text.NumberFormat; * because we want to make the bubble taller than the text and TextView's clip is * too aggressive. */ -public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback { +public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback, + IconLabelDotView { private static final int DISPLAY_WORKSPACE = 0; private static final int DISPLAY_ALL_APPS = 1; @@ -413,7 +413,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } } - public void forceHideDot(boolean forceHideDot) { + @Override + public void setForceHideDot(boolean forceHideDot) { if (mForceHideDot == forceHideDot) { return; } @@ -602,6 +603,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } } + @Override public void setIconVisible(boolean visible) { mIsIconVisible = visible; Drawable icon = visible ? mIcon : new ColorDrawable(Color.TRANSPARENT); diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java index 7ab88a0083..a90025e97f 100644 --- a/src/com/android/launcher3/FastBitmapDrawable.java +++ b/src/com/android/launcher3/FastBitmapDrawable.java @@ -142,8 +142,11 @@ public class FastBitmapDrawable extends Drawable { @Override public void setAlpha(int alpha) { - mAlpha = alpha; - mPaint.setAlpha(alpha); + if (mAlpha != alpha) { + mAlpha = alpha; + mPaint.setAlpha(alpha); + invalidateSelf(); + } } @Override diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index d79230f8be..257f0dfcf3 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -157,6 +157,7 @@ import java.util.List; import java.util.function.Predicate; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; /** * Default launcher application. @@ -209,9 +210,9 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500; // How long to wait before the new-shortcut animation automatically pans the workspace - private static final int NEW_APPS_PAGE_MOVE_DELAY = 500; + @VisibleForTesting public static final int NEW_APPS_PAGE_MOVE_DELAY = 500; private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5; - @Thunk static final int NEW_APPS_ANIMATION_DELAY = 500; + @Thunk @VisibleForTesting public static final int NEW_APPS_ANIMATION_DELAY = 500; private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 1; private static final int SCRIM_VIEW_ALPHA_CHANNEL_INDEX = 0; diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 2a801d6ef8..b4a2216c57 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -94,6 +94,7 @@ public class LauncherAppState { if (FeatureFlags.IS_DOGFOOD_BUILD) { filter.addAction(ACTION_FORCE_ROLOAD); } + FeatureFlags.APP_SEARCH_IMPROVEMENTS.addChangeListener(context, mModel::forceReload); mContext.registerReceiver(mModel, filter); UserManagerCompat.getInstance(mContext).enableAndResetCache(); diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index d79f5d5a94..a0414894b1 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -20,6 +20,7 @@ import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD; import static com.android.launcher3.config.FeatureFlags.IS_DOGFOOD_BUILD; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ShortcutInfo; @@ -51,6 +52,7 @@ import com.android.launcher3.model.UserLockStateChangedTask; import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.IntArray; +import com.android.launcher3.util.IntSparseArrayMap; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.Preconditions; @@ -211,6 +213,30 @@ public class LauncherModel extends BroadcastReceiver enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName)); } + public void onSessionFailure(String packageName, UserHandle user) { + enqueueModelUpdateTask(new BaseModelUpdateTask() { + @Override + public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { + final IntSparseArrayMap<Boolean> removedIds = new IntSparseArrayMap<>(); + synchronized (dataModel) { + for (ItemInfo info : dataModel.itemsIdMap) { + if (info instanceof WorkspaceItemInfo + && ((WorkspaceItemInfo) info).hasPromiseIconUi() + && user.equals(info.user) + && info.getIntent() != null + && TextUtils.equals(packageName, info.getIntent().getPackage())) { + removedIds.put(info.id, true /* remove */); + } + } + } + + if (!removedIds.isEmpty()) { + deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds, false)); + } + } + }); + } + @Override public void onPackageRemoved(String packageName, UserHandle user) { onPackagesRemoved(user, packageName); diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java index e1ea3be04e..63914b0eb5 100644 --- a/src/com/android/launcher3/LauncherStateManager.java +++ b/src/com/android/launcher3/LauncherStateManager.java @@ -228,8 +228,9 @@ public class LauncherStateManager { private void goToState(LauncherState state, boolean animated, long delay, final Runnable onCompleteRunnable) { if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "goToState: " + state + " @ " + - Log.getStackTraceString(new Throwable())); + Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "goToState: " + + state.getClass().getSimpleName() + + " @ " + Log.getStackTraceString(new Throwable())); } animated &= Utilities.areAnimationsEnabled(mLauncher); if (mLauncher.isInState(state)) { @@ -411,6 +412,11 @@ public class LauncherStateManager { mState.onStateDisabled(mLauncher); } mState = state; + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.STABLE_STATE_MISMATCH, "onStateTransitionStart: " + + state.getClass().getSimpleName() + + " @ " + Log.getStackTraceString(new Throwable())); + } mState.onStateEnabled(mLauncher); mLauncher.onStateSet(mState); @@ -431,7 +437,9 @@ public class LauncherStateManager { mLastStableState = state.getHistoryForState(mCurrentStableState); mCurrentStableState = state; if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "onStateTransitionEnd: " + state); + Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "onStateTransitionEnd: " + + state.getClass().getSimpleName() + + " @ " + Log.getStackTraceString(new Throwable())); } } diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index 0c1303b64b..0c12c60a08 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -1,5 +1,7 @@ package com.android.launcher3.accessibility; +import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK; + import static com.android.launcher3.LauncherState.NORMAL; import android.app.AlertDialog; @@ -30,14 +32,14 @@ import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.PendingAddItemInfo; import com.android.launcher3.R; -import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.Workspace; +import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.dragndrop.DragController.DragListener; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.folder.Folder; +import com.android.launcher3.keyboard.CustomActionsPopup; import com.android.launcher3.notification.NotificationListener; import com.android.launcher3.popup.PopupContainerWithArrow; -import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.ShortcutUtil; @@ -164,6 +166,13 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme } public boolean performAction(final View host, final ItemInfo item, int action) { + if (action == ACTION_LONG_CLICK && ShortcutUtil.isDeepShortcut(item)) { + CustomActionsPopup popup = new CustomActionsPopup(mLauncher, host); + if (popup.canShow()) { + popup.show(); + return true; + } + } if (action == MOVE) { beginAccessibleDrag(host, item); } else if (action == ADD_TO_WORKSPACE) { diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index a0e9dc5d8e..f82e380f88 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -26,13 +26,15 @@ import android.util.SparseIntArray; import android.view.MotionEvent; import android.view.View; +import androidx.recyclerview.widget.RecyclerView; + import com.android.launcher3.BaseRecyclerView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; -import com.android.launcher3.Utilities; +import com.android.launcher3.allapps.AllAppsGridAdapter.AppsGridLayoutManager; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; @@ -41,8 +43,6 @@ import com.android.launcher3.views.RecyclerViewFastScroller; import java.util.List; -import androidx.recyclerview.widget.RecyclerView; - /** * A RecyclerView with custom fast scroll support for the all apps view. */ @@ -114,6 +114,13 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine if (mScrollbar != null) { mScrollbar.reattachThumbToScroll(); } + if (getLayoutManager() instanceof AppsGridLayoutManager) { + AppsGridLayoutManager layoutManager = (AppsGridLayoutManager) getLayoutManager(); + if (layoutManager.findFirstCompletelyVisibleItemPosition() == 0) { + // We are at the top, so don't scrollToPosition (would cause unnecessary relayout). + return; + } + } scrollToPosition(0); } @@ -420,13 +427,4 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine public boolean hasOverlappingRendering() { return false; } - - @Override - public void onScrollStateChanged(int state) { - super.onScrollStateChanged(state); - - if (state == SCROLL_STATE_IDLE) { - AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext()); - } - } } diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 5b3beeca7f..3836c9fdb4 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -18,7 +18,6 @@ import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.util.FloatProperty; -import android.util.Log; import android.view.animation.Interpolator; import com.android.launcher3.DeviceProfile; @@ -32,7 +31,6 @@ import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.anim.SpringObjectAnimator; -import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.util.Themes; import com.android.launcher3.views.ScrimView; diff --git a/src/com/android/launcher3/anim/SpringAnimationBuilder.java b/src/com/android/launcher3/anim/SpringAnimationBuilder.java new file mode 100644 index 0000000000..0f34c1e97e --- /dev/null +++ b/src/com/android/launcher3/anim/SpringAnimationBuilder.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.anim; + +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.content.Context; +import android.util.FloatProperty; + +import com.android.launcher3.util.DefaultDisplay; + +import androidx.annotation.FloatRange; +import androidx.dynamicanimation.animation.SpringForce; + +/** + * Utility class to build an object animator which follows the same path as a spring animation for + * an underdamped spring. + */ +public class SpringAnimationBuilder<T> extends FloatProperty<T> { + + private final T mTarget; + private final FloatProperty<T> mProperty; + + private float mStartValue; + private float mEndValue; + private float mVelocity = 0; + + private float mStiffness = SpringForce.STIFFNESS_MEDIUM; + private float mDampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY; + private float mMinVisibleChange = 1; + + // Multiplier to the min visible change value for value threshold + private static final float THRESHOLD_MULTIPLIER = 0.65f; + + /** + * The spring equation is given as + * x = e^(-beta*t/2) * (a cos(gamma * t) + b sin(gamma * t) + * v = e^(-beta*t/2) * ((2 * a * gamma + beta * b) * sin(gamma * t) + * + (a * beta - 2 * b * gamma) * cos(gamma * t)) / 2 + * + * a = x(0) + * b = beta * x(0) / (2 * gamma) + v(0) / gamma + */ + private double beta; + private double gamma; + + private double a, b; + private double va, vb; + + // Threshold for velocity and value to determine when it's reasonable to assume that the spring + // is approximately at rest. + private double mValueThreshold; + private double mVelocityThreshold; + + private float mCurrentTime = 0; + + public SpringAnimationBuilder(T target, FloatProperty<T> property) { + super("dynamic-spring-property"); + mTarget = target; + mProperty = property; + + mStartValue = mProperty.get(target); + } + + public SpringAnimationBuilder<T> setEndValue(float value) { + mEndValue = value; + return this; + } + + public SpringAnimationBuilder<T> setStartValue(float value) { + mStartValue = value; + return this; + } + + public SpringAnimationBuilder<T> setValues(float... values) { + if (values.length > 1) { + mStartValue = values[0]; + mEndValue = values[values.length - 1]; + } else { + mEndValue = values[0]; + } + return this; + } + + public SpringAnimationBuilder<T> setStiffness( + @FloatRange(from = 0.0, fromInclusive = false) float stiffness) { + if (stiffness <= 0) { + throw new IllegalArgumentException("Spring stiffness constant must be positive."); + } + mStiffness = stiffness; + return this; + } + + public SpringAnimationBuilder<T> setDampingRatio( + @FloatRange(from = 0.0, to = 1.0, fromInclusive = false, toInclusive = false) + float dampingRatio) { + if (dampingRatio <= 0 || dampingRatio >= 1) { + throw new IllegalArgumentException("Damping ratio must be between 0 and 1"); + } + mDampingRatio = dampingRatio; + return this; + } + + public SpringAnimationBuilder<T> setMinimumVisibleChange( + @FloatRange(from = 0.0, fromInclusive = false) float minimumVisibleChange) { + if (minimumVisibleChange <= 0) { + throw new IllegalArgumentException("Minimum visible change must be positive."); + } + mMinVisibleChange = minimumVisibleChange; + return this; + } + + public SpringAnimationBuilder<T> setStartVelocity(float startVelocity) { + mVelocity = startVelocity; + return this; + } + + @Override + public void setValue(T object, float time) { + mCurrentTime = time; + mProperty.setValue( + object, (float) (exponentialComponent(time) * cosSinX(time)) + mEndValue); + } + + @Override + public Float get(T t) { + return mCurrentTime; + } + + public ObjectAnimator build(Context context) { + int singleFrameMs = DefaultDisplay.getSingleFrameMs(context); + double naturalFreq = Math.sqrt(mStiffness); + double dampedFreq = naturalFreq * Math.sqrt(1 - mDampingRatio * mDampingRatio); + + // All the calculations assume the stable position to be 0, shift the values accordingly. + beta = 2 * mDampingRatio * naturalFreq; + gamma = dampedFreq; + a = mStartValue - mEndValue; + b = beta * a / (2 * gamma) + mVelocity / gamma; + + va = a * beta / 2 - b * gamma; + vb = a * gamma + beta * b / 2; + + mValueThreshold = mMinVisibleChange * THRESHOLD_MULTIPLIER; + + // This multiplier is used to calculate the velocity threshold given a certain value + // threshold. The idea is that if it takes >= 1 frame to move the value threshold amount, + // then the velocity is a reasonable threshold. + mVelocityThreshold = mValueThreshold * 1000.0 / singleFrameMs; + + // Find the duration (in seconds) for the spring to reach equilibrium. + // equilibrium is reached when x = 0 + double duration = Math.atan2(-a, b) / gamma; + + // Keep moving ahead until the velocity reaches equilibrium. + double piByG = Math.PI / gamma; + while (duration < 0 || Math.abs(exponentialComponent(duration) * cosSinV(duration)) + >= mVelocityThreshold) { + duration += piByG; + } + + // Find the shortest time + double edgeTime = Math.max(0, duration - piByG / 2); + double minDiff = singleFrameMs / 2000.0; // Half frame time in seconds + + do { + if ((duration - edgeTime) < minDiff) { + break; + } + double mid = (edgeTime + duration) / 2; + if (isAtEquilibrium(mid)) { + duration = mid; + } else { + edgeTime = mid; + } + } while (true); + + + long durationMs = (long) (1000.0 * duration); + ObjectAnimator animator = ObjectAnimator.ofFloat(mTarget, this, 0, (float) duration); + animator.setDuration(durationMs).setInterpolator(Interpolators.LINEAR); + animator.addListener(new AnimationSuccessListener() { + @Override + public void onAnimationSuccess(Animator animator) { + mProperty.setValue(mTarget, mEndValue); + } + }); + return animator; + } + + private boolean isAtEquilibrium(double t) { + double ec = exponentialComponent(t); + + if (Math.abs(ec * cosSinX(t)) >= mValueThreshold) { + return false; + } + return Math.abs(ec * cosSinV(t)) < mVelocityThreshold; + } + + private double exponentialComponent(double t) { + return Math.pow(Math.E, - beta * t / 2); + } + + private double cosSinX(double t) { + return cosSin(t, a, b); + } + + private double cosSinV(double t) { + return cosSin(t, va, vb); + } + + private double cosSin(double t, double cosFactor, double sinFactor) { + double angle = t * gamma; + return cosFactor * Math.cos(angle) + sinFactor * Math.sin(angle); + } +} diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java index e1f17cf461..ee9da73727 100644 --- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java +++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java @@ -136,6 +136,30 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat { } } + /** + * Add a promise app icon to the workspace iff: + * - The settings for it are enabled + * - The user installed the app + * - There is an app icon and label (For apps with no launching activity, no icon is provided). + * - The app is not already installed + * - A promise icon for the session has not already been created + */ + private void tryQueuePromiseAppIcon(SessionInfo sessionInfo) { + if (Utilities.ATLEAST_OREO && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get() + && SessionCommitReceiver.isEnabled(mAppContext) + && verify(sessionInfo) != null + && sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER + && sessionInfo.getAppIcon() != null + && !TextUtils.isEmpty(sessionInfo.getAppLabel()) + && !mPromiseIconIds.contains(sessionInfo.getSessionId()) + && mLauncherApps.getApplicationInfo(sessionInfo.getAppPackageName(), 0, + getUserHandle(sessionInfo)) == null) { + SessionCommitReceiver.queuePromiseAppIconAddition(mAppContext, sessionInfo); + mPromiseIconIds.add(sessionInfo.getSessionId()); + updatePromiseIconPrefs(); + } + } + private final SessionCallback mCallback = new SessionCallback() { @Override @@ -149,16 +173,7 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat { } } - if (Utilities.ATLEAST_OREO && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get() - && SessionCommitReceiver.isEnabled(mAppContext) - && sessionInfo != null - && sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER) { - SessionCommitReceiver.queuePromiseAppIconAddition(mAppContext, sessionInfo); - if (!mPromiseIconIds.contains(sessionInfo.getSessionId())) { - mPromiseIconIds.add(sessionInfo.getSessionId()); - updatePromiseIconPrefs(); - } - } + tryQueuePromiseAppIcon(sessionInfo); } @Override @@ -173,12 +188,14 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat { sendUpdate(PackageInstallInfo.fromState(success ? STATUS_INSTALLED : STATUS_FAILED, packageName, key.mUser)); - if (!success && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()) { + if (!success && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get() + && mPromiseIconIds.contains(sessionId)) { LauncherAppState appState = LauncherAppState.getInstanceNoCreate(); if (appState != null) { - LauncherModel model = appState.getModel(); - model.onPackageRemoved(packageName, key.mUser); + appState.getModel().onSessionFailure(packageName, key.mUser); } + // If it is successful, the id is removed in the the package added flow. + removePromiseIconId(sessionId); } } } @@ -196,7 +213,10 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat { @Override public void onBadgingChanged(int sessionId) { - pushSessionDisplayToLauncher(sessionId); + SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId); + if (sessionInfo != null) { + tryQueuePromiseAppIcon(sessionInfo); + } } private SessionInfo pushSessionDisplayToLauncher(int sessionId) { diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java index ea6261a6a6..025087b9d8 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -143,6 +143,8 @@ public abstract class BaseFlags { public static abstract class BaseTogglableFlag { private final String key; + // should be value that is hardcoded in client side. + // Comparatively, getDefaultValue() can be overridden. private final boolean defaultValue; private final String description; private boolean currentValue; @@ -152,8 +154,9 @@ public abstract class BaseFlags { boolean defaultValue, String description) { this.key = checkNotNull(key); - this.currentValue = this.defaultValue = getInitialValue(defaultValue); + this.currentValue = this.defaultValue = defaultValue; this.description = checkNotNull(description); + synchronized (sLock) { sFlags.add((TogglableFlag)this); } @@ -169,16 +172,18 @@ public abstract class BaseFlags { return key; } - void initialize(Context context) { - currentValue = getFromStorage(context, defaultValue); + protected void initialize(Context context) { + currentValue = getFromStorage(context, getDefaultValue()); } - protected abstract boolean getInitialValue(boolean value); + protected abstract boolean getOverridenDefaultValue(boolean value); + + protected abstract void addChangeListener(Context context, Runnable r); public void updateStorage(Context context, boolean value) { SharedPreferences.Editor editor = context.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE).edit(); - if (value == defaultValue) { + if (value == getDefaultValue()) { editor.remove(key).apply(); } else { editor.putBoolean(key, value).apply(); @@ -187,11 +192,11 @@ public abstract class BaseFlags { boolean getFromStorage(Context context, boolean defaultValue) { return context.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE) - .getBoolean(key, defaultValue); + .getBoolean(key, getDefaultValue()); } boolean getDefaultValue() { - return defaultValue; + return getOverridenDefaultValue(defaultValue); } /** Returns the value of the flag at process start, including any overrides present. */ @@ -208,6 +213,8 @@ public abstract class BaseFlags { return "TogglableFlag{" + "key=" + key + ", " + "defaultValue=" + defaultValue + ", " + + "overriddenDefaultValue=" + getOverridenDefaultValue(defaultValue) + ", " + + "currentValue=" + currentValue + ", " + "description=" + description + "}"; } @@ -220,7 +227,7 @@ public abstract class BaseFlags { if (o instanceof TogglableFlag) { BaseTogglableFlag that = (BaseTogglableFlag) o; return (this.key.equals(that.getKey())) - && (this.defaultValue == that.getDefaultValue()) + && (this.getDefaultValue() == that.getDefaultValue()) && (this.description.equals(that.getDescription())); } return false; @@ -232,7 +239,7 @@ public abstract class BaseFlags { h$ *= 1000003; h$ ^= key.hashCode(); h$ *= 1000003; - h$ ^= defaultValue ? 1231 : 1237; + h$ ^= getDefaultValue() ? 1231 : 1237; h$ *= 1000003; h$ ^= description.hashCode(); return h$; diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java index bd2a03b6c2..01e0f923c1 100644 --- a/src/com/android/launcher3/dragndrop/DragDriver.java +++ b/src/com/android/launcher3/dragndrop/DragDriver.java @@ -61,6 +61,9 @@ public abstract class DragDriver { mEventListener.onDriverDragEnd(ev.getX(), ev.getY()); break; case MotionEvent.ACTION_CANCEL: + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "DragDriver.ACTION_CANCEL"); + } mEventListener.onDriverDragCancel(); break; } diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 2ef6d707e3..f22b533380 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -516,7 +516,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { - mFolderIcon.setBackgroundVisible(false); + mFolderIcon.setIconVisible(false); mFolderIcon.drawLeaveBehindIfExists(); } @Override @@ -646,7 +646,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo clearFocus(); if (mFolderIcon != null) { mFolderIcon.setVisibility(View.VISIBLE); - mFolderIcon.setBackgroundVisible(true); + mFolderIcon.setIconVisible(true); mFolderIcon.mFolderName.setTextVisibility(true); if (wasAnimated) { mFolderIcon.animateBgShadowAndStroke(); diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 250169cdb2..0e2d4673e4 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -65,6 +65,7 @@ import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.icons.DotRenderer; import com.android.launcher3.touch.ItemClickHandler; import com.android.launcher3.util.Thunk; +import com.android.launcher3.views.IconLabelDotView; import com.android.launcher3.widget.PendingAddShortcutInfo; import java.util.ArrayList; @@ -73,7 +74,7 @@ import java.util.List; /** * An icon that can appear on in the workspace representing an {@link Folder}. */ -public class FolderIcon extends FrameLayout implements FolderListener { +public class FolderIcon extends FrameLayout implements FolderListener, IconLabelDotView { @Thunk Launcher mLauncher; @Thunk Folder mFolder; @@ -107,6 +108,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { private Alarm mOpenAlarm = new Alarm(); + private boolean mForceHideDot; @ViewDebug.ExportedProperty(category = "launcher", deepExport = true) private FolderDotInfo mDotInfo = new FolderDotInfo(); private DotRenderer mDotRenderer; @@ -409,6 +411,20 @@ public class FolderIcon extends FrameLayout implements FolderListener { return mPreviewLayoutRule; } + @Override + public void setForceHideDot(boolean forceHideDot) { + if (mForceHideDot == forceHideDot) { + return; + } + mForceHideDot = forceHideDot; + + if (forceHideDot) { + invalidate(); + } else if (hasDot()) { + animateDotScale(0, 1); + } + } + /** * Sets mDotScale to 1 or 0, animating if wasDotted or isDotted is false * (the dot is being added or removed). @@ -468,7 +484,8 @@ public class FolderIcon extends FrameLayout implements FolderListener { mBackground.setInvalidateDelegate(this); } - public void setBackgroundVisible(boolean visible) { + @Override + public void setIconVisible(boolean visible) { mBackgroundIsVisible = visible; invalidate(); } @@ -509,7 +526,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { } public void drawDot(Canvas canvas) { - if ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0) { + if (!mForceHideDot && ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0)) { Rect iconBounds = mDotParams.iconBounds; BubbleTextView.getIconBounds(this, iconBounds, mLauncher.getWallpaperDeviceProfile().iconSizePx); diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java index 55d58b9c09..abff237e8b 100644 --- a/src/com/android/launcher3/icons/IconCache.java +++ b/src/com/android/launcher3/icons/IconCache.java @@ -42,6 +42,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic; import com.android.launcher3.icons.cache.BaseIconCache; import com.android.launcher3.icons.cache.CachingLogic; @@ -237,7 +238,8 @@ public class IconCache extends BaseIconCache { @Override protected String getIconSystemState(String packageName) { - return mIconProvider.getSystemStateForPackage(mSystemState, packageName); + return mIconProvider.getSystemStateForPackage(mSystemState, packageName) + + ",flags_asi:" + FeatureFlags.APP_SEARCH_IMPROVEMENTS.get(); } public static abstract class IconLoadRequest extends HandlerRunnable { diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index baaad65736..15fb4cea6f 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -36,7 +36,6 @@ import android.os.Build; import android.os.Handler; import android.os.Looper; import android.util.AttributeSet; -import android.util.Log; import android.util.Pair; import android.view.MotionEvent; import android.view.View; @@ -53,7 +52,6 @@ import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherModel; import com.android.launcher3.R; -import com.android.launcher3.Utilities; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate; import com.android.launcher3.dot.DotInfo; @@ -65,10 +63,8 @@ import com.android.launcher3.notification.NotificationInfo; import com.android.launcher3.notification.NotificationItemView; import com.android.launcher3.notification.NotificationKeyData; import com.android.launcher3.popup.PopupDataProvider.PopupDataChangeListener; -import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.shortcuts.DeepShortcutView; import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider; -import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.touch.ItemClickHandler; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.util.PackageUserKey; @@ -301,7 +297,7 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource, } mLauncher.getDragController().addDragListener(this); - mOriginalIcon.forceHideDot(true); + mOriginalIcon.setForceHideDot(true); // All views are added. Animate layout from now on. setLayoutTransition(new LayoutTransition()); @@ -564,14 +560,14 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource, protected void onCreateCloseAnimation(AnimatorSet anim) { // Animate original icon's text back in. anim.play(mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */)); - mOriginalIcon.forceHideDot(false); + mOriginalIcon.setForceHideDot(false); } @Override protected void closeComplete() { super.closeComplete(); mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible()); - mOriginalIcon.forceHideDot(false); + mOriginalIcon.setForceHideDot(false); } @Override diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java index 3c0c5fddea..d643a0b493 100644 --- a/src/com/android/launcher3/provider/RestoreDbTask.java +++ b/src/com/android/launcher3/provider/RestoreDbTask.java @@ -173,12 +173,6 @@ public class RestoreDbTask { values.put(Favorites.PROFILE_ID, newProfileId); db.update(Favorites.TABLE_NAME, values, "profileId = ?", new String[]{Long.toString(oldProfileId)}); - - // Change default value of the column. - db.execSQL("ALTER TABLE favorites RENAME TO favorites_old;"); - Favorites.addTableToDb(db, newProfileId, false); - db.execSQL("INSERT INTO favorites SELECT * FROM favorites_old;"); - dropTable(db, "favorites_old"); } diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java index 4fd0f884d6..243ff6f965 100644 --- a/src/com/android/launcher3/testing/TestInformationHandler.java +++ b/src/com/android/launcher3/testing/TestInformationHandler.java @@ -17,6 +17,7 @@ package com.android.launcher3.testing; import android.content.Context; import android.os.Bundle; +import android.os.Debug; import com.android.launcher3.DeviceProfile; import com.android.launcher3.InvariantDeviceProfile; @@ -113,13 +114,13 @@ public class TestInformationHandler implements ResourceBasedOverride { break; } - case TestProtocol.REQUEST_ALLOCATED_MEMORY: { - final Runtime runtime = Runtime.getRuntime(); - response.putLong(TestProtocol.TEST_INFO_RESPONSE_FIELD, - runtime.totalMemory() - runtime.freeMemory()); + case TestProtocol.REQUEST_TOTAL_PSS_KB: { + Debug.MemoryInfo mem = new Debug.MemoryInfo(); + Debug.getMemoryInfo(mem); + response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, mem.getTotalPss()); break; } } return response; } -} +}
\ No newline at end of file diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java index 520598adfc..60a59ddfac 100644 --- a/src/com/android/launcher3/testing/TestProtocol.java +++ b/src/com/android/launcher3/testing/TestProtocol.java @@ -73,7 +73,7 @@ public final class TestProtocol { public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags"; public static final String REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN = "overview-left-margin"; public static final String REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN = "overview-right-margin"; - public static final String REQUEST_ALLOCATED_MEMORY = "allocated-memory"; + public static final String REQUEST_TOTAL_PSS_KB = "total_pss"; public static boolean sDebugTracing = false; public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing"; @@ -83,4 +83,5 @@ public final class TestProtocol { public static final String NO_DRAG_TO_WORKSPACE = "b/138729456"; public static final String APP_NOT_DISABLED = "b/139891609"; public static final String ALL_APPS_UPON_RECENTS = "b/139941530"; + public static final String STABLE_STATE_MISMATCH = "b/140311911"; } diff --git a/src/com/android/launcher3/util/ShortcutUtil.java b/src/com/android/launcher3/util/ShortcutUtil.java index 792d69fc36..af99713a1a 100644 --- a/src/com/android/launcher3/util/ShortcutUtil.java +++ b/src/com/android/launcher3/util/ShortcutUtil.java @@ -23,37 +23,57 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.shortcuts.ShortcutKey; public class ShortcutUtil { - public static boolean supportsShortcuts(ItemInfo info) { - return isActive(info) && (isApp(info) || isPinnedShortcut(info)); - } - - public static boolean supportsDeepShortcuts(ItemInfo info) { - return isActive(info) && isApp(info); - } - - public static String getShortcutIdIfPinnedShortcut(ItemInfo info) { - return isActive(info) && isPinnedShortcut(info) ? - ShortcutKey.fromItemInfo(info).getId() : null; - } - - public static String[] getPersonKeysIfPinnedShortcut(ItemInfo info) { - return isActive(info) && isPinnedShortcut(info) ? - ((WorkspaceItemInfo) info).getPersonKeys() : Utilities.EMPTY_STRING_ARRAY; - } - - private static boolean isActive(ItemInfo info) { - boolean isLoading = info instanceof WorkspaceItemInfo - && ((WorkspaceItemInfo) info).hasPromiseIconUi(); - return !isLoading && !info.isDisabled() && !FeatureFlags.GO_DISABLE_WIDGETS; - } - - private static boolean isApp(ItemInfo info) { - return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; - } - - private static boolean isPinnedShortcut(ItemInfo info) { - return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT - && info.container != ItemInfo.NO_ID - && info instanceof WorkspaceItemInfo; - } + /** + * Returns true when we should show shortcut menu for the item. + */ + public static boolean supportsShortcuts(ItemInfo info) { + return isActive(info) && (isApp(info) || isPinnedShortcut(info)); + } + + /** + * Returns true when we should show depp shortcuts in shortcut menu for the item. + */ + public static boolean supportsDeepShortcuts(ItemInfo info) { + return isActive(info) && isApp(info); + } + + /** + * Returns the shortcut id if the item is a pinned shortcut. + */ + public static String getShortcutIdIfPinnedShortcut(ItemInfo info) { + return isActive(info) && isPinnedShortcut(info) + ? ShortcutKey.fromItemInfo(info).getId() : null; + } + + /** + * Returns the person keys associated with the item. (Has no function right now.) + */ + public static String[] getPersonKeysIfPinnedShortcut(ItemInfo info) { + return isActive(info) && isPinnedShortcut(info) + ? ((WorkspaceItemInfo) info).getPersonKeys() : Utilities.EMPTY_STRING_ARRAY; + } + + /** + * Returns true if the item is a deep shortcut. + */ + public static boolean isDeepShortcut(ItemInfo info) { + return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT + && info instanceof WorkspaceItemInfo; + } + + private static boolean isActive(ItemInfo info) { + boolean isLoading = info instanceof WorkspaceItemInfo + && ((WorkspaceItemInfo) info).hasPromiseIconUi(); + return !isLoading && !info.isDisabled() && !FeatureFlags.GO_DISABLE_WIDGETS; + } + + private static boolean isApp(ItemInfo info) { + return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; + } + + private static boolean isPinnedShortcut(ItemInfo info) { + return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT + && info.container != ItemInfo.NO_ID + && info instanceof WorkspaceItemInfo; + } }
\ No newline at end of file diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java index f948beb8d2..a4518bae3c 100644 --- a/src/com/android/launcher3/views/AbstractSlideInView.java +++ b/src/com/android/launcher3/views/AbstractSlideInView.java @@ -153,15 +153,15 @@ public abstract class AbstractSlideInView extends AbstractFloatingView } protected void handleClose(boolean animate, long defaultDuration) { - if (mIsOpen && !animate) { + if (!mIsOpen) { + return; + } + if (!animate) { mOpenCloseAnimator.cancel(); setTranslationShift(TRANSLATION_SHIFT_CLOSED); onCloseComplete(); return; } - if (!mIsOpen) { - return; - } mOpenCloseAnimator.setValues( PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_CLOSED)); mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java index 799762d8fe..c08b659313 100644 --- a/src/com/android/launcher3/views/BaseDragLayer.java +++ b/src/com/android/launcher3/views/BaseDragLayer.java @@ -29,6 +29,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.os.Build; import android.util.AttributeSet; +import android.util.Log; import android.util.Property; import android.view.MotionEvent; import android.view.View; @@ -41,6 +42,7 @@ import android.widget.FrameLayout; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.Utilities; +import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.util.MultiValueAlpha; import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; import com.android.launcher3.util.TouchController; @@ -261,6 +263,10 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext> } case ACTION_CANCEL: case ACTION_UP: + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, + "BaseDragLayer.ACTION_UP/CANCEL " + ev); + } mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE; mTouchDispatchState &= ~TOUCH_DISPATCHING_VIEW; break; diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index e09a9e8bfc..f728a67764 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -18,7 +18,6 @@ package com.android.launcher3.views; import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA; import static com.android.launcher3.Utilities.getBadge; import static com.android.launcher3.Utilities.getFullDrawable; -import static com.android.launcher3.Utilities.isRtl; import static com.android.launcher3.Utilities.mapToRange; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM; @@ -564,11 +563,6 @@ public class FloatingIconView extends View implements */ private void checkIconResult(View originalView, boolean isOpening) { CancellationSignal cancellationSignal = new CancellationSignal(); - if (!isOpening) { - // Hide immediately since the floating view starts at a different location. - originalView.setVisibility(INVISIBLE); - cancellationSignal.setOnCancelListener(() -> originalView.setVisibility(VISIBLE)); - } if (mIconLoadResult == null) { Log.w(TAG, "No icon load result found in checkIconResult"); @@ -580,7 +574,7 @@ public class FloatingIconView extends View implements setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge, mIconLoadResult.iconOffset); if (isOpening) { - originalView.setVisibility(INVISIBLE); + hideOriginalView(originalView); } } else { mIconLoadResult.onIconLoaded = () -> { @@ -591,15 +585,26 @@ public class FloatingIconView extends View implements setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge, mIconLoadResult.iconOffset); - // Delay swapping views until the icon is loaded to prevent a flash. setVisibility(VISIBLE); - originalView.setVisibility(INVISIBLE); + if (isOpening) { + // Delay swapping views until the icon is loaded to prevent a flash. + hideOriginalView(originalView); + } }; mLoadIconSignal = cancellationSignal; } } } + private void hideOriginalView(View originalView) { + if (originalView instanceof BubbleTextView) { + ((BubbleTextView) originalView).setIconVisible(false); + ((BubbleTextView) originalView).setForceHideDot(true); + } else { + originalView.setVisibility(INVISIBLE); + } + } + private void setBackgroundDrawableBounds(float scale) { sTmpRect.set(mFinalDrawableBounds); Utilities.scaleRectAboutCenter(sTmpRect, scale); @@ -716,7 +721,7 @@ public class FloatingIconView extends View implements */ @UiThread public static IconLoadResult fetchIcon(Launcher l, View v, ItemInfo info, boolean isOpening) { - IconLoadResult result = new IconLoadResult(); + IconLoadResult result = new IconLoadResult(info); new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(() -> { RectF position = new RectF(); getLocationBoundsForView(l, v, isOpening, position); @@ -745,10 +750,13 @@ public class FloatingIconView extends View implements // Get the drawable on the background thread boolean shouldLoadIcon = originalView.getTag() instanceof ItemInfo && hideOriginal; - view.mIconLoadResult = sIconLoadResult; - if (shouldLoadIcon && view.mIconLoadResult == null) { - view.mIconLoadResult = fetchIcon(launcher, originalView, - (ItemInfo) originalView.getTag(), isOpening); + if (shouldLoadIcon) { + if (sIconLoadResult != null && sIconLoadResult.itemInfo == originalView.getTag()) { + view.mIconLoadResult = sIconLoadResult; + } else { + view.mIconLoadResult = fetchIcon(launcher, originalView, + (ItemInfo) originalView.getTag(), isOpening); + } } sIconLoadResult = null; @@ -776,7 +784,12 @@ public class FloatingIconView extends View implements if (hideOriginal) { if (isOpening) { - originalView.setVisibility(VISIBLE); + if (originalView instanceof BubbleTextView) { + ((BubbleTextView) originalView).setIconVisible(true); + ((BubbleTextView) originalView).setForceHideDot(false); + } else { + originalView.setVisibility(VISIBLE); + } view.finish(dragLayer); } else { view.mFadeAnimatorSet = view.createFadeAnimation(originalView, dragLayer); @@ -804,38 +817,33 @@ public class FloatingIconView extends View implements } }); - if (mBadge != null && !(mOriginalIcon instanceof FolderIcon)) { + if (mBadge != null) { ObjectAnimator badgeFade = ObjectAnimator.ofInt(mBadge, DRAWABLE_ALPHA, 255); badgeFade.addUpdateListener(valueAnimator -> invalidate()); fade.play(badgeFade); } - if (originalView instanceof BubbleTextView) { - BubbleTextView btv = (BubbleTextView) originalView; - btv.forceHideDot(true); + if (originalView instanceof IconLabelDotView) { + IconLabelDotView view = (IconLabelDotView) originalView; fade.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - btv.forceHideDot(false); + view.setIconVisible(true); + view.setForceHideDot(false); } }); } - if (originalView instanceof FolderIcon) { - FolderIcon folderIcon = (FolderIcon) originalView; - folderIcon.setBackgroundVisible(false); - folderIcon.getFolderName().setTextVisibility(false); - fade.play(folderIcon.getFolderName().createTextAlphaAnimator(true)); + if (originalView instanceof BubbleTextView) { + BubbleTextView btv = (BubbleTextView) originalView; fade.addListener(new AnimatorListenerAdapter() { @Override - public void onAnimationEnd(Animator animation) { - folderIcon.setBackgroundVisible(true); - if (folderIcon.hasDot()) { - folderIcon.animateDotScale(0, 1f); - } + public void onAnimationStart(Animator animation) { + btv.setIconVisible(true); } }); - } else { + fade.play(ObjectAnimator.ofInt(btv.getIcon(), DRAWABLE_ALPHA, 0, 255)); + } else if (!(originalView instanceof FolderIcon)) { fade.play(ObjectAnimator.ofFloat(originalView, ALPHA, 0f, 1f)); } @@ -890,10 +898,15 @@ public class FloatingIconView extends View implements } private static class IconLoadResult { + final ItemInfo itemInfo; Drawable drawable; Drawable badge; int iconOffset; Runnable onIconLoaded; boolean isIconLoaded; + + public IconLoadResult(ItemInfo itemInfo) { + this.itemInfo = itemInfo; + } } } diff --git a/src/com/android/launcher3/views/IconLabelDotView.java b/src/com/android/launcher3/views/IconLabelDotView.java new file mode 100644 index 0000000000..057caafe78 --- /dev/null +++ b/src/com/android/launcher3/views/IconLabelDotView.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.views; + +/** + * A view that has an icon, label, and notification dot. + */ +public interface IconLabelDotView { + void setIconVisible(boolean visible); + void setForceHideDot(boolean hide); +} diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java index 63f742768f..465df448e3 100644 --- a/src/com/android/launcher3/views/OptionsPopupView.java +++ b/src/com/android/launcher3/views/OptionsPopupView.java @@ -18,10 +18,8 @@ package com.android.launcher3.views; import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_FLAVOR; import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_OFFSET; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.pm.ResolveInfo; import android.graphics.Rect; import android.graphics.RectF; import android.text.TextUtils; @@ -33,6 +31,9 @@ import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.widget.Toast; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Utilities; @@ -46,7 +47,6 @@ import com.android.launcher3.widget.WidgetsFullSheet; import java.util.ArrayList; import java.util.List; -import androidx.annotation.VisibleForTesting; /** * Popup shown on long pressing an empty space in launcher @@ -169,16 +169,17 @@ public class OptionsPopupView extends ArrowPopup } public static boolean onWidgetsClicked(View view) { - return openWidgets(Launcher.getLauncher(view.getContext())); + return openWidgets(Launcher.getLauncher(view.getContext())) != null; } - public static boolean openWidgets(Launcher launcher) { + /** Returns WidgetsFullSheet that was opened, or null if nothing was opened. */ + @Nullable + public static WidgetsFullSheet openWidgets(Launcher launcher) { if (launcher.getPackageManager().isSafeMode()) { Toast.makeText(launcher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show(); - return false; + return null; } else { - WidgetsFullSheet.show(launcher, true /* animated */); - return true; + return WidgetsFullSheet.show(launcher, true /* animated */); } } diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java index c36011745c..da1df3f899 100644 --- a/src/com/android/launcher3/views/ScrimView.java +++ b/src/com/android/launcher3/views/ScrimView.java @@ -18,14 +18,14 @@ package com.android.launcher3.views; import static android.content.Context.ACCESSIBILITY_SERVICE; import static android.view.MotionEvent.ACTION_DOWN; +import static androidx.core.graphics.ColorUtils.compositeColors; + import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; -import static androidx.core.graphics.ColorUtils.compositeColors; - import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.Keyframe; @@ -47,6 +47,13 @@ import android.view.View; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.view.ViewCompat; +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat; +import androidx.customview.widget.ExploreByTouchHelper; + import com.android.launcher3.DeviceProfile; import com.android.launcher3.Insettable; import com.android.launcher3.Launcher; @@ -62,15 +69,10 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType; import com.android.launcher3.util.MultiValueAlpha; import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; import com.android.launcher3.util.Themes; +import com.android.launcher3.widget.WidgetsFullSheet; import java.util.List; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.view.ViewCompat; -import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; -import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat; -import androidx.customview.widget.ExploreByTouchHelper; /** * Simple scrim which draws a flat color @@ -325,7 +327,7 @@ public class ScrimView extends View implements Insettable, OnChangeListener, if (enabled) { stateManager.addStateListener(this); - handleStateChangedComplete(mLauncher.getStateManager().getState()); + handleStateChangedComplete(stateManager.getState()); } else { setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); } @@ -437,7 +439,24 @@ public class ScrimView extends View implements Insettable, OnChangeListener, } else if (action == WALLPAPERS) { return OptionsPopupView.startWallpaperPicker(ScrimView.this); } else if (action == WIDGETS) { - return OptionsPopupView.onWidgetsClicked(ScrimView.this); + int originalImportanceForAccessibility = getImportantForAccessibility(); + setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); + WidgetsFullSheet widgetsFullSheet = OptionsPopupView.openWidgets(mLauncher); + if (widgetsFullSheet == null) { + setImportantForAccessibility(originalImportanceForAccessibility); + return false; + } + widgetsFullSheet.addOnAttachStateChangeListener(new OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View view) {} + + @Override + public void onViewDetachedFromWindow(View view) { + setImportantForAccessibility(originalImportanceForAccessibility); + widgetsFullSheet.removeOnAttachStateChangeListener(this); + } + }); + return true; } else if (action == SETTINGS) { return OptionsPopupView.startSettings(ScrimView.this); } diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java b/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java index e875a3c465..60f12d82a7 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java @@ -16,6 +16,7 @@ package com.android.launcher3.uioverrides; +import android.content.Context; import com.android.launcher3.config.BaseFlags.BaseTogglableFlag; public class TogglableFlag extends BaseTogglableFlag { @@ -25,7 +26,10 @@ public class TogglableFlag extends BaseTogglableFlag { } @Override - public boolean getInitialValue(boolean value) { + public boolean getOverridenDefaultValue(boolean value) { return value; } + + @Override + public void addChangeListener(Context context, Runnable r) { } } diff --git a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java new file mode 100644 index 0000000000..efbd9c99c3 --- /dev/null +++ b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.android.launcher3.compat; + +import android.content.pm.PackageInstaller.SessionParams; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.text.TextUtils; + +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.Workspace; +import com.android.launcher3.ui.AbstractLauncherUiTest; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.UUID; + + +/** + * Test to verify promise icon flow. + */ +@LargeTest +@RunWith(AndroidJUnit4.class) +public class PromiseIconUiTest extends AbstractLauncherUiTest { + + private int mSessionId = -1; + + @Override + public void setUp() throws Exception { + super.setUp(); + mDevice.pressHome(); + waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null); + waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL); + mSessionId = -1; + } + + @After + public void tearDown() { + if (mSessionId > -1) { + mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId); + } + } + + /** + * Create a session and return the id. + */ + private int createSession(String label, Bitmap icon) throws Throwable { + SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL); + params.setAppPackageName("test.promise.app"); + params.setAppLabel(label); + params.setAppIcon(icon); + params.setInstallReason(PackageManager.INSTALL_REASON_USER); + return mTargetContext.getPackageManager().getPackageInstaller().createSession(params); + } + + @Test + public void testPromiseIcon_addedFromEligibleSession() throws Throwable { + final String appLabel = "Test Promise App " + UUID.randomUUID().toString(); + final Workspace.ItemOperator findPromiseApp = (info, view) -> + info != null && TextUtils.equals(info.title, appLabel); + + // Create and add test session + mSessionId = createSession(appLabel, Bitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8)); + + // Verify promise icon is added + waitForLauncherCondition("Test Promise App not found on workspace", launcher -> + launcher.getWorkspace().getFirstMatch(findPromiseApp) != null); + + // Remove session + mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId); + mSessionId = -1; + + // Verify promise icon is removed + waitForLauncherCondition("Test Promise App not removed from workspace", launcher -> + launcher.getWorkspace().getFirstMatch(findPromiseApp) == null); + } + + @Test + public void testPromiseIcon_notAddedFromIneligibleSession() throws Throwable { + final String appLabel = "Test Promise App " + UUID.randomUUID().toString(); + final Workspace.ItemOperator findPromiseApp = (info, view) -> + info != null && TextUtils.equals(info.title, appLabel); + + // Create and add test session without icon or label + mSessionId = createSession(null, null); + + // Sleep for duration of animation if a view was to be added + some buffer time. + Thread.sleep(Launcher.NEW_APPS_PAGE_MOVE_DELAY + Launcher.NEW_APPS_ANIMATION_DELAY + 500); + + // Verify promise icon is not added + waitForLauncherCondition("Test Promise App not found on workspace", launcher -> + launcher.getWorkspace().getFirstMatch(findPromiseApp) == null); + } +} diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index fc19baace2..dc890bb005 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -24,7 +24,6 @@ import static org.junit.Assert.assertTrue; import static java.lang.System.exit; -import android.app.Instrumentation; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -112,6 +111,7 @@ public abstract class AbstractLauncherUiTest { launcher -> checkLauncherIntegrity(launcher, containerType))); } + mLauncher.enableDebugTracing(); } protected final LauncherActivityRule mActivityMonitor = new LauncherActivityRule(); @@ -187,14 +187,6 @@ public abstract class AbstractLauncherUiTest { } } - protected void lockRotation(boolean naturalOrientation) throws RemoteException { - if (naturalOrientation) { - mDevice.setOrientationNatural(); - } else { - mDevice.setOrientationRight(); - } - } - protected void clearLauncherData() throws IOException, InterruptedException { if (TestHelpers.isInLauncherProcess()) { LauncherSettings.Settings.call(mTargetContext.getContentResolver(), @@ -202,6 +194,7 @@ public abstract class AbstractLauncherUiTest { resetLoaderState(); } else { clearPackageData(mDevice.getLauncherPackageName()); + mLauncher.enableDebugTracing(); } } @@ -275,6 +268,12 @@ public abstract class AbstractLauncherUiTest { // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide // flakiness. + protected <T> T getOnceNotNull(String message, Function<Launcher, T> f) { + return getOnceNotNull(message, f, DEFAULT_ACTIVITY_TIMEOUT); + } + + // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide + // flakiness. protected void waitForLauncherCondition( String message, Function<Launcher, Boolean> condition, long timeout) { if (!TestHelpers.isInLauncherProcess()) return; @@ -283,6 +282,20 @@ public abstract class AbstractLauncherUiTest { // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide // flakiness. + protected <T> T getOnceNotNull(String message, Function<Launcher, T> f, long timeout) { + if (!TestHelpers.isInLauncherProcess()) return null; + + final Object[] output = new Object[1]; + Wait.atMost(message, () -> { + final Object fromLauncher = getFromLauncher(f); + output[0] = fromLauncher; + return fromLauncher != null; + }, timeout); + return (T) output[0]; + } + + // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide + // flakiness. protected void waitForLauncherCondition( String message, Runnable testThreadAction, Function<Launcher, Boolean> condition, diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java index ddcb4da883..80bb3edddd 100644 --- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java +++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java @@ -44,8 +44,11 @@ class PortraitLandscapeRunner implements TestRule { } finally { mTest.mDevice.setOrientationNatural(); mTest.executeOnLauncher(launcher -> - launcher.getRotationHelper().forceAllowRotationForTesting( - false)); + { + if (launcher != null) { + launcher.getRotationHelper().forceAllowRotationForTesting(false); + } + }); mTest.mLauncher.setExpectedRotation(Surface.ROTATION_0); } } diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index 0c87ab9089..c2a3c1c524 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -173,7 +173,6 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { @Test public void testWorkspace() throws Exception { - mLauncher.enableDebugTracing(); final Workspace workspace = mLauncher.getWorkspace(); // Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there. @@ -209,7 +208,6 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { // Test starting a workspace app. final AppIcon app = workspace.getWorkspaceAppIcon("Chrome"); assertNotNull("No Chrome app in workspace", app); - mLauncher.disableDebugTracing(); } public static void runIconLaunchFromAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) { @@ -300,7 +298,6 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { @Test @PortraitLandscape public void testDragAppIcon() throws Throwable { - mLauncher.enableDebugTracing(); // 1. Open all apps and wait for load complete. // 2. Drag icon to homescreen. // 3. Verify that the icon works on homescreen. @@ -317,13 +314,11 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { "Launcher activity is the top activity; expecting another activity to be the top " + "one", isInBackground(launcher))); - mLauncher.disableDebugTracing(); } @Test @PortraitLandscape public void testDragShortcut() throws Throwable { - mLauncher.enableDebugTracing(); // 1. Open all apps and wait for load complete. // 2. Find the app and long press it to show shortcuts. // 3. Press icon center until shortcuts appear @@ -343,7 +338,6 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { } finally { allApps.unfreeze(); } - mLauncher.disableDebugTracing(); } public static String getAppPackageName() { diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java index 3206a69bbf..7f427b303b 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java @@ -69,34 +69,22 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest { } @Test + @PortraitLandscape public void testWidgetConfig() throws Throwable { - runTest(false, true); - } - - @Test - @Ignore // b/121280703 - public void testWidgetConfig_rotate() throws Throwable { - runTest(true, true); + runTest(true); } @Test + @PortraitLandscape public void testConfigCancelled() throws Throwable { - runTest(false, false); + runTest(false); } - @Test - @Ignore // b/121280703 - public void testConfigCancelled_rotate() throws Throwable { - runTest(true, false); - } /** - * @param rotateConfig should the config screen be rotated * @param acceptConfig accept the config activity */ - private void runTest(boolean rotateConfig, boolean acceptConfig) throws Throwable { - lockRotation(true); - + private void runTest(boolean acceptConfig) throws Throwable { clearHomescreen(); mDevice.pressHome(); @@ -110,13 +98,6 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest { // Widget id for which the config activity was opened mWidgetId = monitor.getWidgetId(); - if (rotateConfig) { - // Rotate the screen and verify that the config activity is recreated - monitor = new WidgetConfigStartupMonitor(); - lockRotation(false); - assertEquals(mWidgetId, monitor.getWidgetId()); - } - // Verify that the widget id is valid and bound assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId)); diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java index 276c6144a2..cb586acebb 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java @@ -15,6 +15,9 @@ */ package com.android.launcher3.ui.widget; +import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; + +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import androidx.test.filters.LargeTest; @@ -22,6 +25,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.tapl.Widget; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.TestViewHelpers; import com.android.launcher3.util.rule.ShellCommandRule; @@ -41,19 +45,8 @@ public class AddWidgetTest extends AbstractLauncherUiTest { @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind(); @Test - public void testDragIcon_portrait() throws Throwable { - lockRotation(true); - performTest(); - } - - @Test - @Ignore // b/121280703 - public void testDragIcon_landscape() throws Throwable { - lockRotation(false); - performTest(); - } - - private void performTest() throws Throwable { + @PortraitLandscape + public void testDragIcon() throws Throwable { clearHomescreen(); mDevice.pressHome(); @@ -70,5 +63,10 @@ public class AddWidgetTest extends AbstractLauncherUiTest { (info, view) -> info instanceof LauncherAppWidgetInfo && ((LauncherAppWidgetInfo) info).providerName.getClassName().equals( widgetInfo.provider.getClassName())).call()); + + final Widget widget = mLauncher.getWorkspace().tryGetWidget(widgetInfo.label, + DEFAULT_UI_TIMEOUT); + assertNotNull("Widget not found on the workspace", widget); + widget.launch(getAppPackageName()); } } diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java index a9a5090988..07129ddd95 100644 --- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java +++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java @@ -128,8 +128,6 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { if (!Utilities.ATLEAST_OREO) { return; } - lockRotation(true); - clearHomescreen(); mDevice.pressHome(); diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 9d433527da..c6e7daceb3 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -368,7 +368,7 @@ public final class LauncherInstrumentation { } } - private void assertEquals(String message, String expected, String actual) { + void assertEquals(String message, String expected, String actual) { if (!TextUtils.equals(expected, actual)) { fail(message + " expected: '" + expected + "' but was: '" + actual + "'"); } @@ -763,8 +763,7 @@ public final class LauncherInstrumentation { final Bundle parcel = (Bundle) executeAndWaitForEvent( () -> linearGesture(startX, startY, endX, endY, steps), event -> TestProtocol.SWITCHED_TO_STATE_MESSAGE.equals(event.getClassName()), - "Swipe failed to receive an event for the swipe end: " + startX + ", " + startY - + ", " + endX + ", " + endY); + "Swipe failed to receive an event for the swipe end"); assertEquals("Swipe switched launcher to a wrong state;", TestProtocol.stateOrdinalToString(expectedState), TestProtocol.stateOrdinalToString(parcel.getInt(TestProtocol.STATE_FIELD))); @@ -958,8 +957,8 @@ public final class LauncherInstrumentation { getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING); } - public long getAllocatedMemory() { - return getTestInfo(TestProtocol.REQUEST_ALLOCATED_MEMORY). - getLong(TestProtocol.TEST_INFO_RESPONSE_FIELD); + public int getTotalPssKb() { + return getTestInfo(TestProtocol.REQUEST_TOTAL_PSS_KB). + getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD); } }
\ No newline at end of file diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java index 2495933ac6..7d308afb99 100644 --- a/tests/tapl/com/android/launcher3/tapl/Widgets.java +++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java @@ -19,6 +19,7 @@ package com.android.launcher3.tapl; import static org.junit.Assert.fail; import android.graphics.Point; +import android.graphics.Rect; import androidx.test.uiautomator.By; import androidx.test.uiautomator.BySelector; @@ -31,7 +32,8 @@ import com.android.launcher3.ResourceUtils; * All widgets container. */ public final class Widgets extends LauncherInstrumentation.VisibleContainer { - private static final int FLING_SPEED = 1500; + private static final Rect MARGINS = new Rect(100, 100, 100, 100); + private static final int FLING_STEPS = 10; Widgets(LauncherInstrumentation launcher) { super(launcher); @@ -46,11 +48,7 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer { "want to fling forward in widgets")) { LauncherInstrumentation.log("Widgets.flingForward enter"); final UiObject2 widgetsContainer = verifyActiveContainer(); - widgetsContainer.setGestureMargins(0, 0, 0, - ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, - mLauncher.getResources()) + 1); - widgetsContainer.fling(Direction.DOWN, - (int) (FLING_SPEED * mLauncher.getDisplayDensity())); + mLauncher.scroll(widgetsContainer, Direction.DOWN, 1f, MARGINS, FLING_STEPS); try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung forward")) { verifyActiveContainer(); } @@ -66,10 +64,7 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer { "want to fling backwards in widgets")) { LauncherInstrumentation.log("Widgets.flingBackward enter"); final UiObject2 widgetsContainer = verifyActiveContainer(); - widgetsContainer.setGestureMargin(100); - widgetsContainer.fling(Direction.UP, - (int) (FLING_SPEED * mLauncher.getDisplayDensity())); - mLauncher.waitForIdle(); + mLauncher.scroll(widgetsContainer, Direction.UP, 1f, MARGINS, FLING_STEPS); try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) { verifyActiveContainer(); } @@ -82,7 +77,7 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer { return LauncherInstrumentation.ContainerType.WIDGETS; } - public Widget getWidget(String label) { + public Widget getWidget(String labelText) { final int margin = ResourceUtils.getNavbarSize( ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1; final UiObject2 widgetsContainer = verifyActiveContainer(); @@ -91,17 +86,24 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer { final Point displaySize = mLauncher.getRealDisplaySize(); int i = 0; - final BySelector selector = By. - clazz("com.android.launcher3.widget.WidgetCell"). - hasDescendant(By.text(label)); + final BySelector selector = By.clazz("android.widget.TextView").text(labelText); for (; ; ) { - final UiObject2 widget = mLauncher.tryWaitForLauncherObject(selector, 300); - if (widget != null && widget.getVisibleBounds().bottom <= displaySize.y - margin) { - return new Widget(mLauncher, widget); + final UiObject2 label = mLauncher.tryWaitForLauncherObject(selector, 300); + if (label != null) { + final UiObject2 widget = label.getParent().getParent(); + mLauncher.assertEquals( + "View is not WidgetCell", + "com.android.launcher3.widget.WidgetCell", + widget.getClassName()); + + if (widget.getVisibleBounds().bottom <= displaySize.y - margin) { + return new Widget(mLauncher, widget); + } } + if (++i > 40) fail("Too many attempts"); - widgetsContainer.scroll(Direction.DOWN, 1f); + mLauncher.scroll(widgetsContainer, Direction.DOWN, 0.7f, MARGINS, 50); } } } diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index 639902fa0d..510ea14091 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -21,6 +21,7 @@ import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL; import static junit.framework.TestCase.assertTrue; import android.graphics.Point; +import android.graphics.Rect; import android.os.SystemClock; import android.view.KeyEvent; import android.view.MotionEvent; @@ -40,6 +41,7 @@ public final class Workspace extends Home { private static final float FLING_SPEED = LauncherInstrumentation.isAvd() ? 1500.0F : 3500.0F; private static final int DRAG_DURACTION = 2000; + private static final int FLING_STEPS = 10; private final UiObject2 mHotseat; Workspace(LauncherInstrumentation launcher) { @@ -180,9 +182,9 @@ public final class Workspace extends Home { */ public void flingForward() { final UiObject2 workspace = verifyActiveContainer(); - workspace.setGestureMargins(0, 0, mLauncher.getEdgeSensitivityWidth(), 0); - workspace.fling(Direction.RIGHT, (int) (FLING_SPEED * mLauncher.getDisplayDensity())); - mLauncher.waitForIdle(); + mLauncher.scroll(workspace, Direction.RIGHT, 1f, + new Rect(0, 0, mLauncher.getEdgeSensitivityWidth(), 0), + FLING_STEPS); verifyActiveContainer(); } @@ -192,9 +194,9 @@ public final class Workspace extends Home { */ public void flingBackward() { final UiObject2 workspace = verifyActiveContainer(); - workspace.setGestureMargins(mLauncher.getEdgeSensitivityWidth(), 0, 0, 0); - workspace.fling(Direction.LEFT, (int) (FLING_SPEED * mLauncher.getDisplayDensity())); - mLauncher.waitForIdle(); + mLauncher.scroll(workspace, Direction.LEFT, 1f, + new Rect(mLauncher.getEdgeSensitivityWidth(), 0, 0, 0), + FLING_STEPS); verifyActiveContainer(); } |