| /* |
| * Copyright (C) 2024 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.quickstep.orientation |
| |
| import android.graphics.Point |
| 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.View |
| import android.widget.FrameLayout |
| import android.widget.LinearLayout |
| import com.android.launcher3.DeviceProfile |
| import com.android.launcher3.logger.LauncherAtom |
| import com.android.launcher3.touch.PagedOrientationHandler |
| import com.android.launcher3.touch.PagedOrientationHandler.Float2DAction |
| import com.android.launcher3.touch.PagedOrientationHandler.Int2DAction |
| import com.android.launcher3.touch.SingleAxisSwipeDetector |
| import com.android.launcher3.util.SplitConfigurationOptions |
| import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption |
| import com.android.launcher3.util.SplitConfigurationOptions.StagePosition |
| import com.android.quickstep.views.IconAppChipView |
| |
| /** |
| * Abstraction layer to separate horizontal and vertical specific implementations for |
| * [com.android.quickstep.views.RecentsView]. Majority of these implementations are (should be) as |
| * simple as choosing the correct X and Y analogous methods. |
| */ |
| interface RecentsPagedOrientationHandler : PagedOrientationHandler { |
| fun <T> setSecondary(target: T, action: Float2DAction<T>, param: Float) |
| |
| operator fun <T> set(target: T, action: Int2DAction<T>, primaryParam: Int, secondaryParam: Int) |
| |
| fun getPrimarySize(view: View): Int |
| |
| fun getPrimarySize(rect: RectF): Float |
| |
| val secondaryTranslationDirectionFactor: Int |
| |
| val degreesRotated: Float |
| |
| val rotation: Int |
| |
| val isLayoutNaturalToLauncher: Boolean |
| |
| fun <T> getPrimaryValue(x: T, y: T): T |
| |
| fun <T> getSecondaryValue(x: T, y: T): T |
| |
| fun setPrimaryScale(view: View, scale: Float) |
| |
| fun setSecondaryScale(view: View, scale: Float) |
| |
| fun getStart(rect: RectF): Float |
| |
| fun getEnd(rect: RectF): Float |
| |
| /** Rotate the provided insets to portrait perspective. */ |
| fun rotateInsets(insets: Rect, outInsets: Rect) |
| |
| fun getClearAllSidePadding(view: View, isRtl: Boolean): Int |
| |
| fun getSecondaryDimension(view: View): Int |
| |
| val primaryViewTranslate: FloatProperty<View> |
| val secondaryViewTranslate: FloatProperty<View> |
| |
| fun getSplitTranslationDirectionFactor( |
| @StagePosition stagePosition: Int, |
| deviceProfile: DeviceProfile |
| ): Int |
| |
| fun <T> getSplitSelectTaskOffset( |
| primary: FloatProperty<T>, |
| secondary: FloatProperty<T>, |
| deviceProfile: DeviceProfile |
| ): Pair<FloatProperty<T>, FloatProperty<T>> |
| |
| fun getDistanceToBottomOfRect(dp: DeviceProfile, rect: Rect): Int |
| |
| fun getSplitPositionOptions(dp: DeviceProfile): List<SplitPositionOption> |
| |
| /** @param placeholderHeight height of placeholder view in portrait, width in landscape */ |
| fun getInitialSplitPlaceholderBounds( |
| placeholderHeight: Int, |
| placeholderInset: Int, |
| dp: DeviceProfile, |
| @StagePosition stagePosition: Int, |
| out: Rect |
| ) |
| |
| /** |
| * 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. |
| */ |
| fun updateSplitIconParams( |
| out: View, |
| onScreenRectCenterX: Float, |
| onScreenRectCenterY: Float, |
| fullscreenScaleX: Float, |
| fullscreenScaleY: Float, |
| drawableWidth: Int, |
| drawableHeight: Int, |
| dp: DeviceProfile, |
| @StagePosition stagePosition: Int |
| ) |
| |
| /** |
| * 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. |
| */ |
| fun setSplitInstructionsParams( |
| out: View, |
| dp: DeviceProfile, |
| splitInstructionsHeight: Int, |
| splitInstructionsWidth: Int |
| ) |
| |
| /** |
| * @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} |
| */ |
| fun getFinalSplitPlaceholderBounds( |
| splitDividerSize: Int, |
| dp: DeviceProfile, |
| @StagePosition stagePosition: Int, |
| out1: Rect, |
| out2: Rect |
| ) |
| |
| fun getDefaultSplitPosition(deviceProfile: DeviceProfile): Int |
| |
| /** |
| * @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 |
| */ |
| fun setSplitTaskSwipeRect( |
| dp: DeviceProfile, |
| outRect: Rect, |
| splitInfo: SplitConfigurationOptions.SplitBounds, |
| @StagePosition desiredStagePosition: Int |
| ) |
| |
| fun measureGroupedTaskViewThumbnailBounds( |
| primarySnapshot: View, |
| secondarySnapshot: View, |
| parentWidth: Int, |
| parentHeight: Int, |
| splitBoundsConfig: SplitConfigurationOptions.SplitBounds, |
| dp: DeviceProfile, |
| isRtl: Boolean, |
| inSplitSelection: Boolean |
| ) |
| |
| /** |
| * Creates two Points representing the dimensions of the two tasks in a GroupedTaskView |
| * |
| * @return first -> primary task snapshot, second -> secondary task snapshot. x -> width, y -> |
| * height |
| */ |
| fun getGroupedTaskViewSizes( |
| dp: DeviceProfile, |
| splitBoundsConfig: SplitConfigurationOptions.SplitBounds, |
| parentWidth: Int, |
| parentHeight: Int |
| ): Pair<Point, Point> |
| |
| // Overview TaskMenuView methods |
| /** Sets layout params on a task's app icon. Only use this when app chip is disabled. */ |
| fun setTaskIconParams( |
| iconParams: FrameLayout.LayoutParams, |
| taskIconMargin: Int, |
| taskIconHeight: Int, |
| thumbnailTopMargin: Int, |
| isRtl: Boolean |
| ) |
| |
| /** |
| * Sets layout params on the children of an app chip. Only use this when app chip is enabled. |
| */ |
| fun setIconAppChipChildrenParams( |
| iconParams: FrameLayout.LayoutParams, |
| chipChildMarginStart: Int |
| ) |
| |
| fun setIconAppChipMenuParams( |
| iconAppChipView: IconAppChipView, |
| iconMenuParams: FrameLayout.LayoutParams, |
| iconMenuMargin: Int, |
| thumbnailTopMargin: Int |
| ) |
| |
| fun setSplitIconParams( |
| primaryIconView: View, |
| secondaryIconView: View, |
| taskIconHeight: Int, |
| primarySnapshotWidth: Int, |
| primarySnapshotHeight: Int, |
| groupedTaskViewHeight: Int, |
| groupedTaskViewWidth: Int, |
| isRtl: Boolean, |
| deviceProfile: DeviceProfile, |
| splitConfig: SplitConfigurationOptions.SplitBounds, |
| inSplitSelection: Boolean |
| ) |
| |
| /* |
| * 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. |
| */ |
| fun getTaskMenuX( |
| x: Float, |
| thumbnailView: View, |
| deviceProfile: DeviceProfile, |
| taskInsetMargin: Float, |
| taskViewIcon: View |
| ): Float |
| |
| fun getTaskMenuY( |
| y: Float, |
| thumbnailView: View, |
| stagePosition: Int, |
| taskMenuView: View, |
| taskInsetMargin: Float, |
| taskViewIcon: View |
| ): Float |
| |
| fun getTaskMenuWidth( |
| thumbnailView: View, |
| deviceProfile: DeviceProfile, |
| @StagePosition stagePosition: Int |
| ): Int |
| |
| fun getTaskMenuHeight( |
| taskInsetMargin: Float, |
| deviceProfile: DeviceProfile, |
| taskMenuX: Float, |
| taskMenuY: Float |
| ): Int |
| |
| /** |
| * Sets linear layout orientation for [com.android.launcher3.popup.SystemShortcut] items inside |
| * task menu view. |
| */ |
| fun setTaskOptionsMenuLayoutOrientation( |
| deviceProfile: DeviceProfile, |
| taskMenuLayout: LinearLayout, |
| dividerSpacing: Int, |
| dividerDrawable: ShapeDrawable |
| ) |
| |
| /** |
| * Sets layout param attributes for [com.android.launcher3.popup.SystemShortcut] child views |
| * inside task menu view. |
| */ |
| fun setLayoutParamsForTaskMenuOptionItem( |
| lp: LinearLayout.LayoutParams, |
| viewGroup: LinearLayout, |
| deviceProfile: DeviceProfile |
| ) |
| |
| /** Layout a Digital Wellbeing Banner on its parent. TaskView. */ |
| fun updateDwbBannerLayout( |
| taskViewWidth: Int, |
| taskViewHeight: Int, |
| isGroupedTaskView: Boolean, |
| deviceProfile: DeviceProfile, |
| snapshotViewWidth: Int, |
| snapshotViewHeight: Int, |
| banner: View |
| ) |
| |
| /** |
| * Calculates the translations where a Digital Wellbeing Banner should be apply on its parent |
| * TaskView. |
| * |
| * @return A Pair of Floats representing the proper x and y translations. |
| */ |
| fun getDwbBannerTranslations( |
| taskViewWidth: Int, |
| taskViewHeight: Int, |
| splitBounds: SplitConfigurationOptions.SplitBounds?, |
| deviceProfile: DeviceProfile, |
| thumbnailViews: Array<View>, |
| desiredTaskId: Int, |
| banner: View |
| ): Pair<Float, Float> |
| |
| // The following are only used by TaskViewTouchHandler. |
| |
| /** @return Either VERTICAL or HORIZONTAL. */ |
| val upDownSwipeDirection: SingleAxisSwipeDetector.Direction |
| |
| /** @return Given [.getUpDownSwipeDirection], whether POSITIVE or NEGATIVE is up. */ |
| fun getUpDirection(isRtl: Boolean): Int |
| |
| /** @return Whether the displacement is going towards the top of the screen. */ |
| fun isGoingUp(displacement: Float, isRtl: Boolean): Boolean |
| |
| /** @return Either 1 or -1, a factor to multiply by so the animation goes the correct way. */ |
| fun getTaskDragDisplacementFactor(isRtl: Boolean): Int |
| |
| /** |
| * Maps the velocity from the coordinate plane of the foreground app to that of Launcher's |
| * (which now will always be portrait) |
| */ |
| fun adjustFloatingIconStartVelocity(velocity: PointF) |
| |
| /** |
| * Ensures that outStartRect left bound is within the DeviceProfile's visual boundaries |
| * |
| * @param outStartRect The start rect that will directly be modified |
| */ |
| fun fixBoundsForHomeAnimStartRect(outStartRect: RectF, 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. |
| */ |
| fun getFloatingTaskOffscreenTranslationTarget( |
| floatingTask: View, |
| onScreenRect: RectF, |
| @StagePosition stagePosition: Int, |
| dp: DeviceProfile |
| ): Float |
| |
| /** |
| * 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. |
| */ |
| fun setFloatingTaskPrimaryTranslation(floatingTask: View, translation: Float, dp: DeviceProfile) |
| |
| /** |
| * 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. |
| */ |
| fun getFloatingTaskPrimaryTranslation(floatingTask: View, dp: DeviceProfile): Float |
| |
| fun getHandlerTypeForLogging(): LauncherAtom.TaskSwitcherContainer.OrientationHandler |
| |
| companion object { |
| @JvmField val PORTRAIT: RecentsPagedOrientationHandler = PortraitPagedViewHandler() |
| @JvmField val LANDSCAPE: RecentsPagedOrientationHandler = LandscapePagedViewHandler() |
| @JvmField val SEASCAPE: RecentsPagedOrientationHandler = SeascapePagedViewHandler() |
| } |
| } |