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.view.Gravity.BOTTOM; 20 import static android.view.Gravity.CENTER_HORIZONTAL; 21 import static android.view.Gravity.START; 22 import static android.view.Gravity.TOP; 23 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 24 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 25 26 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; 27 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; 28 import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL; 29 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT; 30 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT; 31 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN; 32 33 import android.content.res.Resources; 34 import android.graphics.Matrix; 35 import android.graphics.PointF; 36 import android.graphics.Rect; 37 import android.graphics.RectF; 38 import android.graphics.drawable.ShapeDrawable; 39 import android.util.FloatProperty; 40 import android.util.Pair; 41 import android.view.MotionEvent; 42 import android.view.Surface; 43 import android.view.VelocityTracker; 44 import android.view.View; 45 import android.view.accessibility.AccessibilityEvent; 46 import android.widget.FrameLayout; 47 import android.widget.LinearLayout; 48 49 import com.android.launcher3.DeviceProfile; 50 import com.android.launcher3.R; 51 import com.android.launcher3.Utilities; 52 import com.android.launcher3.util.SplitConfigurationOptions; 53 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; 54 import com.android.launcher3.util.SplitConfigurationOptions.StagePosition; 55 import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds; 56 import com.android.launcher3.views.BaseDragLayer; 57 58 import java.util.ArrayList; 59 import java.util.List; 60 61 public class PortraitPagedViewHandler implements PagedOrientationHandler { 62 63 private final Matrix mTmpMatrix = new Matrix(); 64 private final RectF mTmpRectF = new RectF(); 65 66 @Override getPrimaryValue(T x, T y)67 public <T> T getPrimaryValue(T x, T y) { 68 return x; 69 } 70 71 @Override getSecondaryValue(T x, T y)72 public <T> T getSecondaryValue(T x, T y) { 73 return y; 74 } 75 76 @Override getPrimaryValue(int x, int y)77 public int getPrimaryValue(int x, int y) { 78 return x; 79 } 80 81 @Override getSecondaryValue(int x, int y)82 public int getSecondaryValue(int x, int y) { 83 return y; 84 } 85 86 @Override getPrimaryValue(float x, float y)87 public float getPrimaryValue(float x, float y) { 88 return x; 89 } 90 91 @Override getSecondaryValue(float x, float y)92 public float getSecondaryValue(float x, float y) { 93 return y; 94 } 95 96 @Override isLayoutNaturalToLauncher()97 public boolean isLayoutNaturalToLauncher() { 98 return true; 99 } 100 101 @Override adjustFloatingIconStartVelocity(PointF velocity)102 public void adjustFloatingIconStartVelocity(PointF velocity) { 103 //no-op 104 } 105 106 @Override setPrimary(T target, Int2DAction<T> action, int param)107 public <T> void setPrimary(T target, Int2DAction<T> action, int param) { 108 action.call(target, param, 0); 109 } 110 111 @Override setPrimary(T target, Float2DAction<T> action, float param)112 public <T> void setPrimary(T target, Float2DAction<T> action, float param) { 113 action.call(target, param, 0); 114 } 115 116 @Override setSecondary(T target, Float2DAction<T> action, float param)117 public <T> void setSecondary(T target, Float2DAction<T> action, float param) { 118 action.call(target, 0, param); 119 } 120 121 @Override set(T target, Int2DAction<T> action, int primaryParam, int secondaryParam)122 public <T> void set(T target, Int2DAction<T> action, int primaryParam, 123 int secondaryParam) { 124 action.call(target, primaryParam, secondaryParam); 125 } 126 127 @Override getPrimaryDirection(MotionEvent event, int pointerIndex)128 public float getPrimaryDirection(MotionEvent event, int pointerIndex) { 129 return event.getX(pointerIndex); 130 } 131 132 @Override getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId)133 public float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId) { 134 return velocityTracker.getXVelocity(pointerId); 135 } 136 137 @Override getMeasuredSize(View view)138 public int getMeasuredSize(View view) { 139 return view.getMeasuredWidth(); 140 } 141 142 @Override getPrimarySize(View view)143 public int getPrimarySize(View view) { 144 return view.getWidth(); 145 } 146 147 @Override getPrimarySize(RectF rect)148 public float getPrimarySize(RectF rect) { 149 return rect.width(); 150 } 151 152 @Override getStart(RectF rect)153 public float getStart(RectF rect) { 154 return rect.left; 155 } 156 157 @Override getEnd(RectF rect)158 public float getEnd(RectF rect) { 159 return rect.right; 160 } 161 162 @Override getClearAllSidePadding(View view, boolean isRtl)163 public int getClearAllSidePadding(View view, boolean isRtl) { 164 return (isRtl ? view.getPaddingRight() : - view.getPaddingLeft()) / 2; 165 } 166 167 @Override getSecondaryDimension(View view)168 public int getSecondaryDimension(View view) { 169 return view.getHeight(); 170 } 171 172 @Override getPrimaryViewTranslate()173 public FloatProperty<View> getPrimaryViewTranslate() { 174 return VIEW_TRANSLATE_X; 175 } 176 177 @Override getSecondaryViewTranslate()178 public FloatProperty<View> getSecondaryViewTranslate() { 179 return VIEW_TRANSLATE_Y; 180 } 181 182 @Override getSplitTaskViewDismissDirection(@tagePosition int stagePosition, DeviceProfile dp)183 public int getSplitTaskViewDismissDirection(@StagePosition int stagePosition, 184 DeviceProfile dp) { 185 if (stagePosition == STAGE_POSITION_TOP_OR_LEFT) { 186 if (dp.isLandscape) { 187 // Left side 188 return SPLIT_TRANSLATE_PRIMARY_NEGATIVE; 189 } else { 190 // Top side 191 return SPLIT_TRANSLATE_SECONDARY_NEGATIVE; 192 } 193 } else if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) { 194 // We don't have a bottom option, so should be right 195 return SPLIT_TRANSLATE_PRIMARY_POSITIVE; 196 } 197 throw new IllegalStateException("Invalid split stage position: " + stagePosition); 198 } 199 200 @Override getPrimaryScroll(View view)201 public int getPrimaryScroll(View view) { 202 return view.getScrollX(); 203 } 204 205 @Override getPrimaryScale(View view)206 public float getPrimaryScale(View view) { 207 return view.getScaleX(); 208 } 209 210 @Override setMaxScroll(AccessibilityEvent event, int maxScroll)211 public void setMaxScroll(AccessibilityEvent event, int maxScroll) { 212 event.setMaxScrollX(maxScroll); 213 } 214 215 @Override getRecentsRtlSetting(Resources resources)216 public boolean getRecentsRtlSetting(Resources resources) { 217 return !Utilities.isRtl(resources); 218 } 219 220 @Override getDegreesRotated()221 public float getDegreesRotated() { 222 return 0; 223 } 224 225 @Override getRotation()226 public int getRotation() { 227 return Surface.ROTATION_0; 228 } 229 230 @Override setPrimaryScale(View view, float scale)231 public void setPrimaryScale(View view, float scale) { 232 view.setScaleX(scale); 233 } 234 235 @Override setSecondaryScale(View view, float scale)236 public void setSecondaryScale(View view, float scale) { 237 view.setScaleY(scale); 238 } 239 240 @Override getChildStart(View view)241 public int getChildStart(View view) { 242 return view.getLeft(); 243 } 244 245 @Override getCenterForPage(View view, Rect insets)246 public int getCenterForPage(View view, Rect insets) { 247 return (view.getPaddingTop() + view.getMeasuredHeight() + insets.top 248 - insets.bottom - view.getPaddingBottom()) / 2; 249 } 250 251 @Override getScrollOffsetStart(View view, Rect insets)252 public int getScrollOffsetStart(View view, Rect insets) { 253 return insets.left + view.getPaddingLeft(); 254 } 255 256 @Override getScrollOffsetEnd(View view, Rect insets)257 public int getScrollOffsetEnd(View view, Rect insets) { 258 return view.getWidth() - view.getPaddingRight() - insets.right; 259 } 260 getSecondaryTranslationDirectionFactor()261 public int getSecondaryTranslationDirectionFactor() { 262 return -1; 263 } 264 265 @Override getSplitTranslationDirectionFactor(int stagePosition, DeviceProfile deviceProfile)266 public int getSplitTranslationDirectionFactor(int stagePosition, DeviceProfile deviceProfile) { 267 if (deviceProfile.isLandscape && stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) { 268 return -1; 269 } else { 270 return 1; 271 } 272 } 273 274 @Override getTaskMenuX(float x, View thumbnailView, int overScroll, DeviceProfile deviceProfile)275 public float getTaskMenuX(float x, View thumbnailView, int overScroll, 276 DeviceProfile deviceProfile) { 277 if (deviceProfile.isLandscape) { 278 return x + overScroll 279 + (thumbnailView.getMeasuredWidth() - thumbnailView.getMeasuredHeight()) / 2f; 280 } else { 281 return x + overScroll; 282 } 283 } 284 285 @Override getTaskMenuY(float y, View thumbnailView, int overScroll)286 public float getTaskMenuY(float y, View thumbnailView, int overScroll) { 287 return y; 288 } 289 290 @Override getTaskMenuWidth(View view, DeviceProfile deviceProfile)291 public int getTaskMenuWidth(View view, DeviceProfile deviceProfile) { 292 return deviceProfile.isLandscape && !deviceProfile.overviewShowAsGrid ? 293 view.getMeasuredHeight() : 294 view.getMeasuredWidth(); 295 } 296 297 @Override setTaskOptionsMenuLayoutOrientation(DeviceProfile deviceProfile, LinearLayout taskMenuLayout, int dividerSpacing, ShapeDrawable dividerDrawable)298 public void setTaskOptionsMenuLayoutOrientation(DeviceProfile deviceProfile, 299 LinearLayout taskMenuLayout, int dividerSpacing, 300 ShapeDrawable dividerDrawable) { 301 taskMenuLayout.setOrientation(LinearLayout.VERTICAL); 302 dividerDrawable.setIntrinsicHeight(dividerSpacing); 303 taskMenuLayout.setDividerDrawable(dividerDrawable); 304 } 305 306 @Override setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp, LinearLayout viewGroup, DeviceProfile deviceProfile)307 public void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp, 308 LinearLayout viewGroup, DeviceProfile deviceProfile) { 309 viewGroup.setOrientation(LinearLayout.HORIZONTAL); 310 lp.width = LinearLayout.LayoutParams.MATCH_PARENT; 311 lp.height = WRAP_CONTENT; 312 } 313 314 @Override setTaskMenuAroundTaskView(LinearLayout taskView, float margin)315 public void setTaskMenuAroundTaskView(LinearLayout taskView, float margin) { 316 BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskView.getLayoutParams(); 317 lp.topMargin += margin; 318 lp.leftMargin += margin; 319 } 320 321 @Override getAdditionalInsetForTaskMenu(float margin)322 public PointF getAdditionalInsetForTaskMenu(float margin) { 323 return new PointF(0, 0); 324 } 325 326 @Override setDwbLayoutParamsAndGetTranslations(int taskViewWidth, int taskViewHeight, StagedSplitBounds splitBounds, DeviceProfile deviceProfile, View[] thumbnailViews, int desiredTaskId, View banner)327 public Pair<Float, Float> setDwbLayoutParamsAndGetTranslations(int taskViewWidth, 328 int taskViewHeight, StagedSplitBounds splitBounds, DeviceProfile deviceProfile, 329 View[] thumbnailViews, int desiredTaskId, View banner) { 330 float translationX = 0; 331 float translationY = 0; 332 FrameLayout.LayoutParams bannerParams = (FrameLayout.LayoutParams) banner.getLayoutParams(); 333 banner.setPivotX(0); 334 banner.setPivotY(0); 335 banner.setRotation(getDegreesRotated()); 336 if (splitBounds == null) { 337 // Single, fullscreen case 338 bannerParams.width = MATCH_PARENT; 339 bannerParams.gravity = BOTTOM | CENTER_HORIZONTAL; 340 return new Pair<>(translationX, translationY); 341 } 342 343 bannerParams.gravity = BOTTOM | ((deviceProfile.isLandscape) ? START : CENTER_HORIZONTAL); 344 345 // Set correct width 346 if (desiredTaskId == splitBounds.leftTopTaskId) { 347 bannerParams.width = thumbnailViews[0].getMeasuredWidth(); 348 } else { 349 bannerParams.width = thumbnailViews[1].getMeasuredWidth(); 350 } 351 352 // Set translations 353 if (deviceProfile.isLandscape) { 354 if (desiredTaskId == splitBounds.rightBottomTaskId) { 355 translationX = ((taskViewWidth * splitBounds.leftTaskPercent) 356 + (taskViewWidth * splitBounds.dividerWidthPercent)); 357 } 358 } else { 359 if (desiredTaskId == splitBounds.leftTopTaskId) { 360 FrameLayout.LayoutParams snapshotParams = 361 (FrameLayout.LayoutParams) thumbnailViews[0] 362 .getLayoutParams(); 363 translationY = -((taskViewHeight - snapshotParams.topMargin) 364 * (1f - splitBounds.topTaskPercent)); 365 } 366 } 367 return new Pair<>(translationX, translationY); 368 } 369 370 /* ---------- The following are only used by TaskViewTouchHandler. ---------- */ 371 372 @Override getUpDownSwipeDirection()373 public SingleAxisSwipeDetector.Direction getUpDownSwipeDirection() { 374 return VERTICAL; 375 } 376 377 @Override getUpDirection(boolean isRtl)378 public int getUpDirection(boolean isRtl) { 379 // Ignore rtl since it only affects X value displacement, Y displacement doesn't change 380 return SingleAxisSwipeDetector.DIRECTION_POSITIVE; 381 } 382 383 @Override isGoingUp(float displacement, boolean isRtl)384 public boolean isGoingUp(float displacement, boolean isRtl) { 385 // Ignore rtl since it only affects X value displacement, Y displacement doesn't change 386 return displacement < 0; 387 } 388 389 @Override getTaskDragDisplacementFactor(boolean isRtl)390 public int getTaskDragDisplacementFactor(boolean isRtl) { 391 // Ignore rtl since it only affects X value displacement, Y displacement doesn't change 392 return 1; 393 } 394 395 /* -------------------- */ 396 397 @Override getChildBounds(View child, int childStart, int pageCenter, boolean layoutChild)398 public ChildBounds getChildBounds(View child, int childStart, int pageCenter, 399 boolean layoutChild) { 400 final int childWidth = child.getMeasuredWidth(); 401 final int childRight = childStart + childWidth; 402 final int childHeight = child.getMeasuredHeight(); 403 final int childTop = pageCenter - childHeight / 2; 404 if (layoutChild) { 405 child.layout(childStart, childTop, childRight, childTop + childHeight); 406 } 407 return new ChildBounds(childWidth, childHeight, childRight, childTop); 408 } 409 410 @Override getDistanceToBottomOfRect(DeviceProfile dp, Rect rect)411 public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) { 412 return dp.heightPx - rect.bottom; 413 } 414 415 @Override getSplitPositionOptions(DeviceProfile dp)416 public List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp) { 417 List<SplitPositionOption> options = new ArrayList<>(1); 418 // Add both left and right options if we're in tablet mode 419 if (dp.isTablet && dp.isLandscape) { 420 options.add(new SplitPositionOption( 421 R.drawable.ic_split_right, R.string.split_screen_position_right, 422 STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_MAIN)); 423 options.add(new SplitPositionOption( 424 R.drawable.ic_split_left, R.string.split_screen_position_left, 425 STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN)); 426 } else { 427 if (dp.isSeascape()) { 428 // Add left/right options 429 options.add(new SplitPositionOption( 430 R.drawable.ic_split_right, R.string.split_screen_position_right, 431 STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_MAIN)); 432 } else if (dp.isLandscape) { 433 options.add(new SplitPositionOption( 434 R.drawable.ic_split_left, R.string.split_screen_position_left, 435 STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN)); 436 } else { 437 // Only add top option 438 options.add(new SplitPositionOption( 439 R.drawable.ic_split_top, R.string.split_screen_position_top, 440 STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN)); 441 } 442 } 443 return options; 444 } 445 446 @Override getInitialSplitPlaceholderBounds(int placeholderHeight, DeviceProfile dp, @StagePosition int stagePosition, Rect out)447 public void getInitialSplitPlaceholderBounds(int placeholderHeight, DeviceProfile dp, 448 @StagePosition int stagePosition, Rect out) { 449 int width = dp.widthPx; 450 out.set(0, 0, width, placeholderHeight); 451 if (!dp.isLandscape) { 452 // portrait, phone or tablet - spans width of screen, nothing else to do 453 return; 454 } 455 456 // Now we rotate the portrait rect depending on what side we want pinned 457 boolean pinToRight = stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT; 458 459 int screenHeight = dp.heightPx; 460 float postRotateScale = (float) screenHeight / width; 461 mTmpMatrix.reset(); 462 mTmpMatrix.postRotate(pinToRight ? 90 : 270); 463 mTmpMatrix.postTranslate(pinToRight ? width : 0, pinToRight ? 0 : width); 464 // The placeholder height stays constant after rotation, so we don't change width scale 465 mTmpMatrix.postScale(1, postRotateScale); 466 467 mTmpRectF.set(out); 468 mTmpMatrix.mapRect(mTmpRectF); 469 mTmpRectF.roundOut(out); 470 } 471 472 @Override getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp, @StagePosition int stagePosition, Rect out1, Rect out2)473 public void getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp, 474 @StagePosition int stagePosition, Rect out1, Rect out2) { 475 int screenHeight = dp.heightPx; 476 int screenWidth = dp.widthPx; 477 out1.set(0, 0, screenWidth, screenHeight / 2 - splitDividerSize); 478 out2.set(0, screenHeight / 2 + splitDividerSize, screenWidth, screenHeight); 479 if (!dp.isLandscape) { 480 // Portrait - the window bounds are always top and bottom half 481 return; 482 } 483 484 // Now we rotate the portrait rect depending on what side we want pinned 485 boolean pinToRight = stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT; 486 float postRotateScale = (float) screenHeight / screenWidth; 487 488 mTmpMatrix.reset(); 489 mTmpMatrix.postRotate(pinToRight ? 90 : 270); 490 mTmpMatrix.postTranslate(pinToRight ? screenHeight : 0, pinToRight ? 0 : screenWidth); 491 mTmpMatrix.postScale(1 / postRotateScale, postRotateScale); 492 493 mTmpRectF.set(out1); 494 mTmpMatrix.mapRect(mTmpRectF); 495 mTmpRectF.roundOut(out1); 496 497 mTmpRectF.set(out2); 498 mTmpMatrix.mapRect(mTmpRectF); 499 mTmpRectF.roundOut(out2); 500 } 501 502 @Override setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect, StagedSplitBounds splitInfo, int desiredStagePosition)503 public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect, 504 StagedSplitBounds splitInfo, int desiredStagePosition) { 505 boolean isLandscape = dp.isLandscape; 506 if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) { 507 if (isLandscape) { 508 outRect.right = outRect.left + (int) (outRect.width() * splitInfo.leftTaskPercent); 509 } else { 510 outRect.bottom = outRect.top + (int) (outRect.height() * splitInfo.topTaskPercent); 511 } 512 } else { 513 if (isLandscape) { 514 outRect.left += (int) (outRect.width() * 515 (splitInfo.leftTaskPercent + splitInfo.dividerWidthPercent)); 516 } else { 517 outRect.top += (int) (outRect.height() * 518 (splitInfo.topTaskPercent + splitInfo.dividerHeightPercent)); 519 } 520 } 521 } 522 523 @Override measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot, int parentWidth, int parentHeight, StagedSplitBounds splitBoundsConfig, DeviceProfile dp)524 public void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot, 525 int parentWidth, int parentHeight, 526 StagedSplitBounds splitBoundsConfig, DeviceProfile dp) { 527 int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx; 528 int totalThumbnailHeight = parentHeight - spaceAboveSnapshot; 529 int dividerBar = splitBoundsConfig.appsStackedVertically 530 ? (int) (splitBoundsConfig.dividerHeightPercent * parentHeight) 531 : (int) (splitBoundsConfig.dividerWidthPercent * parentWidth); 532 int primarySnapshotHeight; 533 int primarySnapshotWidth; 534 int secondarySnapshotHeight; 535 int secondarySnapshotWidth; 536 float taskPercent = splitBoundsConfig.appsStackedVertically ? 537 splitBoundsConfig.topTaskPercent : splitBoundsConfig.leftTaskPercent; 538 if (dp.isLandscape) { 539 primarySnapshotHeight = totalThumbnailHeight; 540 primarySnapshotWidth = (int) (parentWidth * taskPercent); 541 542 secondarySnapshotHeight = totalThumbnailHeight; 543 secondarySnapshotWidth = parentWidth - primarySnapshotWidth - dividerBar; 544 int translationX = primarySnapshotWidth + dividerBar; 545 secondarySnapshot.setTranslationX(translationX); 546 secondarySnapshot.setTranslationY(spaceAboveSnapshot); 547 } else { 548 primarySnapshotWidth = parentWidth; 549 primarySnapshotHeight = (int) (totalThumbnailHeight * taskPercent); 550 551 secondarySnapshotWidth = parentWidth; 552 secondarySnapshotHeight = totalThumbnailHeight - primarySnapshotHeight - dividerBar; 553 int translationY = primarySnapshotHeight + spaceAboveSnapshot + dividerBar; 554 secondarySnapshot.setTranslationY(translationY); 555 secondarySnapshot.setTranslationX(0); 556 } 557 primarySnapshot.measure( 558 View.MeasureSpec.makeMeasureSpec(primarySnapshotWidth, View.MeasureSpec.EXACTLY), 559 View.MeasureSpec.makeMeasureSpec(primarySnapshotHeight, View.MeasureSpec.EXACTLY)); 560 secondarySnapshot.measure( 561 View.MeasureSpec.makeMeasureSpec(secondarySnapshotWidth, View.MeasureSpec.EXACTLY), 562 View.MeasureSpec.makeMeasureSpec(secondarySnapshotHeight, 563 View.MeasureSpec.EXACTLY)); 564 } 565 566 @Override setIconAndSnapshotParams(View iconView, int taskIconMargin, int taskIconHeight, FrameLayout.LayoutParams snapshotParams, boolean isRtl)567 public void setIconAndSnapshotParams(View iconView, int taskIconMargin, int taskIconHeight, 568 FrameLayout.LayoutParams snapshotParams, boolean isRtl) { 569 FrameLayout.LayoutParams iconParams = 570 (FrameLayout.LayoutParams) iconView.getLayoutParams(); 571 iconParams.gravity = TOP | CENTER_HORIZONTAL; 572 iconParams.leftMargin = iconParams.rightMargin = 0; 573 iconParams.topMargin = taskIconMargin; 574 } 575 576 @Override setSplitIconParams(View primaryIconView, View secondaryIconView, int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight, boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig)577 public void setSplitIconParams(View primaryIconView, View secondaryIconView, 578 int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight, 579 boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig) { 580 FrameLayout.LayoutParams primaryIconParams = 581 (FrameLayout.LayoutParams) primaryIconView.getLayoutParams(); 582 FrameLayout.LayoutParams secondaryIconParams = 583 new FrameLayout.LayoutParams(primaryIconParams); 584 int dividerBar = (splitConfig.appsStackedVertically ? 585 splitConfig.visualDividerBounds.height() : 586 splitConfig.visualDividerBounds.width()); 587 588 if (deviceProfile.isLandscape) { 589 primaryIconParams.gravity = TOP | START; 590 primaryIconView.setTranslationX( 591 primarySnapshotWidth - primaryIconView.getMeasuredWidth()); 592 primaryIconView.setTranslationY(0); 593 secondaryIconParams.gravity = TOP | START; 594 secondaryIconView.setTranslationX(primarySnapshotWidth + dividerBar); 595 } else { 596 primaryIconParams.gravity = TOP | CENTER_HORIZONTAL; 597 primaryIconView.setTranslationX(-(primaryIconView.getMeasuredWidth()) / 2f); 598 primaryIconView.setTranslationY(0); 599 600 secondaryIconParams.gravity = TOP | CENTER_HORIZONTAL; 601 secondaryIconView.setTranslationX(secondaryIconView.getMeasuredWidth() / 2f); 602 } 603 secondaryIconView.setTranslationY(0); 604 primaryIconView.setLayoutParams(primaryIconParams); 605 secondaryIconView.setLayoutParams(secondaryIconParams); 606 } 607 608 @Override getDefaultSplitPosition(DeviceProfile deviceProfile)609 public int getDefaultSplitPosition(DeviceProfile deviceProfile) { 610 if (!deviceProfile.isTablet) { 611 throw new IllegalStateException("Default position available only for large screens"); 612 } 613 if (deviceProfile.isLandscape) { 614 return STAGE_POSITION_BOTTOM_OR_RIGHT; 615 } else { 616 return STAGE_POSITION_TOP_OR_LEFT; 617 } 618 } 619 620 @Override getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary, DeviceProfile deviceProfile)621 public Pair<FloatProperty, FloatProperty> getSplitSelectTaskOffset(FloatProperty primary, 622 FloatProperty secondary, DeviceProfile deviceProfile) { 623 if (deviceProfile.isLandscape) { // or seascape 624 return new Pair<>(primary, secondary); 625 } else { 626 return new Pair<>(secondary, primary); 627 } 628 } 629 } 630