diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java index ffc1e291be..cb46c400e7 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java @@ -209,6 +209,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable WorkspaceItemInfo info = predictions.get(i); PredictedAppIcon icon = PredictedAppIcon.createIcon(mSampleHotseat, info); icon.setEnabled(false); + icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); icon.verifyHighRes(); CellLayout.LayoutParams lp = new CellLayout.LayoutParams(i, 0, 1, 1); mSampleHotseat.addViewToCellLayout(icon, i, info.getViewId(), lp, true); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java index d3bb4f9062..9bc097527c 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java @@ -349,7 +349,10 @@ public class HotseatPredictionController implements DragController.DragListener, mHotSeatItemsCount); } - private void pinPrediction(ItemInfo info) { + /** + * Pins a predicted app icon into place. + */ + public void pinPrediction(ItemInfo info) { PredictedAppIcon icon = (PredictedAppIcon) mHotseat.getChildAt( mHotseat.getCellXFromOrder(info.rank), mHotseat.getCellYFromOrder(info.rank)); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java index 4bbb48ce83..304c77f3da 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.uioverrides; +import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.PIN_PREDICTION; import static com.android.launcher3.graphics.IconShape.getShape; import android.content.Context; @@ -26,15 +27,19 @@ import android.graphics.Rect; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityNodeInfo; import androidx.core.graphics.ColorUtils; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.WorkspaceItemInfo; +import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.graphics.IconPalette; +import com.android.launcher3.hybridhotseat.HotseatPredictionController; import com.android.launcher3.icons.IconNormalizer; import com.android.launcher3.touch.ItemClickHandler; import com.android.launcher3.touch.ItemLongClickListener; @@ -43,7 +48,8 @@ import com.android.launcher3.views.DoubleShadowBubbleTextView; /** * A BubbleTextView with a ring around it's drawable */ -public class PredictedAppIcon extends DoubleShadowBubbleTextView { +public class PredictedAppIcon extends DoubleShadowBubbleTextView implements + LauncherAccessibilityDelegate.AccessibilityActionHandler { private static final float RING_EFFECT_RATIO = 0.11f; @@ -97,6 +103,13 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView { super.applyFromWorkspaceItem(info); int color = IconPalette.getMutedColor(info.bitmap.color, 0.54f); mIconRingPaint.setColor(ColorUtils.setAlphaComponent(color, 200)); + if (mIsPinned) { + setContentDescription(info.contentDescription); + } else { + setContentDescription( + getContext().getString(R.string.hotseat_prediction_content_description, + info.contentDescription)); + } } /** @@ -104,9 +117,9 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView { */ public void pin(WorkspaceItemInfo info) { if (mIsPinned) return; + mIsPinned = true; applyFromWorkspaceItem(info); setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE); - mIsPinned = true; ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true; invalidate(); } @@ -121,6 +134,27 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView { verifyHighRes(); } + @Override + public void addSupportedAccessibilityActions(AccessibilityNodeInfo accessibilityNodeInfo) { + accessibilityNodeInfo.addAction( + new AccessibilityNodeInfo.AccessibilityAction(PIN_PREDICTION, + getContext().getText(R.string.pin_prediction))); + } + + @Override + public boolean performAccessibilityAction(int action, ItemInfo info) { + QuickstepLauncher launcher = Launcher.cast(Launcher.getLauncher(getContext())); + if (action == PIN_PREDICTION) { + if (launcher == null || launcher.getHotseatPredictionController() == null) { + return false; + } + HotseatPredictionController controller = launcher.getHotseatPredictionController(); + controller.pinPrediction(info); + return true; + } + return false; + } + @Override public void getIconBounds(Rect outBounds) { super.getIconBounds(outBounds); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 9352acef12..a02d9c365c 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -19,12 +19,9 @@ import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; -import android.content.Context; import android.content.Intent; import android.content.res.Configuration; -import android.graphics.Rect; import android.os.Bundle; -import android.view.Gravity; import android.view.View; import androidx.annotation.Nullable; @@ -38,7 +35,6 @@ import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.Folder; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.hybridhotseat.HotseatPredictionController; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController; @@ -72,77 +68,6 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { */ public static final AsyncCommand SET_SHELF_HEIGHT = (context, arg1, arg2) -> SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2); - public static final RotationMode ROTATION_LANDSCAPE = new RotationMode(-90) { - @Override - public void mapRect(int left, int top, int right, int bottom, Rect out) { - out.left = top; - out.top = right; - out.right = bottom; - out.bottom = left; - } - - @Override - public void mapInsets(Context context, Rect insets, Rect out) { - // If there is a display cutout, the top insets in portrait would also include the - // cutout, which we will get as the left inset in landscape. Using the max of left and - // top allows us to cover both cases (with or without cutout). - if (SysUINavigationMode.getMode(context) == NO_BUTTON) { - out.top = Math.max(insets.top, insets.left); - out.bottom = Math.max(insets.right, insets.bottom); - out.left = out.right = 0; - } else { - out.top = Math.max(insets.top, insets.left); - out.bottom = insets.right; - out.left = insets.bottom; - out.right = 0; - } - } - }; - public static final RotationMode ROTATION_SEASCAPE = new RotationMode(90) { - @Override - public void mapRect(int left, int top, int right, int bottom, Rect out) { - out.left = bottom; - out.top = left; - out.right = top; - out.bottom = right; - } - - @Override - public void mapInsets(Context context, Rect insets, Rect out) { - if (SysUINavigationMode.getMode(context) == NO_BUTTON) { - out.top = Math.max(insets.top, insets.right); - out.bottom = Math.max(insets.left, insets.bottom); - out.left = out.right = 0; - } else { - out.top = Math.max(insets.top, insets.right); - out.bottom = insets.left; - out.right = insets.bottom; - out.left = 0; - } - } - - @Override - public int toNaturalGravity(int absoluteGravity) { - int horizontalGravity = absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK; - int verticalGravity = absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK; - - if (horizontalGravity == Gravity.RIGHT) { - horizontalGravity = Gravity.LEFT; - } else if (horizontalGravity == Gravity.LEFT) { - horizontalGravity = Gravity.RIGHT; - } - - if (verticalGravity == Gravity.TOP) { - verticalGravity = Gravity.BOTTOM; - } else if (verticalGravity == Gravity.BOTTOM) { - verticalGravity = Gravity.TOP; - } - - return ((absoluteGravity & ~Gravity.HORIZONTAL_GRAVITY_MASK) - & ~Gravity.VERTICAL_GRAVITY_MASK) - | horizontalGravity | verticalGravity; - } - }; private HotseatPredictionController mHotseatPredictionController; @Override @@ -153,12 +78,6 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { } } - @Override - protected RotationMode getFakeRotationMode(DeviceProfile dp) { - return !dp.isVerticalBarLayout() ? RotationMode.NORMAL - : (dp.isSeascape() ? ROTATION_SEASCAPE : ROTATION_LANDSCAPE); - } - @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java index 131b71fc53..3d6e519d3f 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.uioverrides; +import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA; import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT; import static com.android.launcher3.anim.Interpolators.LINEAR; @@ -91,7 +92,7 @@ public final class RecentsViewStateController extends View actionsView = mLauncher.getActionsView(); if (actionsView != null) { - propertySetter.setViewAlpha(actionsView, buttonAlpha, actionInterpolator); + propertySetter.setFloat(actionsView, VIEW_ALPHA, buttonAlpha, actionInterpolator); } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java index 113cdec9d0..5abeae4653 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java @@ -46,7 +46,6 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.model.PagedViewOrientedState; import com.android.launcher3.states.RotationHelper; import com.android.launcher3.testing.TestProtocol; @@ -149,8 +148,8 @@ public abstract class BaseSwipeUpHandler getRecentsViewDispatcher(RotationMode navBarRotationMode) { - return mRecentsView != null ? mRecentsView.getEventDispatcher(navBarRotationMode) : null; + public Consumer getRecentsViewDispatcher(float navbarRotation) { + return mRecentsView != null ? mRecentsView.getEventDispatcher(navbarRotation) : null; } @UiThread diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index 2d0f978a0d..c889632a48 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -444,9 +444,12 @@ public class TouchInteractionService extends Service implements PluginListener mActivity.getAllAppsController(), ALL_APPS_PROGRESS, allAppsProgressOffscreen)); ObjectAnimator dragHandleAnim = ObjectAnimator.ofInt( - mActivity.findViewById(R.id.scrim_view), ScrimView.DRAG_HANDLE_ALPHA, 0); + mActivity.getScrimView(), ScrimView.DRAG_HANDLE_ALPHA, 0); dragHandleAnim.setInterpolator(Interpolators.ACCEL_2); anim.play(dragHandleAnim); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 1c95a9ef77..e3b3102562 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -71,6 +71,7 @@ import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; +import android.view.OrientationEventListener; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; @@ -90,6 +91,7 @@ import com.android.launcher3.LauncherState; import com.android.launcher3.PagedView; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.PendingAnimation.EndState; @@ -97,9 +99,9 @@ import com.android.launcher3.anim.PropertyListBuilder; import com.android.launcher3.anim.SpringProperty; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.states.RotationHelper; +import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; @@ -169,6 +171,7 @@ public abstract class RecentsView extends PagedView impl } }; + private OrientationEventListener mOrientationListener; private int mPreviousRotation; protected RecentsAnimationController mRecentsAnimationController; protected RecentsAnimationTargets mRecentsAnimationTargets; @@ -376,6 +379,22 @@ public abstract class RecentsView extends PagedView impl // Initialize quickstep specific cache params here, as this is constructed only once mActivity.getViewCache().setCacheSize(R.layout.digital_wellbeing_toast, 5); + + mOrientationListener = new OrientationEventListener(getContext()) { + @Override + public void onOrientationChanged(int i) { + int rotation = RotationHelper.getRotationFromDegrees(i); + if (mPreviousRotation != rotation) { + animateRecentsRotationInPlace(rotation); + if (rotation == 0) { + showActionsView(); + } else { + hideActionsView(); + } + mPreviousRotation = rotation; + } + } + }; } public OverScroller getScroller() { @@ -504,6 +523,15 @@ public abstract class RecentsView extends PagedView impl } public void setOverviewStateEnabled(boolean enabled) { + if (supportsVerticalLandscape() + && !TestProtocol.sDisableSensorRotation // Ignore hardware dependency for tests + && mOrientationListener.canDetectOrientation()) { + if (enabled) { + mOrientationListener.enable(); + } else { + mOrientationListener.disable(); + } + } mOverviewStateEnabled = enabled; updateTaskStackListenerState(); if (!enabled) { @@ -784,23 +812,14 @@ public abstract class RecentsView extends PagedView impl if (getPageCount() == 0 || getPageAt(0).getMeasuredWidth() == 0) { return; } - CurveProperties curveProperties = mOrientationHandler - .getCurveProperties(this, mInsets); - int scroll = curveProperties.scroll; - final int halfPageSize = curveProperties.halfPageSize; - final int screenCenter = curveProperties.screenCenter; - final int halfScreenSize = curveProperties.halfScreenSize; - final int pageSpacing = mPageSpacing; - mScrollState.scrollFromEdge = mIsRtl ? scroll : (mMaxScroll - scroll); + mOrientationHandler.getCurveProperties(this, mInsets, mScrollState); + mScrollState.scrollFromEdge = + mIsRtl ? mScrollState.scroll : (mMaxScroll - mScrollState.scroll); final int pageCount = getPageCount(); for (int i = 0; i < pageCount; i++) { View page = getPageAt(i); - float pageCenter = mOrientationHandler.getViewCenterPosition(page) + halfPageSize; - float distanceFromScreenCenter = screenCenter - pageCenter; - float distanceToReachEdge = halfScreenSize + halfPageSize + pageSpacing; - mScrollState.linearInterpolation = Math.min(1, - Math.abs(distanceFromScreenCenter) / distanceToReachEdge); + mScrollState.updateInterpolation(mOrientationHandler.getChildStart(page), mPageSpacing); ((PageCallbacks) page).onPageScroll(mScrollState); } } @@ -947,6 +966,35 @@ public abstract class RecentsView extends PagedView impl setSwipeDownShouldLaunchApp(true); } + private void animateRecentsRotationInPlace(int newRotation) { + if (!supportsVerticalLandscape()) { + return; + } + + AnimatorSet pa = setRecentsChangedOrientation(true); + pa.addListener(AnimationSuccessListener.forRunnable(() -> { + updateLayoutRotation(newRotation); + mActivity.getDragLayer().recreateControllers(); + rotateAllChildTasks(); + setRecentsChangedOrientation(false).start(); + })); + pa.start(); + } + + public AnimatorSet setRecentsChangedOrientation(boolean fadeInChildren) { + getRunningTaskIndex(); + int runningIndex = getCurrentPage(); + AnimatorSet as = new AnimatorSet(); + for (int i = 0; i < getTaskViewCount(); i++) { + if (runningIndex == i) { + continue; + } + View taskView = getTaskViewAt(i); + as.play(ObjectAnimator.ofFloat(taskView, View.ALPHA, fadeInChildren ? 0 : 1)); + } + return as; + } + abstract protected boolean supportsVerticalLandscape(); private void rotateAllChildTasks() { @@ -1143,7 +1191,7 @@ public abstract class RecentsView extends PagedView impl default void onPageScroll(ScrollState scrollState) {} } - public static class ScrollState { + public static class ScrollState extends CurveProperties { /** * The progress from 0 to 1, where 0 is the center @@ -1155,6 +1203,17 @@ public abstract class RecentsView extends PagedView impl * The amount by which all the content is scrolled relative to the end of the list. */ public float scrollFromEdge; + + /** + * Updates linearInterpolation for the provided child position + */ + public void updateInterpolation(int childStart, int pageSpacing) { + float pageCenter = childStart + halfPageSize; + float distanceFromScreenCenter = screenCenter - pageCenter; + float distanceToReachEdge = halfScreenSize + halfPageSize + pageSpacing; + linearInterpolation = Math.min(1, + Math.abs(distanceFromScreenCenter) / distanceToReachEdge); + } } public void setIgnoreResetTask(int taskId) { @@ -1924,13 +1983,13 @@ public abstract class RecentsView extends PagedView impl return offsetX; } - public Consumer getEventDispatcher(RotationMode navBarRotationMode) { + public Consumer getEventDispatcher(float navbarRotation) { float degreesRotated; - if (navBarRotationMode == RotationMode.NORMAL) { + if (navbarRotation == 0) { degreesRotated = mOrientationState.areMultipleLayoutOrientationsDisabled() ? 0 : RotationHelper.getDegreesFromRotation(mLayoutRotation); } else { - degreesRotated = -navBarRotationMode.surfaceRotation; + degreesRotated = -navbarRotation; } if (degreesRotated == 0) { return super::onTouchEvent; @@ -1940,7 +1999,7 @@ public abstract class RecentsView extends PagedView impl // undo that transformation since PagedView also accommodates for the transformation via // PagedOrientationHandler return e -> { - if (navBarRotationMode != RotationMode.NORMAL + if (navbarRotation != 0 && !mOrientationState.areMultipleLayoutOrientationsDisabled()) { RotationHelper.transformEventForNavBar(e, true); super.onTouchEvent(e); diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 8d42c4a6c4..dcc85d52cc 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -66,9 +66,6 @@ docked_stack_divider_thickness - 2 * docked_stack_divider_insets --> 10dp - - 24dp - 48dp diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml index 548a51f849..f5552f0cb2 100644 --- a/quickstep/res/values/strings.xml +++ b/quickstep/res/values/strings.xml @@ -90,6 +90,9 @@ App suggestions added to empty space + + Predicted app: %1$s + Try the back gesture diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index 135daef6bd..abdff0d7bb 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -88,9 +88,7 @@ public abstract class BaseQuickstepLauncher extends Launcher super.onCreate(savedInstanceState); mSystemActions = new SystemActions(this); - SysUINavigationMode.Mode mode = SysUINavigationMode.INSTANCE.get(this) - .addModeChangeListener(this); - getRotationHelper().setRotationHadDifferentUI(mode != Mode.NO_BUTTON); + SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this); if (!getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) { getStateManager().addStateListener(new LauncherStateManager.StateListener() { @@ -141,7 +139,6 @@ public abstract class BaseQuickstepLauncher extends Launcher @Override public void onNavigationModeChanged(Mode newMode) { getDragLayer().recreateControllers(); - getRotationHelper().setRotationHadDifferentUI(newMode != Mode.NO_BUTTON); } @Override diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java index 631df4cd06..5118906b28 100644 --- a/quickstep/src/com/android/quickstep/GestureState.java +++ b/quickstep/src/com/android/quickstep/GestureState.java @@ -130,6 +130,17 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL mGestureId = gestureId; } + public GestureState(GestureState other) { + mHomeIntent = other.mHomeIntent; + mOverviewIntent = other.mOverviewIntent; + mActivityInterface = other.mActivityInterface; + mStateCallback = other.mStateCallback; + mGestureId = other.mGestureId; + mRunningTask = other.mRunningTask; + mEndTarget = other.mEndTarget; + mFinishingRecentsAnimationTaskId = other.mFinishingRecentsAnimationTaskId; + } + public GestureState() { // Do nothing, only used for initializing the gesture state prior to user unlock mHomeIntent = new Intent(); diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java index 8efaeb98d9..818d836c05 100644 --- a/quickstep/src/com/android/quickstep/InputConsumer.java +++ b/quickstep/src/com/android/quickstep/InputConsumer.java @@ -69,6 +69,13 @@ public interface InputConsumer { return false; } + /** + * Returns true if the given input consumer is in the hierarchy of this input consumer. + */ + default boolean isInConsumerHierarchy(InputConsumer candidate) { + return this == candidate; + } + /** * Called by the event queue when the consumer is about to be switched to a new consumer. * Consumers should update the state accordingly here before the state is passed to the new diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java index 593b6952dd..aeb718dceb 100644 --- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java +++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java @@ -107,11 +107,11 @@ public class BackGestureTutorialFragment extends Fragment implements BackGesture } void onAttachedToWindow() { - mEdgeBackGestureHandler.setIsEnabled(true); + mEdgeBackGestureHandler.setViewGroupParent((ViewGroup) getRootView()); } void onDetachedFromWindow() { - mEdgeBackGestureHandler.setIsEnabled(false); + mEdgeBackGestureHandler.setViewGroupParent(null); } @Override diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java index 04cd2f49d6..f34530ec8e 100644 --- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java +++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java @@ -17,21 +17,18 @@ package com.android.quickstep.interaction; import android.content.Context; import android.content.res.Resources; -import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PointF; -import android.hardware.display.DisplayManager; -import android.hardware.display.DisplayManager.DisplayListener; -import android.os.Handler; -import android.os.Looper; import android.os.SystemProperties; import android.view.Display; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewConfiguration; -import android.view.WindowManager; -import android.view.WindowManager.LayoutParams; +import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; + +import androidx.annotation.Nullable; import com.android.launcher3.ResourceUtils; @@ -40,7 +37,7 @@ import com.android.launcher3.ResourceUtils; * * Forked from platform/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java. */ -public class EdgeBackGestureHandler implements DisplayListener, OnTouchListener { +public class EdgeBackGestureHandler implements OnTouchListener { private static final String TAG = "EdgeBackGestureHandler"; private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt( @@ -106,29 +103,22 @@ public class EdgeBackGestureHandler implements DisplayListener, OnTouchListener mEdgeWidth = ResourceUtils.getNavbarSize("config_backGestureInset", res); } - void setIsEnabled(boolean isEnabled) { - if (isEnabled == mIsEnabled) { - return; - } - mIsEnabled = isEnabled; + void setViewGroupParent(@Nullable ViewGroup parent) { + mIsEnabled = parent != null; if (mEdgeBackPanel != null) { mEdgeBackPanel.onDestroy(); mEdgeBackPanel = null; } - if (!mIsEnabled) { - mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this); - } else { - updateDisplaySize(); - mContext.getSystemService(DisplayManager.class).registerDisplayListener(this, - new Handler(Looper.getMainLooper())); - + if (mIsEnabled) { // Add a nav bar panel window. - mEdgeBackPanel = new EdgeBackGesturePanel(mContext); + mEdgeBackPanel = new EdgeBackGesturePanel(mContext, parent, createLayoutParams()); mEdgeBackPanel.setBackCallback(mBackCallback); - mEdgeBackPanel.setLayoutParams(createLayoutParams()); - updateDisplaySize(); + if (mContext.getDisplay() != null) { + mContext.getDisplay().getRealSize(mDisplaySize); + mEdgeBackPanel.setDisplaySize(mDisplaySize); + } } } @@ -136,21 +126,11 @@ public class EdgeBackGestureHandler implements DisplayListener, OnTouchListener mGestureCallback = callback; } - private WindowManager.LayoutParams createLayoutParams() { + private LayoutParams createLayoutParams() { Resources resources = mContext.getResources(); - WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams( + return new LayoutParams( ResourceUtils.getNavbarSize("navigation_edge_panel_width", resources), - ResourceUtils.getNavbarSize("navigation_edge_panel_height", resources), - LayoutParams.TYPE_APPLICATION_PANEL, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH - | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, - PixelFormat.TRANSLUCENT); - layoutParams.setTitle(TAG + mDisplayId); - layoutParams.windowAnimations = 0; - layoutParams.setFitInsetsTypes(0 /* types */); - return layoutParams; + ResourceUtils.getNavbarSize("navigation_edge_panel_height", resources)); } @Override @@ -232,26 +212,6 @@ public class EdgeBackGestureHandler implements DisplayListener, OnTouchListener } } - @Override - public void onDisplayAdded(int displayId) { } - - @Override - public void onDisplayRemoved(int displayId) { } - - @Override - public void onDisplayChanged(int displayId) { - if (displayId == mDisplayId) { - updateDisplaySize(); - } - } - - private void updateDisplaySize() { - mContext.getDisplay().getRealSize(mDisplaySize); - if (mEdgeBackPanel != null) { - mEdgeBackPanel.setDisplaySize(mDisplaySize); - } - } - void setInsets(int leftInset, int rightInset) { mLeftInset = leftInset; mRightInset = rightInset; diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java index 34eeafc676..5bf50260aa 100644 --- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java +++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java @@ -26,11 +26,11 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.Point; import android.os.SystemClock; -import android.view.Gravity; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; -import android.view.WindowManager; +import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; @@ -110,7 +110,6 @@ public class EdgeBackGesturePanel extends View { private static final Interpolator RUBBER_BAND_INTERPOLATOR_APPEAR = new PathInterpolator(1.0f / RUBBER_BAND_AMOUNT_APPEAR, 1.0f, 1.0f, 1.0f); - private final WindowManager mWindowManager; private BackCallback mBackCallback; /** @@ -147,7 +146,6 @@ public class EdgeBackGesturePanel extends View { private VelocityTracker mVelocityTracker; private int mArrowPaddingEnd; - private WindowManager.LayoutParams mLayoutParams; /** * True if the panel is currently on the left of the screen @@ -232,11 +230,9 @@ public class EdgeBackGesturePanel extends View { } }; - public EdgeBackGesturePanel(Context context) { + public EdgeBackGesturePanel(Context context, ViewGroup parent, LayoutParams layoutParams) { super(context); - mWindowManager = context.getSystemService(WindowManager.class); - mDensity = context.getResources().getDisplayMetrics().density; mBaseTranslation = dp(BASE_TRANSLATION_DP); @@ -290,11 +286,15 @@ public class EdgeBackGesturePanel extends View { mSwipeThreshold = ResourceUtils.getDimenByName( "navigation_edge_action_drag_threshold", context.getResources(), 16 /* defaultValue */); + parent.addView(this, layoutParams); setVisibility(GONE); } void onDestroy() { - mWindowManager.removeView(this); + ViewGroup parent = (ViewGroup) getParent(); + if (parent != null) { + parent.removeView(this); + } } @Override @@ -305,9 +305,6 @@ public class EdgeBackGesturePanel extends View { @SuppressLint("RtlHardcoded") void setIsLeftPanel(boolean isLeftPanel) { mIsLeftPanel = isLeftPanel; - mLayoutParams.gravity = mIsLeftPanel - ? (Gravity.LEFT | Gravity.TOP) - : (Gravity.RIGHT | Gravity.TOP); } boolean getIsLeftPanel() { @@ -323,11 +320,6 @@ public class EdgeBackGesturePanel extends View { mBackCallback = callback; } - void setLayoutParams(WindowManager.LayoutParams layoutParams) { - mLayoutParams = layoutParams; - mWindowManager.addView(this, mLayoutParams); - } - private float getCurrentAngle() { return mCurrentAngle; } @@ -349,7 +341,6 @@ public class EdgeBackGesturePanel extends View { mStartY = event.getY(); setVisibility(VISIBLE); updatePosition(event.getY()); - mWindowManager.updateViewLayout(this, mLayoutParams); break; case MotionEvent.ACTION_MOVE: handleMoveEvent(event); @@ -614,10 +605,11 @@ public class EdgeBackGesturePanel extends View { } private void updatePosition(float touchY) { - float position = touchY - mFingerOffset; - position = Math.max(position, mMinArrowPosition); - position -= mLayoutParams.height / 2.0f; - mLayoutParams.y = MathUtils.clamp((int) position, 0, mDisplaySize.y); + float positionY = touchY - mFingerOffset; + positionY = Math.max(positionY, mMinArrowPosition); + positionY -= getLayoutParams().height / 2.0f; + setX(mIsLeftPanel ? 0 : mDisplaySize.x - getLayoutParams().width); + setY(MathUtils.clamp((int) positionY, 0, mDisplaySize.y)); } private void setDesiredVerticalTransition(float verticalTranslation, boolean animated) { diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java index ba99016561..1f1a99937e 100644 --- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java +++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java @@ -33,9 +33,6 @@ import com.android.quickstep.SysUINavigationMode; import java.lang.annotation.Retention; -import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS; -import static java.lang.annotation.RetentionPolicy.SOURCE; - public class LayoutUtils { private static final int MULTI_WINDOW_STRATEGY_HALF_SCREEN = 1; @@ -68,7 +65,7 @@ public class LayoutUtils { // UI when shown. extraSpace = 0; } else { - extraSpace = getDefaultSwipeHeight(context, dp) + dp.verticalDragHandleSizePx + extraSpace = getDefaultSwipeHeight(context, dp) + dp.workspacePageIndicatorHeight + res.getDimensionPixelSize( R.dimen.dynamic_grid_hotseat_extra_vertical_size) + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding); diff --git a/quickstep/src/com/android/quickstep/util/NavBarPosition.java b/quickstep/src/com/android/quickstep/util/NavBarPosition.java index 8dc19dc71e..74e6b29295 100644 --- a/quickstep/src/com/android/quickstep/util/NavBarPosition.java +++ b/quickstep/src/com/android/quickstep/util/NavBarPosition.java @@ -17,12 +17,8 @@ package com.android.quickstep.util; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; -import android.content.Context; -import android.graphics.Rect; -import android.view.Gravity; import android.view.Surface; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.util.DefaultDisplay; import com.android.quickstep.SysUINavigationMode; @@ -31,79 +27,6 @@ import com.android.quickstep.SysUINavigationMode; */ public class NavBarPosition { - public static final RotationMode ROTATION_LANDSCAPE = new RotationMode(-90) { - @Override - public void mapRect(int left, int top, int right, int bottom, Rect out) { - out.left = top; - out.top = right; - out.right = bottom; - out.bottom = left; - } - - @Override - public void mapInsets(Context context, Rect insets, Rect out) { - // If there is a display cutout, the top insets in portrait would also include the - // cutout, which we will get as the left inset in landscape. Using the max of left and - // top allows us to cover both cases (with or without cutout). - if (SysUINavigationMode.getMode(context) == NO_BUTTON) { - out.top = Math.max(insets.top, insets.left); - out.bottom = Math.max(insets.right, insets.bottom); - out.left = out.right = 0; - } else { - out.top = Math.max(insets.top, insets.left); - out.bottom = insets.right; - out.left = insets.bottom; - out.right = 0; - } - } - }; - - public static final RotationMode ROTATION_SEASCAPE = new RotationMode(90) { - @Override - public void mapRect(int left, int top, int right, int bottom, Rect out) { - out.left = bottom; - out.top = left; - out.right = top; - out.bottom = right; - } - - @Override - public void mapInsets(Context context, Rect insets, Rect out) { - if (SysUINavigationMode.getMode(context) == NO_BUTTON) { - out.top = Math.max(insets.top, insets.right); - out.bottom = Math.max(insets.left, insets.bottom); - out.left = out.right = 0; - } else { - out.top = Math.max(insets.top, insets.right); - out.bottom = insets.left; - out.right = insets.bottom; - out.left = 0; - } - } - - @Override - public int toNaturalGravity(int absoluteGravity) { - int horizontalGravity = absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK; - int verticalGravity = absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK; - - if (horizontalGravity == Gravity.RIGHT) { - horizontalGravity = Gravity.LEFT; - } else if (horizontalGravity == Gravity.LEFT) { - horizontalGravity = Gravity.RIGHT; - } - - if (verticalGravity == Gravity.TOP) { - verticalGravity = Gravity.BOTTOM; - } else if (verticalGravity == Gravity.BOTTOM) { - verticalGravity = Gravity.TOP; - } - - return ((absoluteGravity & ~Gravity.HORIZONTAL_GRAVITY_MASK) - & ~Gravity.VERTICAL_GRAVITY_MASK) - | horizontalGravity | verticalGravity; - } - }; - private final SysUINavigationMode.Mode mMode; private final int mDisplayRotation; @@ -120,8 +43,7 @@ public class NavBarPosition { return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_270; } - public RotationMode getRotationMode() { - return isLeftEdge() ? ROTATION_SEASCAPE - : (isRightEdge() ? ROTATION_LANDSCAPE : RotationMode.NORMAL); + public float getRotation() { + return isLeftEdge() ? 90 : (isRightEdge() ? -90 : 0); } } diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java index 14c458ed53..c2ccd90d62 100644 --- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java +++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java @@ -88,7 +88,6 @@ public class ShelfScrimView extends ScrimView private float mShiftRange; - private final float mShelfOffset; private float mTopOffset; private float mShelfTop; private float mShelfTopAtThreshold; @@ -110,7 +109,6 @@ public class ShelfScrimView extends ScrimView mRadius = BOTTOM_CORNER_RADIUS_RATIO * Themes.getDialogCornerRadius(context); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mShelfOffset = context.getResources().getDimension(R.dimen.shelf_surface_offset); // Just assume the easiest UI for now, until we have the proper layout information. mDrawingFlatColor = true; } @@ -179,7 +177,7 @@ public class ShelfScrimView extends ScrimView Math.min(hotseatSize, LayoutUtils.getDefaultSwipeHeight(context, dp)); mDragHandleProgress = 1 - (dragHandleTop / mShiftRange); } - mTopOffset = dp.getInsets().top - mShelfOffset; + mTopOffset = dp.getInsets().top - mDragHandleSize.y; mShelfTopAtThreshold = mShiftRange * SCRIM_CATCHUP_THRESHOLD + mTopOffset; } updateColors(); @@ -190,12 +188,15 @@ public class ShelfScrimView extends ScrimView @Override public void updateColors() { super.updateColors(); + mDragHandleOffset = 0; if (mDrawingFlatColor) { - mDragHandleOffset = 0; return; } - mDragHandleOffset = mShelfOffset - mDragHandleSize; + if (mProgress < mDragHandleProgress) { + mDragHandleOffset = mShiftRange * (mDragHandleProgress - mProgress); + } + if (mProgress >= SCRIM_CATCHUP_THRESHOLD) { mShelfTop = mShiftRange * mProgress + mTopOffset; } else { @@ -231,10 +232,6 @@ public class ShelfScrimView extends ScrimView (float) 0, LINEAR)); mRemainingScreenColor = setColorAlphaBound(mScrimColor, remainingScrimAlpha); } - - if (mProgress < mDragHandleProgress) { - mDragHandleOffset += mShiftRange * (mDragHandleProgress - mProgress); - } } @Override @@ -290,4 +287,9 @@ public class ShelfScrimView extends ScrimView mPaint.setColor(mShelfColor); canvas.drawRoundRect(0, mShelfTop, width, height + mRadius, mRadius, mRadius, mPaint); } + + @Override + public float getVisualTop() { + return mShelfTop; + } } diff --git a/res/drawable-v24/drag_handle_indicator_shadow.xml b/res/drawable-v24/drag_handle_indicator_shadow.xml new file mode 100644 index 0000000000..774bc38ceb --- /dev/null +++ b/res/drawable-v24/drag_handle_indicator_shadow.xml @@ -0,0 +1,19 @@ + + + diff --git a/res/drawable/drag_handle_indicator.xml b/res/drawable/drag_handle_indicator.xml deleted file mode 100644 index b01b84ab81..0000000000 --- a/res/drawable/drag_handle_indicator.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - diff --git a/res/drawable/drag_handle_indicator_no_shadow.xml b/res/drawable/drag_handle_indicator_no_shadow.xml new file mode 100644 index 0000000000..341e60cda9 --- /dev/null +++ b/res/drawable/drag_handle_indicator_no_shadow.xml @@ -0,0 +1,29 @@ + + + + + + + diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml index ab6c960be6..de13277b7f 100644 --- a/res/layout/launcher.xml +++ b/res/layout/launcher.xml @@ -57,7 +57,7 @@ diff --git a/res/values/config.xml b/res/values/config.xml index 1675a986d3..ef6761387c 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -109,6 +109,7 @@ + diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 271511e6b2..0b589a2ed8 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -20,7 +20,6 @@ 8dp - 1dp 8dp 8dp @@ -36,10 +35,18 @@ 34dp 0dp + + 24dp + 1dp + 0dp + 4dp - 24dp - 0dp + 18dp + 6dp + 1dp + 48dp + 16dp 48dp diff --git a/res/values/drawables.xml b/res/values/drawables.xml index 9c57ec1214..7d631426cb 100644 --- a/res/values/drawables.xml +++ b/res/values/drawables.xml @@ -18,4 +18,5 @@ @drawable/ic_remove_no_shadow @drawable/ic_uninstall_no_shadow @drawable/ic_block_no_shadow + @drawable/drag_handle_indicator_no_shadow \ No newline at end of file diff --git a/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java b/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java index f8ac010ee1..7bc34cf91f 100644 --- a/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java +++ b/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java @@ -30,11 +30,8 @@ import android.content.pm.PackageInstaller.SessionParams; import com.android.launcher3.FolderInfo; import com.android.launcher3.ItemInfo; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherSettings; import com.android.launcher3.icons.BitmapInfo; -import com.android.launcher3.model.BgDataModel.Callbacks; -import com.android.launcher3.util.Executors; import com.android.launcher3.util.LauncherLayoutBuilder; import com.android.launcher3.util.LauncherModelHelper; import com.android.launcher3.util.LauncherRoboTestRunner; @@ -46,8 +43,6 @@ import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.LooperMode; import org.robolectric.annotation.LooperMode.Mode; -import java.util.ArrayList; - /** * Tests for layout parser for remote layout */ @@ -120,18 +115,6 @@ public class DefaultLayoutProviderTest { } private void writeLayoutAndLoad(LauncherLayoutBuilder builder) throws Exception { - mModelHelper.setupDefaultLayoutProvider(builder); - - LoaderResults results = new LoaderResults( - LauncherAppState.getInstance(mTargetContext), - mModelHelper.getBgDataModel(), - mModelHelper.getAllAppsList(), - new Callbacks[0]); - LoaderTask task = new LoaderTask( - LauncherAppState.getInstance(mTargetContext), - mModelHelper.getAllAppsList(), - mModelHelper.getBgDataModel(), - results); - Executors.MODEL_EXECUTOR.submit(() -> task.loadWorkspace(new ArrayList<>())).get(); + mModelHelper.setupDefaultLayoutProvider(builder).loadModelSync(); } } diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java index f16ed33cfe..76cb74707f 100644 --- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java +++ b/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java @@ -127,6 +127,10 @@ public class LShadowLauncherApps extends ShadowLauncherApps { @Override protected List getShortcutConfigActivityList(String packageName, UserHandle user) { - return Collections.emptyList(); + Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setPackage(packageName); + return RuntimeEnvironment.application.getPackageManager().queryIntentActivities(intent, 0) + .stream() + .map(ri -> getLauncherActivityInfo(ri.activityInfo)) + .collect(Collectors.toList()); } } diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowTypeface.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowTypeface.java new file mode 100644 index 0000000000..0e7c1deba0 --- /dev/null +++ b/robolectric_tests/src/com/android/launcher3/shadows/LShadowTypeface.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.shadows; + +import android.graphics.Typeface; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.shadows.ShadowTypeface; + +/** + * Extension of {@link ShadowTypeface} with missing shadow methods + */ +@Implements(Typeface.class) +public class LShadowTypeface extends ShadowTypeface { + + @Implementation + public static Typeface create(Typeface family, int weight, boolean italic) { + int style = italic ? Typeface.ITALIC : Typeface.NORMAL; + if (weight >= 400) { + style |= Typeface.BOLD; + } + return create(family, style); + } +} diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowWallpaperManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowWallpaperManager.java new file mode 100644 index 0000000000..d60251c901 --- /dev/null +++ b/robolectric_tests/src/com/android/launcher3/shadows/LShadowWallpaperManager.java @@ -0,0 +1,56 @@ +/* + * 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.shadows; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +import android.app.WallpaperManager; +import android.content.Context; + +import org.robolectric.RuntimeEnvironment; +import org.robolectric.Shadows; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.shadows.ShadowApplication; +import org.robolectric.shadows.ShadowUserManager; +import org.robolectric.shadows.ShadowWallpaperManager; + +/** + * Extension of {@link ShadowUserManager} with missing shadow methods + */ +@Implements(WallpaperManager.class) +public class LShadowWallpaperManager extends ShadowWallpaperManager { + + @Implementation + protected static WallpaperManager getInstance(Context context) { + return context.getSystemService(WallpaperManager.class); + } + + /** + * Remove this once the fix for + * https://github.com/robolectric/robolectric/issues/5285 + * is available + */ + public static void initializeMock() { + WallpaperManager wm = mock(WallpaperManager.class); + ShadowApplication shadowApplication = Shadows.shadowOf(RuntimeEnvironment.application); + shadowApplication.setSystemService(Context.WALLPAPER_SERVICE, wm); + doReturn(0).when(wm).getDesiredMinimumWidth(); + doReturn(0).when(wm).getDesiredMinimumHeight(); + } +} diff --git a/robolectric_tests/src/com/android/launcher3/shadows/ShadowOverrides.java b/robolectric_tests/src/com/android/launcher3/shadows/ShadowOverrides.java new file mode 100644 index 0000000000..131f69136c --- /dev/null +++ b/robolectric_tests/src/com/android/launcher3/shadows/ShadowOverrides.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.shadows; + +import android.content.Context; + +import com.android.launcher3.util.MainThreadInitializedObject.ObjectProvider; +import com.android.launcher3.util.ResourceBasedOverride; +import com.android.launcher3.util.ResourceBasedOverride.Overrides; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.shadow.api.Shadow; +import org.robolectric.util.ReflectionHelpers.ClassParameter; + +import java.util.HashMap; +import java.util.Map; + +/** + * Shadow for {@link Overrides} to provide custom overrides for test + */ +@Implements(value = Overrides.class, isInAndroidSdk = false) +public class ShadowOverrides { + + private static Map sProviderMap = new HashMap<>(); + + @Implementation + public static T getObject( + Class clazz, Context context, int resId) { + ObjectProvider provider = sProviderMap.get(clazz); + if (provider != null) { + return provider.get(context); + } + return Shadow.directlyOn(Overrides.class, "getObject", + ClassParameter.from(Class.class, clazz), + ClassParameter.from(Context.class, context), + ClassParameter.from(int.class, resId)); + } + + public static void setProvider(Class clazz, ObjectProvider provider) { + sProviderMap.put(clazz, provider); + } + + public static void clearProvider() { + sProviderMap.clear(); + } +} diff --git a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java new file mode 100644 index 0000000000..209bae06a8 --- /dev/null +++ b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */package com.android.launcher3.ui; + +import static android.view.View.MeasureSpec.EXACTLY; +import static android.view.View.MeasureSpec.makeMeasureSpec; + +import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.mockito.Mockito.mock; + +import android.app.Activity; +import android.content.Context; +import android.os.SystemClock; +import android.provider.Settings; +import android.view.InputDevice; +import android.view.MotionEvent; +import android.view.MotionEvent.PointerProperties; +import android.view.View; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.folder.Folder; +import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.folder.FolderPagedView; +import com.android.launcher3.logging.UserEventDispatcher; +import com.android.launcher3.shadows.ShadowOverrides; +import com.android.launcher3.util.LauncherLayoutBuilder; +import com.android.launcher3.util.LauncherLayoutBuilder.FolderBuilder; +import com.android.launcher3.util.LauncherModelHelper; +import com.android.launcher3.util.LauncherRoboTestRunner; +import com.android.launcher3.util.ViewOnDrawExecutor; +import com.android.launcher3.widget.WidgetsFullSheet; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.LooperMode; +import org.robolectric.annotation.LooperMode.Mode; +import org.robolectric.shadows.ShadowLooper; +import org.robolectric.util.ReflectionHelpers; + +/** + * Tests scroll behavior at various Launcher UI components + */ +@RunWith(LauncherRoboTestRunner.class) +@LooperMode(Mode.PAUSED) +public class LauncherUIScrollTest { + + private Context mTargetContext; + private InvariantDeviceProfile mIdp; + private LauncherModelHelper mModelHelper; + + private LauncherLayoutBuilder mLayoutBuilder; + + @Before + public void setup() throws Exception { + mModelHelper = new LauncherModelHelper(); + mTargetContext = RuntimeEnvironment.application; + mIdp = InvariantDeviceProfile.INSTANCE.get(mTargetContext); + ShadowOverrides.setProvider(UserEventDispatcher.class, + c -> mock(UserEventDispatcher.class)); + + Settings.Global.putFloat(mTargetContext.getContentResolver(), + Settings.Global.WINDOW_ANIMATION_SCALE, 0); + + mModelHelper.installApp(TEST_PACKAGE); + // LayoutBuilder with 3 workspace pages + mLayoutBuilder = new LauncherLayoutBuilder() + .atWorkspace(0, mIdp.numRows - 1, 0).putApp(TEST_PACKAGE, TEST_PACKAGE) + .atWorkspace(0, mIdp.numRows - 1, 1).putApp(TEST_PACKAGE, TEST_PACKAGE) + .atWorkspace(0, mIdp.numRows - 1, 2).putApp(TEST_PACKAGE, TEST_PACKAGE); + } + + @Test + public void testWorkspacePagesBound() throws Exception { + // Verify that the workspace if bound synchronously + Launcher launcher = loadLauncher(); + assertEquals(3, launcher.getWorkspace().getPageCount()); + assertEquals(0, launcher.getWorkspace().getCurrentPage()); + + launcher.dispatchGenericMotionEvent(createScrollEvent(-1)); + assertNotEquals("Workspace was not scrolled", + 0, launcher.getWorkspace().getNextPage()); + } + + @Test + public void testAllAppsScroll() throws Exception { + // Install 100 apps + for (int i = 0; i < 100; i++) { + mModelHelper.installApp(TEST_PACKAGE + i); + } + + // Bind and open all-apps + Launcher launcher = loadLauncher(); + launcher.getStateManager().goToState(LauncherState.ALL_APPS, false); + doLayout(launcher); + + int currentScroll = launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY(); + launcher.dispatchGenericMotionEvent(createScrollEvent(-1)); + int newScroll = launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY(); + + assertNotEquals("All Apps was not scrolled", currentScroll, newScroll); + assertEquals("Workspace was scrolled", 0, launcher.getWorkspace().getNextPage()); + } + + @Test + public void testWidgetsListScroll() throws Exception { + // Install 100 widgets + for (int i = 0; i < 100; i++) { + mModelHelper.installCustomShortcut(TEST_PACKAGE + i, "shortcutProvider"); + } + + // Bind and open widgets + Launcher launcher = loadLauncher(); + WidgetsFullSheet widgets = WidgetsFullSheet.show(launcher, false); + doLayout(launcher); + + int currentScroll = widgets.getRecyclerView().getCurrentScrollY(); + launcher.dispatchGenericMotionEvent(createScrollEvent(-1)); + int newScroll = widgets.getRecyclerView().getCurrentScrollY(); + assertNotEquals("Widgets was not scrolled", currentScroll, newScroll); + assertEquals("Workspace was scrolled", 0, launcher.getWorkspace().getNextPage()); + } + + @Test + public void testFolderPageScroll() throws Exception { + // Add a folder with multiple icons + FolderBuilder fb = mLayoutBuilder.atWorkspace(mIdp.numColumns / 2, mIdp.numRows / 2, 0) + .putFolder(0); + for (int i = 0; i < 100; i++) { + fb.addApp(TEST_PACKAGE, TEST_PACKAGE); + } + + // Bind and open folder + Launcher launcher = loadLauncher(); + doLayout(launcher); + launcher.getWorkspace().getFirstMatch((i, v) -> v instanceof FolderIcon).performClick(); + ShadowLooper.idleMainLooper(); + doLayout(launcher); + FolderPagedView folderPages = Folder.getOpen(launcher).getContent(); + + assertEquals(0, folderPages.getNextPage()); + launcher.dispatchGenericMotionEvent(createScrollEvent(-1)); + assertNotEquals("Folder page was not scrolled", 0, folderPages.getNextPage()); + assertEquals("Workspace was scrolled", 0, launcher.getWorkspace().getNextPage()); + } + + private Launcher loadLauncher() throws Exception { + mModelHelper.setupDefaultLayoutProvider(mLayoutBuilder).loadModelSync(); + + Launcher launcher = Robolectric.buildActivity(Launcher.class).setup().get(); + doLayout(launcher); + ViewOnDrawExecutor executor = ReflectionHelpers.getField(launcher, "mPendingExecutor"); + if (executor != null) { + executor.runAllTasks(); + } + return launcher; + } + + private static void doLayout(Activity activity) { + DeviceProfile dp = InvariantDeviceProfile.INSTANCE + .get(RuntimeEnvironment.application).portraitProfile; + View view = activity.getWindow().getDecorView(); + view.measure(makeMeasureSpec(dp.widthPx, EXACTLY), makeMeasureSpec(dp.heightPx, EXACTLY)); + view.layout(0, 0, dp.widthPx, dp.heightPx); + ShadowLooper.idleMainLooper(); + } + + private static MotionEvent createScrollEvent(int scroll) { + DeviceProfile dp = InvariantDeviceProfile.INSTANCE + .get(RuntimeEnvironment.application).portraitProfile; + + final PointerProperties[] pointerProperties = new PointerProperties[1]; + pointerProperties[0] = new PointerProperties(); + pointerProperties[0].id = 0; + final MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[1]; + coords[0] = new MotionEvent.PointerCoords(); + coords[0].setAxisValue(MotionEvent.AXIS_VSCROLL, scroll); + coords[0].x = dp.widthPx / 2; + coords[0].y = dp.heightPx / 2; + + final long time = SystemClock.uptimeMillis(); + return MotionEvent.obtain(time, time, MotionEvent.ACTION_SCROLL, 1, + pointerProperties, coords, 0, 0, 1.0f, 1.0f, 0, 0, + InputDevice.SOURCE_CLASS_POINTER, 0); + } + +} diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java b/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java index 20b1453b6f..d593d84778 100644 --- a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java +++ b/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.util; +import static android.content.Intent.ACTION_CREATE_SHORTCUT; + import static com.android.launcher3.LauncherSettings.Favorites.CONTENT_URI; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; @@ -44,6 +46,7 @@ import com.android.launcher3.LauncherProvider; import com.android.launcher3.LauncherSettings; import com.android.launcher3.model.AllAppsList; import com.android.launcher3.model.BgDataModel; +import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.pm.UserCache; import org.mockito.ArgumentCaptor; @@ -61,6 +64,7 @@ import java.io.OutputStreamWriter; import java.lang.reflect.Field; import java.util.HashMap; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.function.Function; @@ -345,7 +349,8 @@ public class LauncherModelHelper { /** * Sets up a dummy provider to load the provided layout by default, next time the layout loads */ - public void setupDefaultLayoutProvider(LauncherLayoutBuilder builder) throws Exception { + public LauncherModelHelper setupDefaultLayoutProvider(LauncherLayoutBuilder builder) + throws Exception { Context context = RuntimeEnvironment.application; InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(context); idp.numRows = idp.numColumns = idp.numHotseatIcons = DEFAULT_GRID_SIZE; @@ -363,22 +368,52 @@ public class LauncherModelHelper { Uri layoutUri = LauncherProvider.getLayoutUri(TEST_PROVIDER_AUTHORITY, context); shadowOf(context.getContentResolver()).registerInputStream(layoutUri, new ByteArrayInputStream(bos.toByteArray())); + return this; } /** * Simulates an apk install with a default main activity with same class and package name */ public void installApp(String component) throws NameNotFoundException { - ShadowPackageManager spm = shadowOf(RuntimeEnvironment.application.getPackageManager()); - ComponentName cn = new ComponentName(component, component); - spm.addActivityIfNotPresent(cn); - IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN); filter.addCategory(Intent.CATEGORY_LAUNCHER); + installApp(component, component, filter); + } + + /** + * Simulates a custom shortcut install + */ + public void installCustomShortcut(String pkg, String clazz) throws NameNotFoundException { + installApp(pkg, clazz, new IntentFilter(ACTION_CREATE_SHORTCUT)); + } + + private void installApp(String pkg, String clazz, IntentFilter filter) + throws NameNotFoundException { + ShadowPackageManager spm = shadowOf(RuntimeEnvironment.application.getPackageManager()); + ComponentName cn = new ComponentName(pkg, clazz); + spm.addActivityIfNotPresent(cn); + filter.addCategory(Intent.CATEGORY_DEFAULT); spm.addIntentFilterForActivity(cn, filter); } + /** + * Loads the model in memory synchronously + */ + public void loadModelSync() throws ExecutionException, InterruptedException { + // Since robolectric tests run on main thread, we run the loader-UI calls on a temp thread, + // so that we can wait appropriately for the loader to complete. + ReflectionHelpers.setField(getModel(), "mMainExecutor", Executors.UI_HELPER_EXECUTOR); + + Callbacks mockCb = mock(Callbacks.class); + getModel().addCallbacksAndLoad(mockCb); + + Executors.MODEL_EXECUTOR.submit(() -> { }).get(); + Executors.UI_HELPER_EXECUTOR.submit(() -> { }).get(); + ReflectionHelpers.setField(getModel(), "mMainExecutor", Executors.MAIN_EXECUTOR); + getModel().removeCallbacks(mockCb); + } + /** * An extension of LauncherProvider backed up by in-memory database. */ diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherRoboTestRunner.java b/robolectric_tests/src/com/android/launcher3/util/LauncherRoboTestRunner.java index 6277c66d2c..744b478b2a 100644 --- a/robolectric_tests/src/com/android/launcher3/util/LauncherRoboTestRunner.java +++ b/robolectric_tests/src/com/android/launcher3/util/LauncherRoboTestRunner.java @@ -21,10 +21,13 @@ import com.android.launcher3.shadows.LShadowAppWidgetManager; import com.android.launcher3.shadows.LShadowBackupManager; import com.android.launcher3.shadows.LShadowBitmap; import com.android.launcher3.shadows.LShadowLauncherApps; +import com.android.launcher3.shadows.LShadowTypeface; import com.android.launcher3.shadows.LShadowUserManager; +import com.android.launcher3.shadows.LShadowWallpaperManager; import com.android.launcher3.shadows.ShadowDeviceFlag; import com.android.launcher3.shadows.ShadowLooperExecutor; import com.android.launcher3.shadows.ShadowMainThreadInitializedObject; +import com.android.launcher3.shadows.ShadowOverrides; import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import org.junit.runners.model.InitializationError; @@ -49,9 +52,12 @@ public class LauncherRoboTestRunner extends RobolectricTestRunner { LShadowLauncherApps.class, LShadowBitmap.class, LShadowBackupManager.class, + LShadowTypeface.class, + LShadowWallpaperManager.class, ShadowLooperExecutor.class, ShadowMainThreadInitializedObject.class, ShadowDeviceFlag.class, + ShadowOverrides.class }; public LauncherRoboTestRunner(Class testClass) throws InitializationError { @@ -78,6 +84,9 @@ public class LauncherRoboTestRunner extends RobolectricTestRunner { // Disable plugins PluginManagerWrapper.INSTANCE.initializeForTesting(mock(PluginManagerWrapper.class)); + + // Initialize mock wallpaper manager + LShadowWallpaperManager.initializeMock(); } @Override @@ -86,6 +95,7 @@ public class LauncherRoboTestRunner extends RobolectricTestRunner { ShadowLog.stream = null; ShadowMainThreadInitializedObject.resetInitializedObjects(); + ShadowOverrides.clearProvider(); } } } diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index c8e73ba8d4..921e8ac14d 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -151,27 +151,25 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BubbleTextView, defStyle, 0); mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false); + DeviceProfile grid = mActivity.getDeviceProfile(); mDisplay = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE); final int defaultIconSize; if (mDisplay == DISPLAY_WORKSPACE) { - DeviceProfile grid = mActivity.getWallpaperDeviceProfile(); setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx); setCompoundDrawablePadding(grid.iconDrawablePaddingPx); defaultIconSize = grid.iconSizePx; } else if (mDisplay == DISPLAY_ALL_APPS) { - DeviceProfile grid = mActivity.getDeviceProfile(); setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx); setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx); defaultIconSize = grid.allAppsIconSizePx; } else if (mDisplay == DISPLAY_FOLDER) { - DeviceProfile grid = mActivity.getDeviceProfile(); setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.folderChildTextSizePx); setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx); defaultIconSize = grid.folderChildIconSizePx; } else { // widget_selection or shortcut_popup - defaultIconSize = mActivity.getDeviceProfile().iconSizePx; + defaultIconSize = grid.iconSizePx; } mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false); diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 9682d09cd1..4259196465 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -59,14 +59,12 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DraggableView; import com.android.launcher3.folder.PreviewBackground; import com.android.launcher3.graphics.DragPreviewProvider; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.util.CellAndSpan; import com.android.launcher3.util.GridOccupancy; import com.android.launcher3.util.ParcelableSparseArray; import com.android.launcher3.util.Themes; import com.android.launcher3.util.Thunk; import com.android.launcher3.views.ActivityContext; -import com.android.launcher3.views.Transposable; import com.android.launcher3.widget.LauncherAppWidgetHostView; import java.lang.annotation.Retention; @@ -77,7 +75,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Stack; -public class CellLayout extends ViewGroup implements Transposable { +public class CellLayout extends ViewGroup { private static final String TAG = "CellLayout"; private static final boolean LOGD = false; @@ -184,7 +182,6 @@ public class CellLayout extends ViewGroup implements Transposable { // Related to accessible drag and drop private boolean mUseTouchHelper = false; - private RotationMode mRotationMode = RotationMode.NORMAL; public CellLayout(Context context) { this(context, null); @@ -206,7 +203,7 @@ public class CellLayout extends ViewGroup implements Transposable { setClipToPadding(false); mActivity = ActivityContext.lookupContext(context); - DeviceProfile grid = mActivity.getWallpaperDeviceProfile(); + DeviceProfile grid = mActivity.getDeviceProfile(); mCellWidth = mCellHeight = -1; mFixedCellWidth = mFixedCellHeight = -1; @@ -314,24 +311,6 @@ public class CellLayout extends ViewGroup implements Transposable { } } - public void setRotationMode(RotationMode mode) { - if (mRotationMode != mode) { - mRotationMode = mode; - requestLayout(); - } - } - - @Override - public RotationMode getRotationMode() { - return mRotationMode; - } - - @Override - public void setPadding(int left, int top, int right, int bottom) { - mRotationMode.mapRect(left, top, right, bottom, mTempRect); - super.setPadding(mTempRect.left, mTempRect.top, mTempRect.right, mTempRect.bottom); - } - @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (mUseTouchHelper || @@ -789,13 +768,6 @@ public class CellLayout extends ViewGroup implements Transposable { int childWidthSize = widthSize - (getPaddingLeft() + getPaddingRight()); int childHeightSize = heightSize - (getPaddingTop() + getPaddingBottom()); - mShortcutsAndWidgets.setRotation(mRotationMode.surfaceRotation); - if (mRotationMode.isTransposed) { - int tmp = childWidthSize; - childWidthSize = childHeightSize; - childHeightSize = tmp; - } - if (mFixedCellWidth < 0 || mFixedCellHeight < 0) { int cw = DeviceProfile.calculateCellWidth(childWidthSize, mCountX); int ch = DeviceProfile.calculateCellHeight(childHeightSize, mCountY); @@ -846,15 +818,7 @@ public class CellLayout extends ViewGroup implements Transposable { right + mTempRect.right + getPaddingRight(), bottom + mTempRect.bottom + getPaddingBottom()); - if (mRotationMode.isTransposed) { - int halfW = mShortcutsAndWidgets.getMeasuredWidth() / 2; - int halfH = mShortcutsAndWidgets.getMeasuredHeight() / 2; - int cX = (left + right) / 2; - int cY = (top + bottom) / 2; - mShortcutsAndWidgets.layout(cX - halfW, cY - halfH, cX + halfW, cY + halfH); - } else { - mShortcutsAndWidgets.layout(left, top, right, bottom); - } + mShortcutsAndWidgets.layout(left, top, right, bottom); } /** @@ -863,8 +827,7 @@ public class CellLayout extends ViewGroup implements Transposable { * width in {@link DeviceProfile#calculateCellWidth(int, int)}. */ public int getUnusedHorizontalSpace() { - return (mRotationMode.isTransposed ? getMeasuredHeight() : getMeasuredWidth()) - - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth); + return getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth); } public Drawable getScrimBackground() { diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index 423f2bbf1c..6f0ebd2a09 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -67,7 +67,7 @@ public class DeleteDropTarget extends ButtonDropTarget { public boolean supportsAccessibilityDrop(ItemInfo info, View view) { if (info instanceof WorkspaceItemInfo) { // Support the action unless the item is in a context menu. - return info.screenId >= 0; + return canRemove(info); } return (info instanceof LauncherAppWidgetInfo) diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index c0490696c9..4e1e5863b3 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -76,9 +76,9 @@ public class DeviceProfile { public float workspaceSpringLoadShrinkFactor; public final int workspaceSpringLoadedBottomSpace; - // Drag handle - public final int verticalDragHandleSizePx; - private final int verticalDragHandleOverlapWorkspace; + // Workspace page indicator + public final int workspacePageIndicatorHeight; + private final int mWorkspacePageIndicatorOverlapWorkspace; // Workspace icons public int iconSizePx; @@ -190,10 +190,10 @@ public class DeviceProfile { cellLayoutBottomPaddingPx = 0; } - verticalDragHandleSizePx = res.getDimensionPixelSize( - R.dimen.vertical_drag_handle_size); - verticalDragHandleOverlapWorkspace = - res.getDimensionPixelSize(R.dimen.vertical_drag_handle_overlap_workspace); + workspacePageIndicatorHeight = res.getDimensionPixelSize( + R.dimen.workspace_page_indicator_height); + mWorkspacePageIndicatorOverlapWorkspace = + res.getDimensionPixelSize(R.dimen.workspace_page_indicator_overlap_workspace); iconDrawablePaddingOriginalPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding); @@ -211,7 +211,7 @@ public class DeviceProfile { hotseatBarSidePaddingEndPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding); // Add a bit of space between nav bar and hotseat in vertical bar layout. - hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? verticalDragHandleSizePx : 0; + hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0; hotseatBarSizePx = ResourceUtils.pxFromDp(inv.iconSize, dm) + (isVerticalBarLayout() ? (hotseatBarSidePaddingStartPx + hotseatBarSidePaddingEndPx) : (res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size) @@ -227,7 +227,7 @@ public class DeviceProfile { // in portrait mode closer together by adding more height to the hotseat. // Note: This calculation was created after noticing a pattern in the design spec. int extraSpace = getCellSize().y - iconSizePx - iconDrawablePaddingPx * 2 - - verticalDragHandleSizePx; + - workspacePageIndicatorHeight; hotseatBarSizePx += extraSpace; hotseatBarBottomPaddingPx += extraSpace; @@ -376,7 +376,7 @@ public class DeviceProfile { if (!isVerticalLayout) { int expectedWorkspaceHeight = availableHeightPx - hotseatBarSizePx - - verticalDragHandleSizePx - edgeMarginPx; + - workspacePageIndicatorHeight - edgeMarginPx; float minRequiredHeight = dropTargetBarSizePx + workspaceSpringLoadedBottomSpace; workspaceSpringLoadShrinkFactor = Math.min( res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f, @@ -480,14 +480,14 @@ public class DeviceProfile { padding.bottom = edgeMarginPx; if (isSeascape()) { padding.left = hotseatBarSizePx; - padding.right = verticalDragHandleSizePx; + padding.right = hotseatBarSidePaddingStartPx; } else { - padding.left = verticalDragHandleSizePx; + padding.left = hotseatBarSidePaddingStartPx; padding.right = hotseatBarSizePx; } } else { - int paddingBottom = hotseatBarSizePx + verticalDragHandleSizePx - - verticalDragHandleOverlapWorkspace; + int paddingBottom = hotseatBarSizePx + workspacePageIndicatorHeight + - mWorkspacePageIndicatorOverlapWorkspace; if (isTablet) { // Pad the left and right of the workspace to ensure consistent spacing // between all icons @@ -554,7 +554,7 @@ public class DeviceProfile { mInsets.top + dropTargetBarSizePx + edgeMarginPx, mInsets.left + availableWidthPx - edgeMarginPx, mInsets.top + availableHeightPx - hotseatBarSizePx - - verticalDragHandleSizePx - edgeMarginPx); + - workspacePageIndicatorHeight - edgeMarginPx); } } diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index 76cfe1c59e..78bd2ff502 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -27,15 +27,13 @@ import android.view.ViewDebug; import android.view.ViewGroup; import android.widget.FrameLayout; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; -import com.android.launcher3.views.Transposable; import java.util.ArrayList; -public class Hotseat extends CellLayout implements LogContainerProvider, Insettable, Transposable { +public class Hotseat extends CellLayout implements LogContainerProvider, Insettable { @ViewDebug.ExportedProperty(category = "launcher") private boolean mHasVerticalHotseat; @@ -89,7 +87,7 @@ public class Hotseat extends CellLayout implements LogContainerProvider, Insetta @Override public void setInsets(Rect insets) { FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); - DeviceProfile grid = mActivity.getWallpaperDeviceProfile(); + DeviceProfile grid = mActivity.getDeviceProfile(); insets = grid.getInsets(); if (grid.isVerticalBarLayout()) { @@ -117,9 +115,4 @@ public class Hotseat extends CellLayout implements LogContainerProvider, Insetta public boolean onTouchEvent(MotionEvent event) { return event.getY() > getCellHeight(); } - - @Override - public RotationMode getRotationMode() { - return Launcher.getLauncher(getContext()).getRotationMode(); - } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 5b9f676bbd..043ea2fc10 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -56,7 +56,6 @@ import android.content.pm.PackageManager; import android.content.res.Configuration; import android.database.sqlite.SQLiteDatabase; import android.graphics.Point; -import android.graphics.Rect; import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; @@ -102,7 +101,6 @@ import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderGridOrganizer; import com.android.launcher3.folder.FolderIcon; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.icons.IconCache; import com.android.launcher3.keyboard.CustomActionsPopup; import com.android.launcher3.keyboard.ViewGroupFocusHelper; @@ -317,9 +315,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, private float mCurrentAssistantVisibility = 0f; - private DeviceProfile mStableDeviceProfile; - private RotationMode mRotationMode = RotationMode.NORMAL; - protected LauncherOverlayManager mOverlayManager; // If true, overlay callbacks are deferred private boolean mDeferOverlayCallbacks; @@ -503,24 +498,12 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, super.onConfigurationChanged(newConfig); } - public void reload() { - onIdpChanged(mDeviceProfile.inv); - } - - private boolean supportsFakeLandscapeUI() { - return FeatureFlags.FAKE_LANDSCAPE_UI.get() && !mRotationHelper.homeScreenCanRotate(); - } - @Override public void reapplyUi() { reapplyUi(true /* cancelCurrentAnimation */); } public void reapplyUi(boolean cancelCurrentAnimation) { - if (supportsFakeLandscapeUI()) { - mRotationMode = mStableDeviceProfile == null - ? RotationMode.NORMAL : getFakeRotationMode(mDeviceProfile); - } getRootView().dispatchInsets(); getStateManager().reapplyState(cancelCurrentAnimation); } @@ -533,7 +516,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, private void onIdpChanged(InvariantDeviceProfile idp) { mUserEventDispatcher = null; - DeviceProfile oldWallpaperProfile = getWallpaperDeviceProfile(); initDeviceProfile(idp); dispatchDeviceProfileChanged(); reapplyUi(); @@ -542,9 +524,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, // Calling onSaveInstanceState ensures that static cache used by listWidgets is // initialized properly. onSaveInstanceState(new Bundle()); - if (oldWallpaperProfile != getWallpaperDeviceProfile()) { - mModel.rebindCallbacks(); - } + mModel.rebindCallbacks(); } public void onAssistantVisibilityChanged(float visibility) { @@ -571,41 +551,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize); } - if (supportsFakeLandscapeUI() && mDeviceProfile.isVerticalBarLayout()) { - mStableDeviceProfile = mDeviceProfile.inv.portraitProfile; - mRotationMode = getFakeRotationMode(mDeviceProfile); - } else { - mStableDeviceProfile = null; - mRotationMode = RotationMode.NORMAL; - } - - mRotationHelper.updateRotationAnimation(); onDeviceProfileInitiated(); - mModelWriter = mModel.getWriter(getWallpaperDeviceProfile().isVerticalBarLayout(), true); - } - - public void updateInsets(Rect insets) { - mDeviceProfile.updateInsets(insets); - if (mStableDeviceProfile != null) { - Rect r = mStableDeviceProfile.getInsets(); - mRotationMode.mapInsets(this, insets, r); - mStableDeviceProfile.updateInsets(r); - } - } - - @Override - public RotationMode getRotationMode() { - return mRotationMode; - } - - /** - * Device profile to be used by UI elements which are shown directly on top of the wallpaper - * and whose presentation is tied to the wallpaper (and physical device) and not the activity - * configuration. - */ - @Override - public DeviceProfile getWallpaperDeviceProfile() { - return mStableDeviceProfile == null ? mDeviceProfile : mStableDeviceProfile; + mModelWriter = mModel.getWriter(getDeviceProfile().isVerticalBarLayout(), true); } public RotationHelper getRotationHelper() { @@ -1193,7 +1140,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, // Setup the drag controller (drop targets have to be added in reverse order in priority) mDropTargetBar.setup(mDragController); - mAllAppsController.setupViews(mAppsView); + mAllAppsController.setupViews(mAppsView, mScrimView); } /** @@ -1415,6 +1362,10 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, return mDropTargetBar; } + public ScrimView getScrimView() { + return mScrimView; + } + public LauncherAppWidgetHost getAppWidgetHost() { return mAppWidgetHost; } @@ -1753,7 +1704,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, public FolderIcon addFolder(CellLayout layout, int container, final int screenId, int cellX, int cellY) { final FolderInfo folderInfo = new FolderInfo(); - folderInfo.title = ""; // Update the model getModelWriter().addItemToDatabase(folderInfo, container, screenId, cellX, cellY); @@ -2049,7 +1999,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, mAppWidgetHost.clearViews(); if (mHotseat != null) { - mHotseat.resetLayout(getWallpaperDeviceProfile().isVerticalBarLayout()); + mHotseat.resetLayout(getDeviceProfile().isVerticalBarLayout()); } TraceHelper.INSTANCE.endSection(traceToken); } @@ -2724,10 +2674,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, return new TouchController[] {getDragController(), new AllAppsSwipeController(this)}; } - protected RotationMode getFakeRotationMode(DeviceProfile deviceProfile) { - return RotationMode.NORMAL; - } - protected ScaleAndTranslation getOverviewScaleAndTranslationForNormalState() { return new ScaleAndTranslation(1.1f, 0f, 0f); } diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java index 2b2224a600..b4fbbc3279 100644 --- a/src/com/android/launcher3/LauncherRootView.java +++ b/src/com/android/launcher3/LauncherRootView.java @@ -83,7 +83,7 @@ public class LauncherRootView extends InsettableFrameLayout { UI_STATE_ROOT_VIEW, drawInsetBar ? FLAG_DARK_NAV : 0); // Update device profile before notifying th children. - mLauncher.updateInsets(insets); + mLauncher.getDeviceProfile().updateInsets(insets); boolean resetState = !insets.equals(mInsets); setInsets(insets); @@ -127,7 +127,7 @@ public class LauncherRootView extends InsettableFrameLayout { } public void dispatchInsets() { - mLauncher.updateInsets(mInsets); + mLauncher.getDeviceProfile().updateInsets(mInsets); super.setInsets(mInsets); } diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 7d7739eb12..5e47e2ff73 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -16,6 +16,14 @@ package com.android.launcher3; +import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; +import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType; +import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; +import static com.android.launcher3.touch.OverScroll.OVERSCROLL_DAMP_FACTOR; +import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE; +import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_BY; +import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_TO; + import android.animation.LayoutTransition; import android.animation.TimeInterpolator; import android.annotation.SuppressLint; @@ -42,14 +50,6 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.Interpolator; import android.widget.ScrollView; -import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; -import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType; -import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; -import static com.android.launcher3.touch.OverScroll.OVERSCROLL_DAMP_FACTOR; -import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE; -import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_BY; -import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_TO; - import androidx.annotation.Nullable; import com.android.launcher3.anim.Interpolators; @@ -63,6 +63,7 @@ import com.android.launcher3.touch.PagedOrientationHandler.ChildBounds; import com.android.launcher3.touch.PortraitPagedViewHandler; import com.android.launcher3.util.OverScroller; import com.android.launcher3.util.Thunk; +import com.android.launcher3.views.ActivityContext; import java.util.ArrayList; @@ -1369,10 +1370,6 @@ public abstract class PagedView extends ViewGrou if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { switch (event.getAction()) { case MotionEvent.ACTION_SCROLL: { - Launcher launcher = Launcher.getLauncher(getContext()); - if (launcher != null) { - AbstractFloatingView.closeAllOpenViews(launcher); - } // Handle mouse (or ext. device) by shifting the page depending on the scroll final float vscroll; final float hscroll; @@ -1383,8 +1380,8 @@ public abstract class PagedView extends ViewGrou vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL); hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL); } - if (Math.abs(vscroll) > Math.abs(hscroll) && !isVerticalScrollable()) { - return true; + if (!canScroll(Math.abs(vscroll), Math.abs(hscroll))) { + return false; } if (hscroll != 0 || vscroll != 0) { boolean isForwardScroll = mIsRtl ? (hscroll < 0 || vscroll < 0) @@ -1402,8 +1399,13 @@ public abstract class PagedView extends ViewGrou return super.onGenericMotionEvent(event); } - protected boolean isVerticalScrollable() { - return true; + /** + * Returns true if the paged view can scroll for the provided vertical and horizontal + * scroll values + */ + protected boolean canScroll(float absVScroll, float absHScroll) { + ActivityContext ac = ActivityContext.lookupContext(getContext()); + return (ac == null || AbstractFloatingView.getTopOpenView(ac) == null); } private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) { diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java index c07dd9d3f8..6326b7ac6d 100644 --- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java +++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java @@ -93,7 +93,7 @@ public class ShortcutAndWidgetContainer extends ViewGroup { public void setupLp(View child) { CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); if (child instanceof LauncherAppWidgetHostView) { - DeviceProfile profile = mActivity.getWallpaperDeviceProfile(); + DeviceProfile profile = mActivity.getDeviceProfile(); lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, profile.appWidgetScale.x, profile.appWidgetScale.y); } else { @@ -108,12 +108,12 @@ public class ShortcutAndWidgetContainer extends ViewGroup { public int getCellContentHeight() { return Math.min(getMeasuredHeight(), - mActivity.getWallpaperDeviceProfile().getCellHeight(mContainerType)); + mActivity.getDeviceProfile().getCellHeight(mContainerType)); } public void measureChild(View child) { CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); - final DeviceProfile profile = mActivity.getWallpaperDeviceProfile(); + final DeviceProfile profile = mActivity.getDeviceProfile(); if (child instanceof LauncherAppWidgetHostView) { lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 122b393c7f..0cd08d4faa 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -62,7 +62,6 @@ import android.view.animation.Interpolator; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.FolderAdaptiveIcon; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.graphics.TintedDrawableSpan; import com.android.launcher3.icons.IconProvider; import com.android.launcher3.icons.LauncherIcons; @@ -72,7 +71,6 @@ import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.shortcuts.ShortcutRequest; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.PackageManagerHelper; -import com.android.launcher3.views.Transposable; import com.android.launcher3.widget.PendingAddShortcutInfo; import java.lang.reflect.Method; @@ -165,7 +163,7 @@ public final class Utilities { public static float getDescendantCoordRelativeToAncestor( View descendant, View ancestor, float[] coord, boolean includeRootScroll) { return getDescendantCoordRelativeToAncestor(descendant, ancestor, coord, includeRootScroll, - false, null); + false); } /** @@ -178,15 +176,12 @@ public final class Utilities { * @param includeRootScroll Whether or not to account for the scroll of the descendant: * sometimes this is relevant as in a child's coordinates within the descendant. * @param ignoreTransform If true, view transform is ignored - * @param outRotation If not null, and {@param ignoreTransform} is true, this is set to the - * overall rotation of the view in degrees. * @return The factor by which this descendant is scaled relative to this DragLayer. Caution * this scale factor is assumed to be equal in X and Y, and so if at any point this * assumption fails, we will need to return a pair of scale factors. */ public static float getDescendantCoordRelativeToAncestor(View descendant, View ancestor, - float[] coord, boolean includeRootScroll, boolean ignoreTransform, - float[] outRotation) { + float[] coord, boolean includeRootScroll, boolean ignoreTransform) { float scale = 1.0f; View v = descendant; while(v != ancestor && v != null) { @@ -196,19 +191,7 @@ public final class Utilities { offsetPoints(coord, -v.getScrollX(), -v.getScrollY()); } - if (ignoreTransform) { - if (v instanceof Transposable) { - RotationMode m = ((Transposable) v).getRotationMode(); - if (m.isTransposed) { - sMatrix.setRotate(m.surfaceRotation, v.getPivotX(), v.getPivotY()); - sMatrix.mapPoints(coord); - - if (outRotation != null) { - outRotation[0] += m.surfaceRotation; - } - } - } - } else { + if (!ignoreTransform) { v.getMatrix().mapPoints(coord); } offsetPoints(coord, v.getLeft(), v.getTop()); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 8bc0242920..3a3de264fd 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -75,7 +75,6 @@ import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.folder.PreviewBackground; import com.android.launcher3.graphics.DragPreviewProvider; import com.android.launcher3.graphics.PreloadIconDrawable; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.icons.BitmapRenderer; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.pageindicators.WorkspacePageIndicator; @@ -125,7 +124,7 @@ public class Workspace extends PagedView private static final int ADJACENT_SCREEN_DROP_DURATION = 300; - private static final int DEFAULT_PAGE = 0; + public static final int DEFAULT_PAGE = 0; private LayoutTransition mLayoutTransition; @Thunk final WallpaperManager mWallpaperManager; @@ -276,20 +275,13 @@ public class Workspace extends PagedView @Override public void setInsets(Rect insets) { DeviceProfile grid = mLauncher.getDeviceProfile(); - DeviceProfile stableGrid = mLauncher.getWallpaperDeviceProfile(); - mMaxDistanceForFolderCreation = stableGrid.isTablet - ? 0.75f * stableGrid.iconSizePx - : 0.55f * stableGrid.iconSizePx; + mMaxDistanceForFolderCreation = grid.isTablet + ? 0.75f * grid.iconSizePx : 0.55f * grid.iconSizePx; mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens(); - Rect padding = stableGrid.workspacePadding; - - RotationMode rotationMode = mLauncher.getRotationMode(); - - rotationMode.mapRect(padding, mTempRect); - setPadding(mTempRect.left, mTempRect.top, mTempRect.right, mTempRect.bottom); - rotationMode.mapRect(stableGrid.getInsets(), mInsets); + Rect padding = grid.workspacePadding; + setPadding(padding.left, padding.top, padding.right, padding.bottom); if (mWorkspaceFadeInAdjacentScreens) { // In landscape mode the page spacing is set to the default. @@ -302,12 +294,11 @@ public class Workspace extends PagedView } - int paddingLeftRight = stableGrid.cellLayoutPaddingLeftRightPx; - int paddingBottom = stableGrid.cellLayoutBottomPaddingPx; + int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx; + int paddingBottom = grid.cellLayoutBottomPaddingPx; for (int i = mWorkspaceScreens.size() - 1; i >= 0; i--) { - CellLayout page = mWorkspaceScreens.valueAt(i); - page.setRotationMode(rotationMode); - page.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom); + mWorkspaceScreens.valueAt(i) + .setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom); } } @@ -327,7 +318,7 @@ public class Workspace extends PagedView float scale = 1; if (isWidget) { - DeviceProfile profile = mLauncher.getWallpaperDeviceProfile(); + DeviceProfile profile = mLauncher.getDeviceProfile(); scale = Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y); } size[0] = r.width(); @@ -555,10 +546,9 @@ public class Workspace extends PagedView // created CellLayout. CellLayout newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate( R.layout.workspace_screen, this, false /* attachToRoot */); - DeviceProfile grid = mLauncher.getWallpaperDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx; int paddingBottom = grid.cellLayoutBottomPaddingPx; - newScreen.setRotationMode(mLauncher.getRotationMode()); newScreen.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom); mWorkspaceScreens.put(screenId, newScreen); diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index c521c34e33..c4c4377e83 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -104,10 +104,8 @@ public class WorkspaceStateTransitionAnimation { Interpolator scaleInterpolator = config.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT); propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator); - if (!hotseat.getRotationMode().isTransposed) { - setPivotToScaleWithWorkspace(hotseat); - setPivotToScaleWithWorkspace(qsbScaleView); - } + setPivotToScaleWithWorkspace(hotseat); + setPivotToScaleWithWorkspace(qsbScaleView); float hotseatScale = hotseatScaleAndTranslation.scale; Interpolator hotseatScaleInterpolator = config.getInterpolator(ANIM_HOTSEAT_SCALE, scaleInterpolator); diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index 24c846cf83..414ababc98 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -56,6 +56,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme public static final int REMOVE = R.id.action_remove; public static final int UNINSTALL = R.id.action_uninstall; public static final int DISMISS_PREDICTION = R.id.action_dismiss_prediction; + public static final int PIN_PREDICTION = R.id.action_pin_prediction; public static final int RECONFIGURE = R.id.action_reconfigure; protected static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace; protected static final int MOVE = R.id.action_move; @@ -120,6 +121,10 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme if (!(host.getTag() instanceof ItemInfo)) return; ItemInfo item = (ItemInfo) host.getTag(); + if (host instanceof AccessibilityActionHandler) { + ((AccessibilityActionHandler) host).addSupportedAccessibilityActions(info); + } + // If the request came from keyboard, do not add custom shortcuts as that is already // exposed as a direct shortcut if (!fromKeyboard && ShortcutUtil.supportsShortcuts(item)) { @@ -157,14 +162,14 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme } private boolean itemSupportsLongClick(View host, ItemInfo info) { - return new CustomActionsPopup(mLauncher, host).canShow() - || ShortcutUtil.supportsShortcuts(info); + return PopupContainerWithArrow.canShow(host, info) + || new CustomActionsPopup(mLauncher, host).canShow(); } private boolean itemSupportsAccessibleDrag(ItemInfo item) { if (item instanceof WorkspaceItemInfo) { // Support the action unless the item is in a context menu. - return item.screenId >= 0; + return item.screenId >= 0 && item.container != Favorites.CONTAINER_HOTSEAT_PREDICTION; } return (item instanceof LauncherAppWidgetInfo) || (item instanceof FolderInfo); @@ -181,7 +186,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme public boolean performAction(final View host, final ItemInfo item, int action) { if (action == ACTION_LONG_CLICK) { - if (ShortcutUtil.supportsShortcuts(item)) { + if (PopupContainerWithArrow.canShow(host, item)) { // Long press should be consumed for workspace items, and it should invoke the // Shortcuts / Notifications / Actions pop-up menu, and not start a drag as the // standard long press path does. @@ -195,7 +200,10 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme } } } - + if (host instanceof AccessibilityActionHandler + && ((AccessibilityActionHandler) host).performAccessibilityAction(action, item)) { + return true; + } if (action == MOVE) { beginAccessibleDrag(host, item); } else if (action == ADD_TO_WORKSPACE) { @@ -466,4 +474,20 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme } return screenId; } + + /** + * An interface allowing views to handle their own action. + */ + public interface AccessibilityActionHandler { + + /** + * performs accessibility action and returns true on success + */ + boolean performAccessibilityAction(int action, ItemInfo itemInfo); + + /** + * adds all the accessibility actions that can be handled. + */ + void addSupportedAccessibilityActions(AccessibilityNodeInfo accessibilityNodeInfo); + } } diff --git a/src/com/android/launcher3/allapps/AllAppsPagedView.java b/src/com/android/launcher3/allapps/AllAppsPagedView.java index ab4cb6b8db..f640c3e8d0 100644 --- a/src/com/android/launcher3/allapps/AllAppsPagedView.java +++ b/src/com/android/launcher3/allapps/AllAppsPagedView.java @@ -83,7 +83,7 @@ public class AllAppsPagedView extends PagedView { } @Override - protected boolean isVerticalScrollable() { - return false; + protected boolean canScroll(float absVScroll, float absHScroll) { + return (absHScroll > absVScroll) && super.canScroll(absVScroll, absHScroll); } } diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 7600f527d0..68b070617d 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -123,8 +123,8 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil // Use a light system UI (dark icons) if all apps is behind at least half of the // status bar. - boolean forceChange = shiftCurrent - mScrimView.getDragHandleSize() - <= mLauncher.getDeviceProfile().getInsets().top / 2; + boolean forceChange = Math.min(shiftCurrent, mScrimView.getVisualTop()) + <= mLauncher.getDeviceProfile().getInsets().top / 2f; if (forceChange) { mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, !mIsDarkTheme); } else { @@ -212,9 +212,9 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil return AnimationSuccessListener.forRunnable(this::onProgressAnimationEnd); } - public void setupViews(AllAppsContainerView appsView) { + public void setupViews(AllAppsContainerView appsView, ScrimView scrimView) { mAppsView = appsView; - mScrimView = mLauncher.findViewById(R.id.scrim_view); + mScrimView = scrimView; } /** diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 92f511222d..ec3435024c 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -95,9 +95,6 @@ public final class FeatureFlags { public static final BooleanFlag ENABLE_HINTS_IN_OVERVIEW = getDebugFlag( "ENABLE_HINTS_IN_OVERVIEW", false, "Show chip hints and gleams on the overview screen"); - public static final BooleanFlag FAKE_LANDSCAPE_UI = getDebugFlag( - "FAKE_LANDSCAPE_UI", false, "Rotate launcher UI instead of using transposed layout"); - public static final BooleanFlag FOLDER_NAME_SUGGEST = new DeviceFlag( "FOLDER_NAME_SUGGEST", true, "Suggests folder names instead of blank text."); diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index 9ece3d3bca..970c5a02e7 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -17,10 +17,6 @@ package com.android.launcher3.dragndrop; -import static android.view.View.MeasureSpec.EXACTLY; -import static android.view.View.MeasureSpec.getMode; -import static android.view.View.MeasureSpec.getSize; - import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5; import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent; @@ -34,14 +30,12 @@ import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Rect; import android.util.AttributeSet; -import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.animation.Interpolator; -import android.widget.FrameLayout; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.CellLayout; @@ -52,12 +46,10 @@ import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.Workspace; import com.android.launcher3.folder.Folder; import com.android.launcher3.graphics.OverviewScrim; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.graphics.WorkspaceAndHotseatScrim; import com.android.launcher3.keyboard.ViewGroupFocusHelper; import com.android.launcher3.util.Thunk; import com.android.launcher3.views.BaseDragLayer; -import com.android.launcher3.views.Transposable; import java.util.ArrayList; @@ -560,145 +552,4 @@ public class DragLayer extends BaseDragLayer { public OverviewScrim getOverviewScrim() { return mOverviewScrim; } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - RotationMode rotation = mActivity.getRotationMode(); - int count = getChildCount(); - - if (!rotation.isTransposed - || getMode(widthMeasureSpec) != EXACTLY - || getMode(heightMeasureSpec) != EXACTLY) { - - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - child.setRotation(rotation.surfaceRotation); - } - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } else { - - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.getVisibility() == GONE) { - continue; - } - if (!(child instanceof Transposable)) { - measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); - } else { - measureChildWithMargins(child, heightMeasureSpec, 0, widthMeasureSpec, 0); - - child.setPivotX(child.getMeasuredWidth() / 2); - child.setPivotY(child.getMeasuredHeight() / 2); - child.setRotation(rotation.surfaceRotation); - } - } - setMeasuredDimension(getSize(widthMeasureSpec), getSize(heightMeasureSpec)); - } - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - RotationMode rotation = mActivity.getRotationMode(); - if (!rotation.isTransposed) { - super.onLayout(changed, left, top, right, bottom); - return; - } - - final int count = getChildCount(); - - final int parentWidth = right - left; - final int parentHeight = bottom - top; - - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.getVisibility() == GONE) { - continue; - } - - final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) child.getLayoutParams(); - - if (lp instanceof LayoutParams) { - final LayoutParams dlp = (LayoutParams) lp; - if (dlp.customPosition) { - child.layout(dlp.x, dlp.y, dlp.x + dlp.width, dlp.y + dlp.height); - continue; - } - } - - final int width = child.getMeasuredWidth(); - final int height = child.getMeasuredHeight(); - - int childLeft; - int childTop; - - int gravity = lp.gravity; - if (gravity == -1) { - gravity = Gravity.TOP | Gravity.START; - } - - final int layoutDirection = getLayoutDirection(); - - int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); - - if (child instanceof Transposable) { - absoluteGravity = rotation.toNaturalGravity(absoluteGravity); - - switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.CENTER_HORIZONTAL: - childTop = (parentHeight - height) / 2 + - lp.topMargin - lp.bottomMargin; - break; - case Gravity.RIGHT: - childTop = width / 2 + lp.rightMargin - height / 2; - break; - case Gravity.LEFT: - default: - childTop = parentHeight - lp.leftMargin - width / 2 - height / 2; - } - - switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) { - case Gravity.CENTER_VERTICAL: - childLeft = (parentWidth - width) / 2 + - lp.leftMargin - lp.rightMargin; - break; - case Gravity.BOTTOM: - childLeft = parentWidth - width / 2 - height / 2 - lp.bottomMargin; - break; - case Gravity.TOP: - default: - childLeft = height / 2 - width / 2 + lp.topMargin; - } - } else { - switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.CENTER_HORIZONTAL: - childLeft = (parentWidth - width) / 2 + - lp.leftMargin - lp.rightMargin; - break; - case Gravity.RIGHT: - childLeft = parentWidth - width - lp.rightMargin; - break; - case Gravity.LEFT: - default: - childLeft = lp.leftMargin; - } - - switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) { - case Gravity.TOP: - childTop = lp.topMargin; - break; - case Gravity.CENTER_VERTICAL: - childTop = (parentHeight - height) / 2 + - lp.topMargin - lp.bottomMargin; - break; - case Gravity.BOTTOM: - childTop = parentHeight - height - lp.bottomMargin; - break; - default: - childTop = lp.topMargin; - } - } - - child.layout(childLeft, childTop, childLeft + width, childTop + height); - } - } } diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 365e76fa2b..202836daa5 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -437,7 +437,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } mItemsInvalidated = true; mInfo.addListener(this); - mPreviousLabel = mInfo.title.toString(); + Optional.ofNullable(mInfo.title).ifPresent(title -> mPreviousLabel = title.toString()); mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME); if (!isEmpty(mInfo.title)) { @@ -1648,6 +1648,10 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } } + public FolderPagedView getContent() { + return mContent; + } + private void logEditFolderLabel() { LauncherEvent launcherEvent = LauncherEvent.newBuilder() .setAction(Action.newBuilder().setType(Action.Type.SOFT_KEYBOARD)) diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java index b83609e680..3d72b49b44 100644 --- a/src/com/android/launcher3/folder/FolderAnimationManager.java +++ b/src/com/android/launcher3/folder/FolderAnimationManager.java @@ -115,6 +115,7 @@ public class FolderAnimationManager { */ public AnimatorSet getAnimator() { final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) mFolder.getLayoutParams(); + mFolderIcon.getPreviewItemManager().recomputePreviewDrawingParams(); ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule(); final List itemsInPreview = getPreviewIconsOnPage(0); diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 8251d68b70..eda9545b17 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -172,7 +172,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel "is dependent on this"); } - DeviceProfile grid = activity.getWallpaperDeviceProfile(); + DeviceProfile grid = activity.getDeviceProfile(); FolderIcon icon = (FolderIcon) LayoutInflater.from(group.getContext()) .inflate(resId, group, false); @@ -570,8 +570,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel public void drawDot(Canvas canvas) { if (!mForceHideDot && ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0)) { Rect iconBounds = mDotParams.iconBounds; - BubbleTextView.getIconBounds(this, iconBounds, - mActivity.getWallpaperDeviceProfile().iconSizePx); + BubbleTextView.getIconBounds(this, iconBounds, mActivity.getDeviceProfile().iconSizePx); float iconScale = (float) mBackground.previewSize / iconBounds.width(); Utilities.scaleRectAboutCenter(iconBounds, iconScale); diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java index c6d62f8bc7..dcd0e144bc 100644 --- a/src/com/android/launcher3/folder/FolderPagedView.java +++ b/src/com/android/launcher3/folder/FolderPagedView.java @@ -16,6 +16,9 @@ package com.android.launcher3.folder; +import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; +import static com.android.launcher3.AbstractFloatingView.TYPE_FOLDER; + import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; @@ -27,6 +30,7 @@ import android.view.Gravity; import android.view.View; import android.view.ViewDebug; +import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BaseActivity; import com.android.launcher3.BubbleTextView; import com.android.launcher3.CellLayout; @@ -258,7 +262,7 @@ public class FolderPagedView extends PagedView { @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); - mPageIndicator.setScroll(l, mMaxScroll); + if (mMaxScroll > 0) mPageIndicator.setScroll(l, mMaxScroll); } /** @@ -614,6 +618,12 @@ public class FolderPagedView extends PagedView { } } + @Override + protected boolean canScroll(float absVScroll, float absHScroll) { + return AbstractFloatingView.getTopOpenViewWithType(mFolder.mLauncher, + TYPE_ALL & ~TYPE_FOLDER) == null; + } + public int itemsPerPage() { return mOrganizer.getMaxItemsPerPage(); } diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java index 2d177d2f2d..27b906bcbb 100644 --- a/src/com/android/launcher3/folder/PreviewBackground.java +++ b/src/com/android/launcher3/folder/PreviewBackground.java @@ -153,7 +153,7 @@ public class PreviewBackground extends CellLayout.DelegatedCellDrawing { mBgColor = ta.getColor(R.styleable.FolderIconPreview_folderFillColor, 0); ta.recycle(); - DeviceProfile grid = activity.getWallpaperDeviceProfile(); + DeviceProfile grid = activity.getDeviceProfile(); previewSize = grid.folderIconSizePx; basePreviewOffsetX = (availableSpaceX - previewSize) / 2; diff --git a/src/com/android/launcher3/graphics/RotationMode.java b/src/com/android/launcher3/graphics/RotationMode.java deleted file mode 100644 index 6dd356ada9..0000000000 --- a/src/com/android/launcher3/graphics/RotationMode.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.graphics; - -import android.content.Context; -import android.graphics.Rect; - -public abstract class RotationMode { - - public static final RotationMode NORMAL = new RotationMode(0) { }; - - public final float surfaceRotation; - public final boolean isTransposed; - - public RotationMode(float surfaceRotation) { - this.surfaceRotation = surfaceRotation; - isTransposed = surfaceRotation != 0; - } - - public final void mapRect(Rect rect, Rect out) { - mapRect(rect.left, rect.top, rect.right, rect.bottom, out); - } - - public void mapRect(int left, int top, int right, int bottom, Rect out) { - out.set(left, top, right, bottom); - } - - public void mapInsets(Context context, Rect insets, Rect out) { - out.set(insets); - } - - public int toNaturalGravity(int absoluteGravity) { - return absoluteGravity; - } -} diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 62904ae4a4..fc0997b198 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -45,8 +45,6 @@ import android.util.LongSparseArray; import android.util.MutableInt; import android.util.TimingLogger; -import androidx.annotation.VisibleForTesting; - import com.android.launcher3.AppInfo; import com.android.launcher3.FolderInfo; import com.android.launcher3.InstallShortcutReceiver; @@ -282,8 +280,7 @@ public class LoaderTask implements Runnable { this.notify(); } - @VisibleForTesting - void loadWorkspace(List allDeepShortcuts) { + private void loadWorkspace(List allDeepShortcuts) { loadWorkspace(allDeepShortcuts, LauncherSettings.Favorites.CONTENT_URI); } diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java index 0f2ca72c0e..408796f8c7 100644 --- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java +++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java @@ -121,7 +121,7 @@ public class WorkspacePageIndicator extends View implements Insettable, PageIndi mLinePaint.setAlpha(0); mLauncher = Launcher.getLauncher(context); - mLineHeight = res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_line_height); + mLineHeight = res.getDimensionPixelSize(R.dimen.workspace_page_indicator_line_height); boolean darkText = WallpaperColorInfo.INSTANCE.get(context).supportsDarkText(); mActiveAlpha = darkText ? BLACK_ALPHA : WHITE_ALPHA; diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index 9bac259efe..406e1b2180 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -184,6 +184,13 @@ public class PopupContainerWithArrow extends Arr return false; } + /** + * Returns true if we can show the container. + */ + public static boolean canShow(View icon, ItemInfo item) { + return icon instanceof BubbleTextView && ShortcutUtil.supportsShortcuts(item); + } + /** * Shows the notifications and deep shortcuts associated with {@param icon}. * @return the container if shown or null. @@ -196,7 +203,7 @@ public class PopupContainerWithArrow extends Arr return null; } ItemInfo item = (ItemInfo) icon.getTag(); - if (!ShortcutUtil.supportsShortcuts(item)) { + if (!canShow(icon, item)) { return null; } diff --git a/src/com/android/launcher3/states/HintState.java b/src/com/android/launcher3/states/HintState.java index 290dbb6baa..43f30f1e07 100644 --- a/src/com/android/launcher3/states/HintState.java +++ b/src/com/android/launcher3/states/HintState.java @@ -17,6 +17,7 @@ package com.android.launcher3.states; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; +import com.android.launcher3.LauncherStateManager; import com.android.launcher3.Workspace; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; @@ -50,8 +51,13 @@ public class HintState extends LauncherState { @Override public void onStateTransitionEnd(Launcher launcher) { - launcher.getStateManager().goToState(NORMAL); + LauncherStateManager stateManager = launcher.getStateManager(); Workspace workspace = launcher.getWorkspace(); - workspace.post(workspace::moveToDefaultScreen); + boolean willMoveScreens = workspace.getNextPage() != Workspace.DEFAULT_PAGE; + stateManager.goToState(NORMAL, true, willMoveScreens ? null + : launcher.getScrimView()::startDragHandleEducationAnim); + if (willMoveScreens) { + workspace.post(workspace::moveToDefaultScreen); + } } } diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java index d28fcf6884..8bb6a08a34 100644 --- a/src/com/android/launcher3/states/RotationHelper.java +++ b/src/com/android/launcher3/states/RotationHelper.java @@ -26,6 +26,7 @@ import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE; import static com.android.launcher3.config.FeatureFlags.FLAG_ENABLE_FIXED_ROTATION_TRANSFORM; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; +import android.app.Activity; import android.content.ContentResolver; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; @@ -36,12 +37,9 @@ import android.graphics.RectF; import android.provider.Settings; import android.view.MotionEvent; import android.view.Surface; -import android.view.WindowManager; -import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.UiThreadHelper; import java.util.ArrayList; @@ -77,7 +75,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { public static final int REQUEST_ROTATE = 1; public static final int REQUEST_LOCK = 2; - private final Launcher mLauncher; + private final Activity mActivity; private final SharedPreferences mSharedPrefs; private final SharedPreferences mFeatureFlagsPrefs; @@ -104,17 +102,16 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { // This is used to defer setting rotation flags until the activity is being created private boolean mInitialized; private boolean mDestroyed; - private boolean mRotationHasDifferentUI; private int mLastActivityFlags = -1; - public RotationHelper(Launcher launcher) { - mLauncher = launcher; + public RotationHelper(Activity activity) { + mActivity = activity; // On large devices we do not handle auto-rotate differently. - mIgnoreAutoRotateSettings = mLauncher.getResources().getBoolean(R.bool.allow_rotation); + mIgnoreAutoRotateSettings = mActivity.getResources().getBoolean(R.bool.allow_rotation); if (!mIgnoreAutoRotateSettings) { - mSharedPrefs = Utilities.getPrefs(mLauncher); + mSharedPrefs = Utilities.getPrefs(mActivity); mSharedPrefs.registerOnSharedPreferenceChangeListener(this); mAutoRotateEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, getAllowRotationDefaultValue()); @@ -122,8 +119,8 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { mSharedPrefs = null; } - mContentResolver = launcher.getContentResolver(); - mFeatureFlagsPrefs = Utilities.getFeatureFlagsPrefs(mLauncher); + mContentResolver = activity.getContentResolver(); + mFeatureFlagsPrefs = Utilities.getFeatureFlagsPrefs(mActivity); mFeatureFlagsPrefs.registerOnSharedPreferenceChangeListener(this); updateForcedRotation(true); } @@ -144,7 +141,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { mForcedRotation = isForcedRotation; } UI_HELPER_EXECUTOR.execute(() -> { - if (mLauncher.checkSelfPermission(WRITE_SECURE_SETTINGS) == PERMISSION_GRANTED) { + if (mActivity.checkSelfPermission(WRITE_SECURE_SETTINGS) == PERMISSION_GRANTED) { Settings.Global.putInt(mContentResolver, FIXED_ROTATION_TRANSFORM_SETTING_NAME, mForcedRotation ? 1 : 0); } @@ -165,29 +162,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { mForcedRotationChangedListeners.remove(listener); } - public void setRotationHadDifferentUI(boolean rotationHasDifferentUI) { - mRotationHasDifferentUI = rotationHasDifferentUI; - } - - public boolean homeScreenCanRotate() { - return mRotationHasDifferentUI || mIgnoreAutoRotateSettings || mAutoRotateEnabled - || mStateHandlerRequest != REQUEST_NONE - || mLauncher.getDeviceProfile().isMultiWindowMode; - } - - public void updateRotationAnimation() { - if (FeatureFlags.FAKE_LANDSCAPE_UI.get()) { - WindowManager.LayoutParams lp = mLauncher.getWindow().getAttributes(); - int oldAnim = lp.rotationAnimation; - lp.rotationAnimation = homeScreenCanRotate() - ? WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE - : WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; - if (oldAnim != lp.rotationAnimation) { - mLauncher.getWindow().setAttributes(lp); - } - } - } - @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) { if (FLAG_ENABLE_FIXED_ROTATION_TRANSFORM.equals(s)) { @@ -199,17 +173,13 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { mAutoRotateEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, getAllowRotationDefaultValue()); if (mAutoRotateEnabled != wasRotationEnabled) { - notifyChange(); - updateRotationAnimation(); - mLauncher.reapplyUi(); } } public void setStateHandlerRequest(int request) { if (mStateHandlerRequest != request) { mStateHandlerRequest = request; - updateRotationAnimation(); notifyChange(); } } @@ -231,7 +201,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { // Used by tests only. public void forceAllowRotationForTesting(boolean allowRotation) { mIgnoreAutoRotateSettings = - allowRotation || mLauncher.getResources().getBoolean(R.bool.allow_rotation); + allowRotation || mActivity.getResources().getBoolean(R.bool.allow_rotation); // TODO(b/150214193) Tests currently expect launcher to be able to be rotated // Modify tests for this new behavior mForcedRotation = !allowRotation; @@ -243,7 +213,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { if (!mInitialized) { mInitialized = true; notifyChange(); - updateRotationAnimation(); } } @@ -285,7 +254,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { } if (activityFlags != mLastActivityFlags) { mLastActivityFlags = activityFlags; - UiThreadHelper.setOrientationAsync(mLauncher, activityFlags); + UiThreadHelper.setOrientationAsync(mActivity, activityFlags); } } diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java index 4e49c6edf2..e786f07237 100644 --- a/src/com/android/launcher3/testing/TestInformationHandler.java +++ b/src/com/android/launcher3/testing/TestInformationHandler.java @@ -184,6 +184,10 @@ public class TestInformationHandler implements ResourceBasedOverride { mDeviceProfile.allAppsCellHeightPx); break; } + + case TestProtocol.REQUEST_MOCK_SENSOR_ROTATION: + TestProtocol.sDisableSensorRotation = true; + break; } return response; } diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java index 9f20df66ce..3181752883 100644 --- a/src/com/android/launcher3/testing/TestProtocol.java +++ b/src/com/android/launcher3/testing/TestProtocol.java @@ -92,6 +92,9 @@ public final class TestProtocol { public static final String REQUEST_OVERVIEW_ACTIONS_ENABLED = "overview-actions-enabled"; + public static boolean sDisableSensorRotation; + public static final String REQUEST_MOCK_SENSOR_ROTATION = "mock-sensor-rotation"; + public static final String PERMANENT_DIAG_TAG = "TaplTarget"; public static final String NO_BACKGROUND_TO_OVERVIEW_TAG = "b/138251824"; diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java index 1db65b9705..6715bc1a0b 100644 --- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java +++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java @@ -16,8 +16,11 @@ package com.android.launcher3.touch; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; +import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL; + import android.content.res.Resources; -import android.graphics.Matrix; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; @@ -32,13 +35,8 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherState.ScaleAndTranslation; import com.android.launcher3.PagedView; import com.android.launcher3.Utilities; -import com.android.launcher3.states.RotationHelper; import com.android.launcher3.util.OverScroller; -import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; -import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; -import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL; - public class LandscapePagedViewHandler implements PagedOrientationHandler { @Override @@ -67,12 +65,11 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { } @Override - public CurveProperties getCurveProperties(PagedView pagedView, Rect mInsets) { - int scroll = pagedView.getScrollY(); - final int halfPageSize = pagedView.getNormalChildHeight() / 2; - final int screenCenter = mInsets.top + pagedView.getPaddingTop() + scroll + halfPageSize; - final int halfScreenSize = pagedView.getMeasuredHeight() / 2; - return new CurveProperties(scroll, halfPageSize, screenCenter, halfScreenSize); + public void getCurveProperties(PagedView view, Rect mInsets, CurveProperties out) { + out.scroll = view.getScrollY(); + out.halfPageSize = view.getNormalChildHeight() / 2; + out.halfScreenSize = view.getMeasuredHeight() / 2; + out.screenCenter = mInsets.top + view.getPaddingTop() + out.scroll + out.halfPageSize; } @Override diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java index b4802cda90..24fa81590a 100644 --- a/src/com/android/launcher3/touch/PagedOrientationHandler.java +++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java @@ -83,7 +83,7 @@ public interface PagedOrientationHandler { void delegateScrollTo(PagedView pagedView, int primaryScroll); void delegateScrollBy(PagedView pagedView, int unboundedScroll, int x, int y); void scrollerStartScroll(OverScroller scroller, int newPosition); - CurveProperties getCurveProperties(PagedView pagedView, Rect insets); + void getCurveProperties(PagedView view, Rect mInsets, CurveProperties out); boolean isGoingUp(float displacement); /** @@ -92,18 +92,12 @@ public interface PagedOrientationHandler { */ void adjustFloatingIconStartVelocity(PointF velocity); - class CurveProperties { - public final int scroll; - public final int halfPageSize; - public final int screenCenter; - public final int halfScreenSize; - public CurveProperties(int scroll, int halfPageSize, int screenCenter, int halfScreenSize) { - this.scroll = scroll; - this.halfPageSize = halfPageSize; - this.screenCenter = screenCenter; - this.halfScreenSize = halfScreenSize; - } + class CurveProperties { + public int scroll; + public int halfPageSize; + public int screenCenter; + public int halfScreenSize; } class ChildBounds { diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java index 22eee49df6..6d903b3882 100644 --- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java +++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java @@ -16,8 +16,11 @@ package com.android.launcher3.touch; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; +import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL; + import android.content.res.Resources; -import android.graphics.Matrix; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; @@ -32,13 +35,8 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherState.ScaleAndTranslation; import com.android.launcher3.PagedView; import com.android.launcher3.Utilities; -import com.android.launcher3.states.RotationHelper; import com.android.launcher3.util.OverScroller; -import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; -import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; -import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL; - public class PortraitPagedViewHandler implements PagedOrientationHandler { @Override @@ -67,12 +65,11 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { } @Override - public CurveProperties getCurveProperties(PagedView pagedView, Rect mInsets) { - int scroll = pagedView.getScrollX(); - final int halfPageSize = pagedView.getNormalChildWidth() / 2; - final int screenCenter = mInsets.left + pagedView.getPaddingLeft() + scroll + halfPageSize; - final int halfScreenSize = pagedView.getMeasuredWidth() / 2; - return new CurveProperties(scroll, halfPageSize, screenCenter, halfScreenSize); + public void getCurveProperties(PagedView view, Rect mInsets, CurveProperties out) { + out.scroll = view.getScrollX(); + out.halfPageSize = view.getNormalChildWidth() / 2; + out.halfScreenSize = view.getMeasuredWidth() / 2; + out.screenCenter = mInsets.left + view.getPaddingLeft() + out.scroll + out.halfPageSize; } @Override diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java index 0331a86cd5..c9cdeffb9b 100644 --- a/src/com/android/launcher3/views/ActivityContext.java +++ b/src/com/android/launcher3/views/ActivityContext.java @@ -22,7 +22,6 @@ import android.view.View.AccessibilityDelegate; import com.android.launcher3.DeviceProfile; import com.android.launcher3.ItemInfo; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.dot.DotInfo; /** @@ -57,19 +56,6 @@ public interface ActivityContext { DeviceProfile getDeviceProfile(); - /** - * Device profile to be used by UI elements which are shown directly on top of the wallpaper - * and whose presentation is tied to the wallpaper (and physical device) and not the activity - * configuration. - */ - default DeviceProfile getWallpaperDeviceProfile() { - return getDeviceProfile(); - } - - default RotationMode getRotationMode() { - return RotationMode.NORMAL; - } - static ActivityContext lookupContext(Context context) { if (context instanceof ActivityContext) { return (ActivityContext) context; diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 3e2560f13c..ad8d69dfa7 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -92,8 +92,6 @@ public class FloatingIconView extends FrameLayout implements private ClipIconView mClipIconView; private @Nullable Drawable mBadge; - private float mRotation; - private View mOriginalIcon; private RectF mPositionOut; private Runnable mOnTargetChangeRunnable; @@ -194,18 +192,17 @@ public class FloatingIconView extends FrameLayout implements * @param positionOut Rect that will hold the size and position of v. */ private void matchPositionOf(Launcher launcher, View v, boolean isOpening, RectF positionOut) { - float rotation = getLocationBoundsForView(launcher, v, isOpening, positionOut); + getLocationBoundsForView(launcher, v, isOpening, positionOut); final InsettableFrameLayout.LayoutParams lp = new InsettableFrameLayout.LayoutParams( Math.round(positionOut.width()), Math.round(positionOut.height())); - updatePosition(rotation, positionOut, lp); + updatePosition(positionOut, lp); setLayoutParams(lp); mClipIconView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height)); } - private void updatePosition(float rotation, RectF pos, InsettableFrameLayout.LayoutParams lp) { - mRotation = rotation; + private void updatePosition(RectF pos, InsettableFrameLayout.LayoutParams lp) { mPositionOut.set(pos); lp.ignoreInsets = true; // Position the floating view exactly on top of the original @@ -228,7 +225,7 @@ public class FloatingIconView extends FrameLayout implements * - For DeepShortcutView, we return the bounds of the icon view. * - For BubbleTextView, we return the icon bounds. */ - private static float getLocationBoundsForView(Launcher launcher, View v, boolean isOpening, + private static void getLocationBoundsForView(Launcher launcher, View v, boolean isOpening, RectF outRect) { boolean ignoreTransform = !isOpening; if (v instanceof DeepShortcutView) { @@ -239,7 +236,7 @@ public class FloatingIconView extends FrameLayout implements ignoreTransform = false; } if (v == null) { - return 0; + return; } Rect iconBounds = new Rect(); @@ -253,15 +250,13 @@ public class FloatingIconView extends FrameLayout implements float[] points = new float[] {iconBounds.left, iconBounds.top, iconBounds.right, iconBounds.bottom}; - float[] rotation = new float[] {0}; Utilities.getDescendantCoordRelativeToAncestor(v, launcher.getDragLayer(), points, - false, ignoreTransform, rotation); + false, ignoreTransform); outRect.set( Math.min(points[0], points[2]), Math.min(points[1], points[3]), Math.max(points[0], points[2]), Math.max(points[1], points[3])); - return rotation[0]; } /** @@ -443,14 +438,10 @@ public class FloatingIconView extends FrameLayout implements @Override protected void dispatchDraw(Canvas canvas) { - int count = canvas.save(); - canvas.rotate(mRotation, - mFinalDrawableBounds.exactCenterX(), mFinalDrawableBounds.exactCenterY()); super.dispatchDraw(canvas); if (mBadge != null) { mBadge.draw(canvas); } - canvas.restoreToCount(count); } public void fastFinish() { @@ -487,11 +478,10 @@ public class FloatingIconView extends FrameLayout implements @Override public void onGlobalLayout() { if (mOriginalIcon.isAttachedToWindow() && mPositionOut != null) { - float rotation = getLocationBoundsForView(mLauncher, mOriginalIcon, mIsOpening, + getLocationBoundsForView(mLauncher, mOriginalIcon, mIsOpening, sTmpRectF); - if (rotation != mRotation || !sTmpRectF.equals(mPositionOut)) { - updatePosition(rotation, sTmpRectF, - (InsettableFrameLayout.LayoutParams) getLayoutParams()); + if (!sTmpRectF.equals(mPositionOut)) { + updatePosition(sTmpRectF, (InsettableFrameLayout.LayoutParams) getLayoutParams()); if (mOnTargetChangeRunnable != null) { mOnTargetChangeRunnable.run(); } diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java index 6d204f6c5b..39e1eac758 100644 --- a/src/com/android/launcher3/views/ScrimView.java +++ b/src/com/android/launcher3/views/ScrimView.java @@ -22,8 +22,10 @@ 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.ACCEL_DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL; +import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.anim.Interpolators.clampToProgress; import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; import android.animation.Animator; @@ -33,8 +35,10 @@ import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.RectEvaluator; import android.content.Context; +import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; @@ -98,6 +102,12 @@ public class ScrimView extends View implements Insettable, O private static final int SETTINGS = R.string.settings_button_text; private static final int ALPHA_CHANNEL_COUNT = 1; + private static final long DRAG_HANDLE_BOUNCE_DURATION_MS = 300; + // How much to delay before repeating the bounce. + private static final long DRAG_HANDLE_BOUNCE_DELAY_MS = 200; + // Repeat this many times (i.e. total number of bounces is 1 + this). + private static final int DRAG_HANDLE_BOUNCE_REPEAT_COUNT = 2; + private final Rect mTempRect = new Rect(); private final int[] mTempPos = new int[2]; @@ -115,10 +125,13 @@ public class ScrimView extends View implements Insettable, O protected int mEndFlatColor; protected int mEndFlatColorAlpha; - protected final int mDragHandleSize; + protected final Point mDragHandleSize; + private final int mDragHandleTouchSize; + private final int mDragHandlePaddingInVerticalBarLayout; protected float mDragHandleOffset; private final Rect mDragHandleBounds; private final RectF mHitRect = new RectF(); + private ObjectAnimator mDragHandleAnim; private final MultiValueAlpha mMultiValueAlpha; @@ -136,9 +149,13 @@ public class ScrimView extends View implements Insettable, O mMaxScrimAlpha = 0.7f; - mDragHandleSize = context.getResources() - .getDimensionPixelSize(R.dimen.vertical_drag_handle_size); - mDragHandleBounds = new Rect(0, 0, mDragHandleSize, mDragHandleSize); + Resources res = context.getResources(); + mDragHandleSize = new Point(res.getDimensionPixelSize(R.dimen.vertical_drag_handle_width), + res.getDimensionPixelSize(R.dimen.vertical_drag_handle_height)); + mDragHandleBounds = new Rect(0, 0, mDragHandleSize.x, mDragHandleSize.y); + mDragHandleTouchSize = res.getDimensionPixelSize(R.dimen.vertical_drag_handle_touch_size); + mDragHandlePaddingInVerticalBarLayout = context.getResources() + .getDimensionPixelSize(R.dimen.vertical_drag_handle_padding_in_vertical_bar_layout); mAccessibilityHelper = createAccessibilityHelper(); ViewCompat.setAccessibilityDelegate(this, mAccessibilityHelper); @@ -204,6 +221,7 @@ public class ScrimView extends View implements Insettable, O public void setProgress(float progress) { if (mProgress != progress) { mProgress = progress; + stopDragHandleEducationAnim(); updateColors(); updateDragHandleAlpha(); invalidate(); @@ -251,70 +269,103 @@ public class ScrimView extends View implements Insettable, O @Override public boolean onTouchEvent(MotionEvent event) { - boolean value = super.onTouchEvent(event); - if (!value && mDragHandle != null && event.getAction() == ACTION_DOWN - && mDragHandle.getAlpha() == 255 - && mHitRect.contains(event.getX(), event.getY())) { - - final Drawable drawable = mDragHandle; - mDragHandle = null; - - Rect bounds = new Rect(mDragHandleBounds); - bounds.offset(0, -(int) mDragHandleOffset); - drawable.setBounds(bounds); - - Rect topBounds = new Rect(bounds); - topBounds.offset(0, -bounds.height() / 2); - - Rect invalidateRegion = new Rect(bounds); - invalidateRegion.top = topBounds.top; - - Keyframe frameTop = Keyframe.ofObject(0.6f, topBounds); - frameTop.setInterpolator(DEACCEL); - Keyframe frameBot = Keyframe.ofObject(1, bounds); - frameBot.setInterpolator(ACCEL); - PropertyValuesHolder holder = PropertyValuesHolder .ofKeyframe("bounds", - Keyframe.ofObject(0, bounds), frameTop, frameBot); - holder.setEvaluator(new RectEvaluator()); - - ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(drawable, holder); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - getOverlay().remove(drawable); - updateDragHandleVisibility(drawable); + boolean superHandledTouch = super.onTouchEvent(event); + if (event.getAction() == ACTION_DOWN) { + if (!superHandledTouch && mHitRect.contains(event.getX(), event.getY())) { + if (startDragHandleEducationAnim()) { + return true; } - }); - anim.addUpdateListener((v) -> invalidate(invalidateRegion)); - getOverlay().add(drawable); - anim.start(); - return true; + } + stopDragHandleEducationAnim(); + } + return superHandledTouch; + } + + /** + * Animates the drag handle to demonstrate how to get to all apps. + * @return Whether the animation was started (false if drag handle is invisible). + */ + public boolean startDragHandleEducationAnim() { + stopDragHandleEducationAnim(); + + if (mDragHandle == null || mDragHandle.getAlpha() != 255) { + return false; + } + + final Drawable drawable = mDragHandle; + mDragHandle = null; + + Rect bounds = new Rect(mDragHandleBounds); + bounds.offset(0, -(int) mDragHandleOffset); + drawable.setBounds(bounds); + + Rect topBounds = new Rect(bounds); + topBounds.offset(0, -bounds.height()); + + Rect invalidateRegion = new Rect(bounds); + invalidateRegion.top = topBounds.top; + + final float progressToReachTop = 0.6f; + Keyframe frameTop = Keyframe.ofObject(progressToReachTop, topBounds); + frameTop.setInterpolator(DEACCEL); + Keyframe frameBot = Keyframe.ofObject(1, bounds); + frameBot.setInterpolator(ACCEL_DEACCEL); + PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("bounds", + Keyframe.ofObject(0, bounds), frameTop, frameBot); + holder.setEvaluator(new RectEvaluator()); + + mDragHandleAnim = ObjectAnimator.ofPropertyValuesHolder(drawable, holder); + long totalBounceDuration = DRAG_HANDLE_BOUNCE_DURATION_MS + DRAG_HANDLE_BOUNCE_DELAY_MS; + // The bounce finishes by this progress, the rest of the duration just delays next bounce. + float delayStartProgress = 1f - (float) DRAG_HANDLE_BOUNCE_DELAY_MS / totalBounceDuration; + mDragHandleAnim.addUpdateListener((v) -> invalidate(invalidateRegion)); + mDragHandleAnim.setDuration(totalBounceDuration); + mDragHandleAnim.setInterpolator(clampToProgress(LINEAR, 0, delayStartProgress)); + mDragHandleAnim.setRepeatCount(DRAG_HANDLE_BOUNCE_REPEAT_COUNT); + getOverlay().add(drawable); + + mDragHandleAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mDragHandleAnim = null; + getOverlay().remove(drawable); + updateDragHandleVisibility(drawable); + } + }); + mDragHandleAnim.start(); + return true; + } + + private void stopDragHandleEducationAnim() { + if (mDragHandleAnim != null) { + mDragHandleAnim.end(); } - return value; } protected void updateDragHandleBounds() { DeviceProfile grid = mLauncher.getDeviceProfile(); final int left; final int width = getMeasuredWidth(); - final int top = getMeasuredHeight() - mDragHandleSize - grid.getInsets().bottom; + final int top = getMeasuredHeight() - mDragHandleSize.y - grid.getInsets().bottom; final int topMargin; if (grid.isVerticalBarLayout()) { - topMargin = grid.workspacePadding.bottom; + topMargin = grid.workspacePadding.bottom + mDragHandlePaddingInVerticalBarLayout; if (grid.isSeascape()) { - left = width - grid.getInsets().right - mDragHandleSize; + left = width - grid.getInsets().right - mDragHandleSize.x + - mDragHandlePaddingInVerticalBarLayout; } else { - left = mDragHandleSize + grid.getInsets().left; + left = grid.getInsets().left + mDragHandlePaddingInVerticalBarLayout; } } else { - left = (width - mDragHandleSize) / 2; + left = Math.round((width - mDragHandleSize.x) / 2f); topMargin = grid.hotseatBarSizePx; } mDragHandleBounds.offsetTo(left, top - topMargin); mHitRect.set(mDragHandleBounds); - float inset = -mDragHandleSize / 2; - mHitRect.inset(inset, inset); + // Inset outwards to increase touch size. + mHitRect.inset((mDragHandleSize.x - mDragHandleTouchSize) / 2f, + (mDragHandleSize.y - mDragHandleTouchSize) / 2f); if (mDragHandle != null) { mDragHandle.setBounds(mDragHandleBounds); @@ -341,7 +392,7 @@ public class ScrimView extends View implements Insettable, O if (visible != wasVisible) { if (visible) { mDragHandle = recycle != null ? recycle : - mLauncher.getDrawable(R.drawable.drag_handle_indicator); + mLauncher.getDrawable(R.drawable.drag_handle_indicator_shadow); mDragHandle.setBounds(mDragHandleBounds); updateDragHandleAlpha(); @@ -397,7 +448,7 @@ public class ScrimView extends View implements Insettable, O @Override protected int getVirtualViewAt(float x, float y) { - return mDragHandleBounds.contains((int) x, (int) y) + return mHitRect.contains((int) x, (int) y) ? DRAG_HANDLE_ID : INVALID_ID; } @@ -470,7 +521,10 @@ public class ScrimView extends View implements Insettable, O } } - public int getDragHandleSize() { - return mDragHandleSize; + /** + * @return The top of this scrim view, or {@link Float#MAX_VALUE} if there's no distinct top. + */ + public float getVisualTop() { + return Float.MAX_VALUE; } } diff --git a/src/com/android/launcher3/views/Transposable.java b/src/com/android/launcher3/views/Transposable.java deleted file mode 100644 index 929c1aa9b3..0000000000 --- a/src/com/android/launcher3/views/Transposable.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * 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; - -import com.android.launcher3.graphics.RotationMode; - -/** - * Indicates that a view can be transposed. - */ -public interface Transposable { - - RotationMode getRotationMode(); -} diff --git a/src/com/android/launcher3/widget/WidgetsFullSheet.java b/src/com/android/launcher3/widget/WidgetsFullSheet.java index aaebedd2ce..b3e9734e9d 100644 --- a/src/com/android/launcher3/widget/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/WidgetsFullSheet.java @@ -98,6 +98,11 @@ public class WidgetsFullSheet extends BaseWidgetSheet onWidgetsBound(); } + @VisibleForTesting + public WidgetsRecyclerView getRecyclerView() { + return mRecyclerView; + } + @Override protected Pair getAccessibilityTarget() { return Pair.create(mRecyclerView, getContext().getString( diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java index c15557beb2..7ec62145e1 100644 --- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java @@ -19,11 +19,14 @@ package com.android.launcher3.widget; import android.content.Context; import android.graphics.Point; import android.util.AttributeSet; +import android.util.Log; import android.view.MotionEvent; import android.view.View; import com.android.launcher3.BaseRecyclerView; import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.testing.TestProtocol; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -155,18 +158,66 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch mScrollbar.isHitInParent(e.getX(), e.getY(), mFastScrollerOffset); } if (mTouchDownOnScroller) { - return mScrollbar.handleTouchEvent(e, mFastScrollerOffset); + final boolean result = mScrollbar.handleTouchEvent(e, mFastScrollerOffset); + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onInterceptTouchEvent 1 " + result); + } + return result; + } + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onInterceptTouchEvent 2 false"); } return false; } @Override public void onTouchEvent(RecyclerView rv, MotionEvent e) { + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "WidgetsRecyclerView.onTouchEvent"); + } if (mTouchDownOnScroller) { mScrollbar.handleTouchEvent(e, mFastScrollerOffset); } } @Override - public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { } + public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onRequestDisallowInterceptTouchEvent " + + disallowIntercept); + } + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + final boolean result = super.dispatchTouchEvent(ev); + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "WidgetsRecyclerView: state: " + + getScrollState() + + " can scroll: " + getLayoutManager().canScrollVertically() + + " result: " + result + + " layout suppressed: " + isLayoutSuppressed() + + " event: " + ev); + } + return result; + } + + @Override + public void stopNestedScroll() { + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "stopNestedScroll"); + } + super.stopNestedScroll(); + } + + @Override + public void setLayoutFrozen(boolean frozen) { + if (frozen != isLayoutSuppressed()) { + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "setLayoutFrozen " + frozen + + " @ " + android.util.Log.getStackTraceString(new Throwable())); + } + } + super.setLayoutFrozen(frozen); + } } \ No newline at end of file diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 7cd656e811..873f1cbe6d 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -162,6 +162,7 @@ public abstract class AbstractLauncherUiTest { mLauncher.enableDebugTracing(); // Avoid double-reporting of Launcher crashes. mLauncher.setOnLauncherCrashed(() -> mLauncherPid = 0); + mLauncher.disableSensorRotation(); } protected final LauncherActivityRule mActivityMonitor = new LauncherActivityRule(); @@ -277,6 +278,7 @@ public abstract class AbstractLauncherUiTest { clearPackageData(mDevice.getLauncherPackageName()); mLauncher.enableDebugTracing(); mLauncherPid = mLauncher.getPid(); + mLauncher.disableSensorRotation(); } } diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java index a3c70ecbe9..001a88fdf6 100644 --- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java @@ -275,8 +275,10 @@ public class BindWidgetTest extends AbstractLauncherUiTest { } private void verifyPendingWidgetPresent() { + final Widget widget = mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT); + if (widget == null) mLauncher.dumpViewHierarchy(); // b/152645831 assertTrue("Pending widget is not present", - mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT) != null); + widget != null); } /** diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java index 4a2d6995f4..808be6644b 100644 --- a/tests/tapl/com/android/launcher3/tapl/AllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java @@ -43,7 +43,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { AllApps(LauncherInstrumentation launcher) { super(launcher); final UiObject2 allAppsContainer = verifyActiveContainer(); - mHeight = allAppsContainer.getVisibleBounds().height(); + mHeight = mLauncher.getVisibleBounds(allAppsContainer).height(); final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer, "apps_list_view"); // Wait for the recycler to populate. @@ -66,7 +66,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { LauncherInstrumentation.log("hasClickableIcon: icon not visible"); return false; } - final Rect iconBounds = icon.getVisibleBounds(); + final Rect iconBounds = mLauncher.getVisibleBounds(icon); LauncherInstrumentation.log("hasClickableIcon: icon bounds: " + iconBounds); if (iconBounds.height() < mIconHeight / 2) { LauncherInstrumentation.log("hasClickableIcon: icon has insufficient height"); @@ -86,7 +86,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { private boolean iconCenterInSearchBox(UiObject2 allAppsContainer, UiObject2 icon) { final Point iconCenter = icon.getVisibleCenter(); - return getSearchBox(allAppsContainer).getVisibleBounds().contains( + return mLauncher.getVisibleBounds(getSearchBox(allAppsContainer)).contains( iconCenter.x, iconCenter.y); } @@ -125,11 +125,11 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { mLauncher.getObjectsInContainer(allAppsContainer, "icon") .stream() .filter(icon -> - icon.getVisibleBounds().bottom + mLauncher.getVisibleBounds(icon).bottom <= displayBottom) .collect(Collectors.toList()), - searchBox.getVisibleBounds().bottom - - allAppsContainer.getVisibleBounds().top); + mLauncher.getVisibleBounds(searchBox).bottom + - mLauncher.getVisibleBounds(allAppsContainer).top); final int newScroll = getAllAppsScroll(); mLauncher.assertTrue( "Scrolled in a wrong direction in AllApps: from " + scroll + " to " @@ -166,7 +166,8 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { final UiObject2 searchBox = getSearchBox(allAppsContainer); int attempts = 0; - final Rect margins = new Rect(0, searchBox.getVisibleBounds().bottom + 1, 0, 5); + final Rect margins = + new Rect(0, mLauncher.getVisibleBounds(searchBox).bottom + 1, 0, 5); for (int scroll = getAllAppsScroll(); scroll != 0; diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java index a769acfc35..69afcc4e44 100644 --- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java +++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java @@ -117,8 +117,8 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { // part) one in the center, and parts of its right and left siblings. Find the // main task view by its width. final UiObject2 widestTask = Collections.max(taskViews, - (t1, t2) -> Integer.compare(t1.getVisibleBounds().width(), - t2.getVisibleBounds().width())); + (t1, t2) -> Integer.compare(mLauncher.getVisibleBounds(t1).width(), + mLauncher.getVisibleBounds(t2).width())); return new OverviewTask(mLauncher, widestTask, this); } diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java index 2177032e6e..2922acf73c 100644 --- a/tests/tapl/com/android/launcher3/tapl/Launchable.java +++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java @@ -57,7 +57,7 @@ abstract class Launchable { private Background launch(BySelector selector) { LauncherInstrumentation.log("Launchable.launch before click " + - mObject.getVisibleCenter() + " in " + mObject.getVisibleBounds()); + mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject)); mLauncher.executeAndWaitForEvent( () -> mLauncher.clickLauncherObject(mObject), diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 3ddc0d2752..975fe9c1e7 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -977,7 +977,7 @@ public final class LauncherInstrumentation { int getBottomGestureMarginInContainer(UiObject2 container) { final int bottomGestureStartOnScreen = getRealDisplaySize().y - getBottomGestureSize(); - return container.getVisibleBounds().bottom - bottomGestureStartOnScreen; + return getVisibleBounds(container).bottom - bottomGestureStartOnScreen; } void clickLauncherObject(UiObject2 object) { @@ -995,10 +995,10 @@ public final class LauncherInstrumentation { Collection items, int topPaddingInContainer) { final UiObject2 lowestItem = Collections.max(items, (i1, i2) -> - Integer.compare(i1.getVisibleBounds().top, i2.getVisibleBounds().top)); + Integer.compare(getVisibleBounds(i1).top, getVisibleBounds(i2).top)); - final int itemRowCurrentTopOnScreen = lowestItem.getVisibleBounds().top; - final Rect containerRect = container.getVisibleBounds(); + final int itemRowCurrentTopOnScreen = getVisibleBounds(lowestItem).top; + final Rect containerRect = getVisibleBounds(container); final int itemRowNewTopOnScreen = containerRect.top + topPaddingInContainer; final int distance = itemRowCurrentTopOnScreen - itemRowNewTopOnScreen + getTouchSlop(); @@ -1017,7 +1017,7 @@ public final class LauncherInstrumentation { void scroll( UiObject2 container, Direction direction, Rect margins, int steps, boolean slowDown) { - final Rect rect = container.getVisibleBounds(); + final Rect rect = getVisibleBounds(container); if (margins != null) { rect.left += margins.left; rect.top += margins.top; @@ -1263,6 +1263,10 @@ public final class LauncherInstrumentation { TestProtocol.TEST_INFO_RESPONSE_FIELD); } + public void disableSensorRotation() { + getTestInfo(TestProtocol.REQUEST_MOCK_SENSOR_ROTATION); + } + public void disableDebugTracing() { getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING); } @@ -1330,4 +1334,13 @@ public final class LauncherInstrumentation { void expectEvent(String sequence, Pattern expected) { if (sCheckingEvents) sEventChecker.expectPattern(sequence, expected); } + + Rect getVisibleBounds(UiObject2 object) { + try { + return object.getVisibleBounds(); + } catch (Throwable t) { + fail(t.toString()); + return null; + } + } } diff --git a/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenuItem.java b/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenuItem.java index b8e6c0edb3..63a97f4ec4 100644 --- a/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenuItem.java +++ b/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenuItem.java @@ -41,7 +41,7 @@ public class OptionsPopupMenuItem { public void launch(@NonNull String expectedPackageName) { try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) { LauncherInstrumentation.log("OptionsPopupMenuItem before click " - + mObject.getVisibleCenter() + " in " + mObject.getVisibleBounds()); + + mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject)); mLauncher.clickLauncherObject(mObject); if (!Build.MODEL.contains("Cuttlefish") || Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q && diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java index 5c51782826..fae5f19126 100644 --- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java +++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java @@ -56,7 +56,7 @@ public final class OverviewTask { "want to dismiss a task")) { verifyActiveContainer(); // Dismiss the task via flinging it up. - final Rect taskBounds = mTask.getVisibleBounds(); + final Rect taskBounds = mLauncher.getVisibleBounds(mTask); final int centerX = taskBounds.centerX(); final int centerY = taskBounds.centerY(); mLauncher.linearGesture(centerX, centerY, centerX, 0, 10, false, diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java index a14d2f0501..5be57c6c0b 100644 --- a/tests/tapl/com/android/launcher3/tapl/Widgets.java +++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java @@ -73,7 +73,7 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer { mLauncher.scroll( widgetsContainer, Direction.UP, - new Rect(0, 0, widgetsContainer.getVisibleBounds().width(), 0), + new Rect(0, 0, mLauncher.getVisibleBounds(widgetsContainer).width(), 0), FLING_STEPS, false); try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) { verifyActiveContainer(); @@ -111,19 +111,19 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer { int maxWidth = 0; for (UiObject2 sibling : widget.getParent().getChildren()) { - maxWidth = Math.max(sibling.getVisibleBounds().width(), maxWidth); + maxWidth = Math.max(mLauncher.getVisibleBounds(sibling).width(), maxWidth); } - int visibleDelta = maxWidth - widget.getVisibleBounds().width(); + int visibleDelta = maxWidth - mLauncher.getVisibleBounds(widget).width(); if (visibleDelta > 0) { - Rect parentBounds = cell.getVisibleBounds(); + Rect parentBounds = mLauncher.getVisibleBounds(cell); mLauncher.linearGesture(parentBounds.centerX() + visibleDelta + mLauncher.getTouchSlop(), parentBounds.centerY(), parentBounds.centerX(), parentBounds.centerY(), 10, true, GestureScope.INSIDE); } - if (widget.getVisibleBounds().bottom + if (mLauncher.getVisibleBounds(widget).bottom <= displaySize.y - mLauncher.getBottomGestureSize()) { return new Widget(mLauncher, widget); } diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index 86aafc21f4..b3b5e3215a 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -177,7 +177,7 @@ public final class Workspace extends Home { mLauncher, getHotseatAppIcon("Chrome"), new Point(mLauncher.getDevice().getDisplayWidth(), - workspace.getVisibleBounds().centerY()), + mLauncher.getVisibleBounds(workspace).centerY()), "deep_shortcuts_container", false, () -> mLauncher.expectEvent(