/* * 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.touch; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.ShapeDrawable; import android.util.FloatProperty; import android.util.Pair; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.widget.FrameLayout; import android.widget.LinearLayout; import com.android.launcher3.DeviceProfile; import com.android.launcher3.util.SplitConfigurationOptions; import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds; import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; import com.android.launcher3.util.SplitConfigurationOptions.StagePosition; import java.util.List; /** * Abstraction layer to separate horizontal and vertical specific implementations * for {@link com.android.launcher3.PagedView}. Majority of these implementations are (should be) as * simple as choosing the correct X and Y analogous methods. */ public interface PagedOrientationHandler { PagedOrientationHandler PORTRAIT = new PortraitPagedViewHandler(); PagedOrientationHandler LANDSCAPE = new LandscapePagedViewHandler(); PagedOrientationHandler SEASCAPE = new SeascapePagedViewHandler(); interface Int2DAction { void call(T target, int x, int y); } interface Float2DAction { void call(T target, float x, float y); } Int2DAction VIEW_SCROLL_BY = View::scrollBy; Int2DAction VIEW_SCROLL_TO = View::scrollTo; Float2DAction CANVAS_TRANSLATE = Canvas::translate; Float2DAction MATRIX_POST_TRANSLATE = Matrix::postTranslate; void setPrimary(T target, Int2DAction action, int param); void setPrimary(T target, Float2DAction action, float param); void setSecondary(T target, Float2DAction action, float param); void set(T target, Int2DAction action, int primaryParam, int secondaryParam); float getPrimaryDirection(MotionEvent event, int pointerIndex); float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId); int getMeasuredSize(View view); int getPrimarySize(View view); float getPrimarySize(RectF rect); float getStart(RectF rect); float getEnd(RectF rect); int getClearAllSidePadding(View view, boolean isRtl); int getSecondaryDimension(View view); FloatProperty getPrimaryViewTranslate(); FloatProperty getSecondaryViewTranslate(); int getPrimaryScroll(View view); float getPrimaryScale(View view); int getChildStart(View view); int getCenterForPage(View view, Rect insets); int getScrollOffsetStart(View view, Rect insets); int getScrollOffsetEnd(View view, Rect insets); int getSecondaryTranslationDirectionFactor(); int getSplitTranslationDirectionFactor(@StagePosition int stagePosition, DeviceProfile deviceProfile); ChildBounds getChildBounds(View child, int childStart, int pageCenter, boolean layoutChild); void setMaxScroll(AccessibilityEvent event, int maxScroll); boolean getRecentsRtlSetting(Resources resources); float getDegreesRotated(); int getRotation(); void setPrimaryScale(View view, float scale); void setSecondaryScale(View view, float scale); T getPrimaryValue(T x, T y); T getSecondaryValue(T x, T y); int getPrimaryValue(int x, int y); int getSecondaryValue(int x, int y); float getPrimaryValue(float x, float y); float getSecondaryValue(float x, float y); boolean isLayoutNaturalToLauncher(); Pair getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary, DeviceProfile deviceProfile); int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect); List getSplitPositionOptions(DeviceProfile dp); /** * @param placeholderHeight height of placeholder view in portrait, width in landscape */ void getInitialSplitPlaceholderBounds(int placeholderHeight, int placeholderInset, DeviceProfile dp, @StagePosition int stagePosition, Rect out); /** * Centers an icon in the split staging area, accounting for insets. * @param out The icon that needs to be centered. * @param onScreenRectCenterX The x-center of the on-screen staging area (most of the Rect is * offscreen). * @param onScreenRectCenterY The y-center of the on-screen staging area (most of the Rect is * offscreen). * @param fullscreenScaleX A x-scaling factor used to convert coordinates back into pixels. * @param fullscreenScaleY A y-scaling factor used to convert coordinates back into pixels. * @param drawableWidth The icon's drawable (final) width. * @param drawableHeight The icon's drawable (final) height. * @param dp The device profile, used to report rotation and hardware insets. * @param stagePosition 0 if the staging area is pinned to top/left, 1 for bottom/right. */ void updateSplitIconParams(View out, float onScreenRectCenterX, float onScreenRectCenterY, float fullscreenScaleX, float fullscreenScaleY, int drawableWidth, int drawableHeight, DeviceProfile dp, @StagePosition int stagePosition); /** * Sets positioning and rotation for a SplitInstructionsView. * @param out The SplitInstructionsView that needs to be positioned. * @param dp The device profile, used to report rotation and device type. * @param splitInstructionsHeight The SplitInstructionView's height. * @param splitInstructionsWidth The SplitInstructionView's width. */ void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight, int splitInstructionsWidth); /** * @param splitDividerSize height of split screen drag handle in portrait, width in landscape * @param stagePosition the split position option (top/left, bottom/right) of the first * task selected for entering split * @param out1 the bounds for where the first selected app will be * @param out2 the bounds for where the second selected app will be, complimentary to * {@param out1} based on {@param initialSplitOption} */ void getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp, @StagePosition int stagePosition, Rect out1, Rect out2); int getDefaultSplitPosition(DeviceProfile deviceProfile); /** * @param outRect This is expected to be the rect that has the dimensions for a non-split, * fullscreen task in overview. This will directly be modified. * @param desiredStagePosition Which stage position (topLeft/rightBottom) we want to resize * outRect for */ void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect, SplitBounds splitInfo, @SplitConfigurationOptions.StagePosition int desiredStagePosition); void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot, int parentWidth, int parentHeight, SplitBounds splitBoundsConfig, DeviceProfile dp, boolean isRtl); // Overview TaskMenuView methods void setTaskIconParams(FrameLayout.LayoutParams iconParams, int taskIconMargin, int taskIconHeight, int thumbnailTopMargin, boolean isRtl); void setSplitIconParams(View primaryIconView, View secondaryIconView, int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight, int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl, DeviceProfile deviceProfile, SplitBounds splitConfig); /* * The following two methods try to center the TaskMenuView in landscape by finding the center * of the thumbnail view and then subtracting half of the taskMenu width. In this case, the * taskMenu width is the same size as the thumbnail width (what got set below in * getTaskMenuWidth()), so we directly use that in the calculations. */ float getTaskMenuX(float x, View thumbnailView, DeviceProfile deviceProfile, float taskInsetMargin); float getTaskMenuY(float y, View thumbnailView, int stagePosition, View taskMenuView, float taskInsetMargin); int getTaskMenuWidth(View thumbnailView, DeviceProfile deviceProfile, @StagePosition int stagePosition); /** * Sets linear layout orientation for {@link com.android.launcher3.popup.SystemShortcut} items * inside task menu view. */ void setTaskOptionsMenuLayoutOrientation(DeviceProfile deviceProfile, LinearLayout taskMenuLayout, int dividerSpacing, ShapeDrawable dividerDrawable); /** * Sets layout param attributes for {@link com.android.launcher3.popup.SystemShortcut} child * views inside task menu view. */ void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp, LinearLayout viewGroup, DeviceProfile deviceProfile); /** * Calculates the position where a Digital Wellbeing Banner should be placed on its parent * TaskView. * @return A Pair of Floats representing the proper x and y translations. */ Pair getDwbLayoutTranslations(int taskViewWidth, int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile, View[] thumbnailViews, int desiredTaskId, View banner); // The following are only used by TaskViewTouchHandler. /** @return Either VERTICAL or HORIZONTAL. */ SingleAxisSwipeDetector.Direction getUpDownSwipeDirection(); /** @return Given {@link #getUpDownSwipeDirection()}, whether POSITIVE or NEGATIVE is up. */ int getUpDirection(boolean isRtl); /** @return Whether the displacement is going towards the top of the screen. */ boolean isGoingUp(float displacement, boolean isRtl); /** @return Either 1 or -1, a factor to multiply by so the animation goes the correct way. */ int getTaskDragDisplacementFactor(boolean isRtl); /** * Maps the velocity from the coordinate plane of the foreground app to that * of Launcher's (which now will always be portrait) */ void adjustFloatingIconStartVelocity(PointF velocity); /** * Ensures that outStartRect left bound is within the DeviceProfile's visual boundaries * @param outStartRect The start rect that will directly be modified */ void fixBoundsForHomeAnimStartRect(RectF outStartRect, DeviceProfile deviceProfile); /** * Determine the target translation for animating the FloatingTaskView out. This value could * either be an x-coordinate or a y-coordinate, depending on which way the FloatingTaskView was * docked. * * @param floatingTask The FloatingTaskView. * @param onScreenRect The current on-screen dimensions of the FloatingTaskView. * @param stagePosition STAGE_POSITION_TOP_OR_LEFT or STAGE_POSITION_BOTTOM_OR_RIGHT. * @param dp The device profile. * @return A float. When an animation translates the FloatingTaskView to this position, it will * appear to tuck away off the edge of the screen. */ float getFloatingTaskOffscreenTranslationTarget(View floatingTask, RectF onScreenRect, @StagePosition int stagePosition, DeviceProfile dp); /** * Sets the translation of a FloatingTaskView along its "slide-in/slide-out" axis (could be * either x or y), depending on how the view is oriented. * * @param floatingTask The FloatingTaskView to be translated. * @param translation The target translation value. * @param dp The current device profile. */ void setFloatingTaskPrimaryTranslation(View floatingTask, float translation, DeviceProfile dp); /** * Gets the translation of a FloatingTaskView along its "slide-in/slide-out" axis (could be * either x or y), depending on how the view is oriented. * * @param floatingTask The FloatingTaskView in question. * @param dp The current device profile. * @return The current translation value. */ Float getFloatingTaskPrimaryTranslation(View floatingTask, DeviceProfile dp); class ChildBounds { public final int primaryDimension; public final int secondaryDimension; public final int childPrimaryEnd; public final int childSecondaryEnd; ChildBounds(int primaryDimension, int secondaryDimension, int childPrimaryEnd, int childSecondaryEnd) { this.primaryDimension = primaryDimension; this.secondaryDimension = secondaryDimension; this.childPrimaryEnd = childPrimaryEnd; this.childSecondaryEnd = childSecondaryEnd; } } }