1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.launcher3.touch; 18 19 import static android.widget.ListPopupWindow.WRAP_CONTENT; 20 21 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; 22 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; 23 import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL; 24 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT; 25 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT; 26 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN; 27 28 import android.content.res.Resources; 29 import android.graphics.PointF; 30 import android.graphics.Rect; 31 import android.graphics.RectF; 32 import android.graphics.drawable.ShapeDrawable; 33 import android.util.FloatProperty; 34 import android.view.MotionEvent; 35 import android.view.Surface; 36 import android.view.VelocityTracker; 37 import android.view.View; 38 import android.view.accessibility.AccessibilityEvent; 39 import android.widget.LinearLayout; 40 41 import com.android.launcher3.DeviceProfile; 42 import com.android.launcher3.R; 43 import com.android.launcher3.Utilities; 44 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; 45 import com.android.launcher3.views.BaseDragLayer; 46 47 import java.util.Collections; 48 import java.util.List; 49 50 public class LandscapePagedViewHandler implements PagedOrientationHandler { 51 52 @Override getPrimaryValue(T x, T y)53 public <T> T getPrimaryValue(T x, T y) { 54 return y; 55 } 56 57 @Override getSecondaryValue(T x, T y)58 public <T> T getSecondaryValue(T x, T y) { 59 return x; 60 } 61 62 @Override getPrimaryValue(int x, int y)63 public int getPrimaryValue(int x, int y) { 64 return y; 65 } 66 67 @Override getSecondaryValue(int x, int y)68 public int getSecondaryValue(int x, int y) { 69 return x; 70 } 71 72 @Override getPrimaryValue(float x, float y)73 public float getPrimaryValue(float x, float y) { 74 return y; 75 } 76 77 @Override getSecondaryValue(float x, float y)78 public float getSecondaryValue(float x, float y) { 79 return x; 80 } 81 82 @Override isLayoutNaturalToLauncher()83 public boolean isLayoutNaturalToLauncher() { 84 return false; 85 } 86 87 @Override adjustFloatingIconStartVelocity(PointF velocity)88 public void adjustFloatingIconStartVelocity(PointF velocity) { 89 float oldX = velocity.x; 90 float oldY = velocity.y; 91 velocity.set(-oldY, oldX); 92 } 93 94 @Override set(T target, Int2DAction<T> action, int param)95 public <T> void set(T target, Int2DAction<T> action, int param) { 96 action.call(target, 0, param); 97 } 98 99 @Override set(T target, Float2DAction<T> action, float param)100 public <T> void set(T target, Float2DAction<T> action, float param) { 101 action.call(target, 0, param); 102 } 103 104 @Override setSecondary(T target, Float2DAction<T> action, float param)105 public <T> void setSecondary(T target, Float2DAction<T> action, float param) { 106 action.call(target, param, 0); 107 } 108 109 @Override getPrimaryDirection(MotionEvent event, int pointerIndex)110 public float getPrimaryDirection(MotionEvent event, int pointerIndex) { 111 return event.getY(pointerIndex); 112 } 113 114 @Override getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId)115 public float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId) { 116 return velocityTracker.getYVelocity(pointerId); 117 } 118 119 @Override getMeasuredSize(View view)120 public int getMeasuredSize(View view) { 121 return view.getMeasuredHeight(); 122 } 123 124 @Override getPrimarySize(View view)125 public int getPrimarySize(View view) { 126 return view.getHeight(); 127 } 128 129 @Override getPrimarySize(RectF rect)130 public float getPrimarySize(RectF rect) { 131 return rect.height(); 132 } 133 134 @Override getStart(RectF rect)135 public float getStart(RectF rect) { 136 return rect.top; 137 } 138 139 @Override getEnd(RectF rect)140 public float getEnd(RectF rect) { 141 return rect.bottom; 142 } 143 144 @Override getClearAllSidePadding(View view, boolean isRtl)145 public int getClearAllSidePadding(View view, boolean isRtl) { 146 return (isRtl ? view.getPaddingBottom() : - view.getPaddingTop()) / 2; 147 } 148 149 @Override getSecondaryDimension(View view)150 public int getSecondaryDimension(View view) { 151 return view.getWidth(); 152 } 153 154 @Override getPrimaryViewTranslate()155 public FloatProperty<View> getPrimaryViewTranslate() { 156 return VIEW_TRANSLATE_Y; 157 } 158 159 @Override getSecondaryViewTranslate()160 public FloatProperty<View> getSecondaryViewTranslate() { 161 return VIEW_TRANSLATE_X; 162 } 163 164 @Override getSplitTaskViewDismissDirection(SplitPositionOption splitPosition, DeviceProfile dp)165 public int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition, 166 DeviceProfile dp) { 167 // Don't use device profile here because we know we're in fake landscape, only split option 168 // available is top/left 169 if (splitPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT) { 170 // Top (visually left) side 171 return SPLIT_TRANSLATE_PRIMARY_NEGATIVE; 172 } 173 throw new IllegalStateException("Invalid split stage position: " + 174 splitPosition.mStagePosition); 175 } 176 177 @Override getPrimaryScroll(View view)178 public int getPrimaryScroll(View view) { 179 return view.getScrollY(); 180 } 181 182 @Override getPrimaryScale(View view)183 public float getPrimaryScale(View view) { 184 return view.getScaleY(); 185 } 186 187 @Override setMaxScroll(AccessibilityEvent event, int maxScroll)188 public void setMaxScroll(AccessibilityEvent event, int maxScroll) { 189 event.setMaxScrollY(maxScroll); 190 } 191 192 @Override getRecentsRtlSetting(Resources resources)193 public boolean getRecentsRtlSetting(Resources resources) { 194 return !Utilities.isRtl(resources); 195 } 196 197 @Override getDegreesRotated()198 public float getDegreesRotated() { 199 return 90; 200 } 201 202 @Override getRotation()203 public int getRotation() { 204 return Surface.ROTATION_90; 205 } 206 207 @Override getChildStart(View view)208 public int getChildStart(View view) { 209 return view.getTop(); 210 } 211 212 @Override getChildStartWithTranslation(View view)213 public float getChildStartWithTranslation(View view) { 214 return view.getTop() + view.getTranslationY(); 215 } 216 217 @Override getCenterForPage(View view, Rect insets)218 public int getCenterForPage(View view, Rect insets) { 219 return (view.getPaddingLeft() + view.getMeasuredWidth() + insets.left 220 - insets.right - view.getPaddingRight()) / 2; 221 } 222 223 @Override getScrollOffsetStart(View view, Rect insets)224 public int getScrollOffsetStart(View view, Rect insets) { 225 return insets.top + view.getPaddingTop(); 226 } 227 228 @Override getScrollOffsetEnd(View view, Rect insets)229 public int getScrollOffsetEnd(View view, Rect insets) { 230 return view.getHeight() - view.getPaddingBottom() - insets.bottom; 231 } 232 233 @Override getPrimaryTranslationDirectionFactor()234 public int getPrimaryTranslationDirectionFactor() { 235 return -1; 236 } 237 getSecondaryTranslationDirectionFactor()238 public int getSecondaryTranslationDirectionFactor() { 239 return 1; 240 } 241 242 @Override getSplitTranslationDirectionFactor(int stagePosition)243 public int getSplitTranslationDirectionFactor(int stagePosition) { 244 if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) { 245 return -1; 246 } else { 247 return 1; 248 } 249 } 250 251 @Override getSplitAnimationTranslation(int translationOffset, DeviceProfile dp)252 public int getSplitAnimationTranslation(int translationOffset, DeviceProfile dp) { 253 return translationOffset; 254 } 255 256 @Override getTaskMenuX(float x, View thumbnailView, int overScroll)257 public float getTaskMenuX(float x, View thumbnailView, int overScroll) { 258 return thumbnailView.getMeasuredWidth() + x; 259 } 260 261 @Override getTaskMenuY(float y, View thumbnailView, int overScroll)262 public float getTaskMenuY(float y, View thumbnailView, int overScroll) { 263 return y + overScroll; 264 } 265 266 @Override getTaskMenuWidth(View view)267 public int getTaskMenuWidth(View view) { 268 return view.getMeasuredHeight(); 269 } 270 271 @Override setTaskOptionsMenuLayoutOrientation(DeviceProfile deviceProfile, LinearLayout taskMenuLayout, int dividerSpacing, ShapeDrawable dividerDrawable)272 public void setTaskOptionsMenuLayoutOrientation(DeviceProfile deviceProfile, 273 LinearLayout taskMenuLayout, int dividerSpacing, 274 ShapeDrawable dividerDrawable) { 275 taskMenuLayout.setOrientation(LinearLayout.HORIZONTAL); 276 dividerDrawable.setIntrinsicWidth(dividerSpacing); 277 taskMenuLayout.setDividerDrawable(dividerDrawable); 278 } 279 280 @Override setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp, LinearLayout viewGroup, DeviceProfile deviceProfile)281 public void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp, 282 LinearLayout viewGroup, DeviceProfile deviceProfile) { 283 // Phone fake landscape 284 viewGroup.setOrientation(LinearLayout.VERTICAL); 285 lp.width = 0; 286 lp.height = WRAP_CONTENT; 287 lp.weight = 1; 288 Utilities.setStartMarginForView(viewGroup.findViewById(R.id.text), 0); 289 Utilities.setStartMarginForView(viewGroup.findViewById(R.id.icon), 0); 290 } 291 292 @Override setTaskMenuAroundTaskView(LinearLayout taskView, float margin)293 public void setTaskMenuAroundTaskView(LinearLayout taskView, float margin) { 294 BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskView.getLayoutParams(); 295 lp.topMargin += margin; 296 } 297 298 @Override getAdditionalInsetForTaskMenu(float margin)299 public PointF getAdditionalInsetForTaskMenu(float margin) { 300 return new PointF(margin, 0); 301 } 302 303 /* ---------- The following are only used by TaskViewTouchHandler. ---------- */ 304 305 @Override getUpDownSwipeDirection()306 public SingleAxisSwipeDetector.Direction getUpDownSwipeDirection() { 307 return HORIZONTAL; 308 } 309 310 @Override getUpDirection(boolean isRtl)311 public int getUpDirection(boolean isRtl) { 312 return isRtl ? SingleAxisSwipeDetector.DIRECTION_NEGATIVE 313 : SingleAxisSwipeDetector.DIRECTION_POSITIVE; 314 } 315 316 @Override isGoingUp(float displacement, boolean isRtl)317 public boolean isGoingUp(float displacement, boolean isRtl) { 318 return isRtl ? displacement < 0 : displacement > 0; 319 } 320 321 @Override getTaskDragDisplacementFactor(boolean isRtl)322 public int getTaskDragDisplacementFactor(boolean isRtl) { 323 return isRtl ? 1 : -1; 324 } 325 326 /* -------------------- */ 327 328 @Override getChildBounds(View child, int childStart, int pageCenter, boolean layoutChild)329 public ChildBounds getChildBounds(View child, int childStart, int pageCenter, 330 boolean layoutChild) { 331 final int childHeight = child.getMeasuredHeight(); 332 final int childBottom = childStart + childHeight; 333 final int childWidth = child.getMeasuredWidth(); 334 final int childLeft = pageCenter - childWidth/ 2; 335 if (layoutChild) { 336 child.layout(childLeft, childStart, childLeft + childWidth, childBottom); 337 } 338 return new ChildBounds(childHeight, childWidth, childBottom, childLeft); 339 } 340 341 @SuppressWarnings("SuspiciousNameCombination") 342 @Override getDistanceToBottomOfRect(DeviceProfile dp, Rect rect)343 public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) { 344 return rect.left; 345 } 346 347 @Override getSplitPositionOptions(DeviceProfile dp)348 public List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp) { 349 // Add "left" side of phone which is actually the top 350 return Collections.singletonList(new SplitPositionOption( 351 R.drawable.ic_split_screen, R.string.split_screen_position_left, 352 STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN)); 353 } 354 355 @Override getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary, DeviceProfile deviceProfile)356 public FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary, 357 DeviceProfile deviceProfile) { 358 return primary; 359 } 360 } 361