1 /* 2 * Copyright (C) 2008 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; 18 19 import android.content.Context; 20 import android.content.res.Configuration; 21 import android.content.res.Resources; 22 import android.graphics.Point; 23 import android.graphics.PointF; 24 import android.graphics.Rect; 25 import android.util.DisplayMetrics; 26 import android.view.Surface; 27 import android.view.WindowManager; 28 29 import com.android.launcher3.CellLayout.ContainerType; 30 import com.android.launcher3.graphics.IconShape; 31 import com.android.launcher3.icons.DotRenderer; 32 import com.android.launcher3.icons.IconNormalizer; 33 34 public class DeviceProfile { 35 36 public final InvariantDeviceProfile inv; 37 38 // Device properties 39 public final boolean isTablet; 40 public final boolean isLargeTablet; 41 public final boolean isPhone; 42 public final boolean transposeLayoutWithOrientation; 43 44 // Device properties in current orientation 45 public final boolean isLandscape; 46 public final boolean isMultiWindowMode; 47 48 public final int widthPx; 49 public final int heightPx; 50 public final int availableWidthPx; 51 public final int availableHeightPx; 52 53 public final float aspectRatio; 54 55 /** 56 * The maximum amount of left/right workspace padding as a percentage of the screen width. 57 * To be clear, this means that up to 7% of the screen width can be used as left padding, and 58 * 7% of the screen width can be used as right padding. 59 */ 60 private static final float MAX_HORIZONTAL_PADDING_PERCENT = 0.14f; 61 62 private static final float TALL_DEVICE_ASPECT_RATIO_THRESHOLD = 2.0f; 63 64 // To evenly space the icons, increase the left/right margins for tablets in portrait mode. 65 private static final int PORTRAIT_TABLET_LEFT_RIGHT_PADDING_MULTIPLIER = 4; 66 67 // Workspace 68 public final int desiredWorkspaceLeftRightMarginPx; 69 public final int cellLayoutPaddingLeftRightPx; 70 public final int cellLayoutBottomPaddingPx; 71 public final int edgeMarginPx; 72 public float workspaceSpringLoadShrinkFactor; 73 public final int workspaceSpringLoadedBottomSpace; 74 75 // Drag handle 76 public final int verticalDragHandleSizePx; 77 private final int verticalDragHandleOverlapWorkspace; 78 79 // Workspace icons 80 public int iconSizePx; 81 public int iconTextSizePx; 82 public int iconDrawablePaddingPx; 83 public int iconDrawablePaddingOriginalPx; 84 85 public int cellWidthPx; 86 public int cellHeightPx; 87 public int workspaceCellPaddingXPx; 88 89 // Folder 90 public int folderIconSizePx; 91 public int folderIconOffsetYPx; 92 93 // Folder cell 94 public int folderCellWidthPx; 95 public int folderCellHeightPx; 96 97 // Folder child 98 public int folderChildIconSizePx; 99 public int folderChildTextSizePx; 100 public int folderChildDrawablePaddingPx; 101 102 // Hotseat 103 public int hotseatCellHeightPx; 104 // In portrait: size = height, in landscape: size = width 105 public int hotseatBarSizePx; 106 public final int hotseatBarTopPaddingPx; 107 public int hotseatBarBottomPaddingPx; 108 // Start is the side next to the nav bar, end is the side next to the workspace 109 public final int hotseatBarSidePaddingStartPx; 110 public final int hotseatBarSidePaddingEndPx; 111 112 // All apps 113 public int allAppsCellHeightPx; 114 public int allAppsIconSizePx; 115 public int allAppsIconDrawablePaddingPx; 116 public float allAppsIconTextSizePx; 117 118 // Widgets 119 public final PointF appWidgetScale = new PointF(1.0f, 1.0f); 120 121 // Drop Target 122 public int dropTargetBarSizePx; 123 124 // Insets 125 private final Rect mInsets = new Rect(); 126 public final Rect workspacePadding = new Rect(); 127 private final Rect mHotseatPadding = new Rect(); 128 // When true, nav bar is on the left side of the screen. 129 private boolean mIsSeascape; 130 131 // Notification dots 132 public DotRenderer mDotRenderer; 133 DeviceProfile(Context context, InvariantDeviceProfile inv, Point minSize, Point maxSize, int width, int height, boolean isLandscape, boolean isMultiWindowMode)134 public DeviceProfile(Context context, InvariantDeviceProfile inv, 135 Point minSize, Point maxSize, 136 int width, int height, boolean isLandscape, boolean isMultiWindowMode) { 137 138 this.inv = inv; 139 this.isLandscape = isLandscape; 140 this.isMultiWindowMode = isMultiWindowMode; 141 142 // Determine sizes. 143 widthPx = width; 144 heightPx = height; 145 if (isLandscape) { 146 availableWidthPx = maxSize.x; 147 availableHeightPx = minSize.y; 148 } else { 149 availableWidthPx = minSize.x; 150 availableHeightPx = maxSize.y; 151 } 152 153 Resources res = context.getResources(); 154 DisplayMetrics dm = res.getDisplayMetrics(); 155 156 // Constants from resources 157 isTablet = res.getBoolean(R.bool.is_tablet); 158 isLargeTablet = res.getBoolean(R.bool.is_large_tablet); 159 isPhone = !isTablet && !isLargeTablet; 160 aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx); 161 boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0; 162 163 // Some more constants 164 transposeLayoutWithOrientation = 165 res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation); 166 167 context = getContext(context, isVerticalBarLayout() 168 ? Configuration.ORIENTATION_LANDSCAPE 169 : Configuration.ORIENTATION_PORTRAIT); 170 res = context.getResources(); 171 172 edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin); 173 desiredWorkspaceLeftRightMarginPx = isVerticalBarLayout() ? 0 : edgeMarginPx; 174 175 int cellLayoutPaddingLeftRightMultiplier = !isVerticalBarLayout() && isTablet 176 ? PORTRAIT_TABLET_LEFT_RIGHT_PADDING_MULTIPLIER : 1; 177 int cellLayoutPadding = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding); 178 if (isLandscape) { 179 cellLayoutPaddingLeftRightPx = 0; 180 cellLayoutBottomPaddingPx = cellLayoutPadding; 181 } else { 182 cellLayoutPaddingLeftRightPx = cellLayoutPaddingLeftRightMultiplier * cellLayoutPadding; 183 cellLayoutBottomPaddingPx = 0; 184 } 185 186 verticalDragHandleSizePx = res.getDimensionPixelSize( 187 R.dimen.vertical_drag_handle_size); 188 verticalDragHandleOverlapWorkspace = 189 res.getDimensionPixelSize(R.dimen.vertical_drag_handle_overlap_workspace); 190 191 iconDrawablePaddingOriginalPx = 192 res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding); 193 dropTargetBarSizePx = res.getDimensionPixelSize(R.dimen.dynamic_grid_drop_target_size); 194 workspaceSpringLoadedBottomSpace = 195 res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space); 196 197 workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x); 198 199 hotseatBarTopPaddingPx = 200 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding); 201 hotseatBarBottomPaddingPx = (isTallDevice ? 0 202 : res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_non_tall_padding)) 203 + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding); 204 hotseatBarSidePaddingEndPx = 205 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding); 206 // Add a bit of space between nav bar and hotseat in vertical bar layout. 207 hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? verticalDragHandleSizePx : 0; 208 hotseatBarSizePx = ResourceUtils.pxFromDp(inv.iconSize, dm) + (isVerticalBarLayout() 209 ? (hotseatBarSidePaddingStartPx + hotseatBarSidePaddingEndPx) 210 : (res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size) 211 + hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx)); 212 213 // Calculate all of the remaining variables. 214 updateAvailableDimensions(dm, res); 215 216 // Now that we have all of the variables calculated, we can tune certain sizes. 217 if (!isVerticalBarLayout() && isPhone && isTallDevice) { 218 // We increase the hotseat size when there is extra space. 219 // ie. For a display with a large aspect ratio, we can keep the icons on the workspace 220 // in portrait mode closer together by adding more height to the hotseat. 221 // Note: This calculation was created after noticing a pattern in the design spec. 222 int extraSpace = getCellSize().y - iconSizePx - iconDrawablePaddingPx * 2 223 - verticalDragHandleSizePx; 224 hotseatBarSizePx += extraSpace; 225 hotseatBarBottomPaddingPx += extraSpace; 226 227 // Recalculate the available dimensions using the new hotseat size. 228 updateAvailableDimensions(dm, res); 229 } 230 updateWorkspacePadding(); 231 232 // This is done last, after iconSizePx is calculated above. 233 mDotRenderer = new DotRenderer(iconSizePx, IconShape.getShapePath(), 234 IconShape.DEFAULT_PATH_SIZE); 235 } 236 copy(Context context)237 public DeviceProfile copy(Context context) { 238 Point size = new Point(availableWidthPx, availableHeightPx); 239 return new DeviceProfile(context, inv, size, size, widthPx, heightPx, isLandscape, 240 isMultiWindowMode); 241 } 242 getMultiWindowProfile(Context context, Point mwSize)243 public DeviceProfile getMultiWindowProfile(Context context, Point mwSize) { 244 // We take the minimum sizes of this profile and it's multi-window variant to ensure that 245 // the system decor is always excluded. 246 mwSize.set(Math.min(availableWidthPx, mwSize.x), Math.min(availableHeightPx, mwSize.y)); 247 248 // In multi-window mode, we can have widthPx = availableWidthPx 249 // and heightPx = availableHeightPx because Launcher uses the InvariantDeviceProfiles' 250 // widthPx and heightPx values where it's needed. 251 DeviceProfile profile = new DeviceProfile(context, inv, mwSize, mwSize, mwSize.x, mwSize.y, 252 isLandscape, true); 253 254 // If there isn't enough vertical cell padding with the labels displayed, hide the labels. 255 float workspaceCellPaddingY = profile.getCellSize().y - profile.iconSizePx 256 - iconDrawablePaddingPx - profile.iconTextSizePx; 257 if (workspaceCellPaddingY < profile.iconDrawablePaddingPx * 2) { 258 profile.adjustToHideWorkspaceLabels(); 259 } 260 261 // We use these scales to measure and layout the widgets using their full invariant profile 262 // sizes and then draw them scaled and centered to fit in their multi-window mode cellspans. 263 float appWidgetScaleX = (float) profile.getCellSize().x / getCellSize().x; 264 float appWidgetScaleY = (float) profile.getCellSize().y / getCellSize().y; 265 profile.appWidgetScale.set(appWidgetScaleX, appWidgetScaleY); 266 profile.updateWorkspacePadding(); 267 268 return profile; 269 } 270 271 /** 272 * Inverse of {@link #getMultiWindowProfile(Context, Point)} 273 * @return device profile corresponding to the current orientation in non multi-window mode. 274 */ getFullScreenProfile()275 public DeviceProfile getFullScreenProfile() { 276 return isLandscape ? inv.landscapeProfile : inv.portraitProfile; 277 } 278 279 /** 280 * Adjusts the profile so that the labels on the Workspace are hidden. 281 * It is important to call this method after the All Apps variables have been set. 282 */ adjustToHideWorkspaceLabels()283 private void adjustToHideWorkspaceLabels() { 284 iconTextSizePx = 0; 285 iconDrawablePaddingPx = 0; 286 cellHeightPx = iconSizePx; 287 288 // In normal cases, All Apps cell height should equal the Workspace cell height. 289 // Since we are removing labels from the Workspace, we need to manually compute the 290 // All Apps cell height. 291 int topBottomPadding = allAppsIconDrawablePaddingPx * (isVerticalBarLayout() ? 2 : 1); 292 allAppsCellHeightPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx 293 + Utilities.calculateTextHeight(allAppsIconTextSizePx) 294 + topBottomPadding * 2; 295 } 296 updateAvailableDimensions(DisplayMetrics dm, Resources res)297 private void updateAvailableDimensions(DisplayMetrics dm, Resources res) { 298 updateIconSize(1f, res, dm); 299 300 // Check to see if the icons fit within the available height. If not, then scale down. 301 float usedHeight = (cellHeightPx * inv.numRows); 302 int maxHeight = (availableHeightPx - getTotalWorkspacePadding().y); 303 if (usedHeight > maxHeight) { 304 float scale = maxHeight / usedHeight; 305 updateIconSize(scale, res, dm); 306 } 307 updateAvailableFolderCellDimensions(dm, res); 308 } 309 updateIconSize(float scale, Resources res, DisplayMetrics dm)310 private void updateIconSize(float scale, Resources res, DisplayMetrics dm) { 311 // Workspace 312 final boolean isVerticalLayout = isVerticalBarLayout(); 313 float invIconSizePx = isVerticalLayout ? inv.landscapeIconSize : inv.iconSize; 314 iconSizePx = Math.max(1, (int) (ResourceUtils.pxFromDp(invIconSizePx, dm) * scale)); 315 iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale); 316 iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale); 317 318 cellHeightPx = iconSizePx + iconDrawablePaddingPx 319 + Utilities.calculateTextHeight(iconTextSizePx); 320 int cellYPadding = (getCellSize().y - cellHeightPx) / 2; 321 if (iconDrawablePaddingPx > cellYPadding && !isVerticalLayout 322 && !isMultiWindowMode) { 323 // Ensures that the label is closer to its corresponding icon. This is not an issue 324 // with vertical bar layout or multi-window mode since the issue is handled separately 325 // with their calls to {@link #adjustToHideWorkspaceLabels}. 326 cellHeightPx -= (iconDrawablePaddingPx - cellYPadding); 327 iconDrawablePaddingPx = cellYPadding; 328 } 329 cellWidthPx = iconSizePx + iconDrawablePaddingPx; 330 331 // All apps 332 allAppsIconTextSizePx = iconTextSizePx; 333 allAppsIconSizePx = iconSizePx; 334 allAppsIconDrawablePaddingPx = iconDrawablePaddingPx; 335 allAppsCellHeightPx = getCellSize().y; 336 337 if (isVerticalLayout) { 338 // Always hide the Workspace text with vertical bar layout. 339 adjustToHideWorkspaceLabels(); 340 } 341 342 // Hotseat 343 if (isVerticalLayout) { 344 hotseatBarSizePx = iconSizePx + hotseatBarSidePaddingStartPx 345 + hotseatBarSidePaddingEndPx; 346 } 347 hotseatCellHeightPx = iconSizePx; 348 349 if (!isVerticalLayout) { 350 int expectedWorkspaceHeight = availableHeightPx - hotseatBarSizePx 351 - verticalDragHandleSizePx - edgeMarginPx; 352 float minRequiredHeight = dropTargetBarSizePx + workspaceSpringLoadedBottomSpace; 353 workspaceSpringLoadShrinkFactor = Math.min( 354 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f, 355 1 - (minRequiredHeight / expectedWorkspaceHeight)); 356 } else { 357 workspaceSpringLoadShrinkFactor = 358 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f; 359 } 360 361 // Folder icon 362 folderIconSizePx = IconNormalizer.getNormalizedCircleSize(iconSizePx); 363 folderIconOffsetYPx = (iconSizePx - folderIconSizePx) / 2; 364 } 365 updateAvailableFolderCellDimensions(DisplayMetrics dm, Resources res)366 private void updateAvailableFolderCellDimensions(DisplayMetrics dm, Resources res) { 367 int folderBottomPanelSize = res.getDimensionPixelSize(R.dimen.folder_label_padding_top) 368 + res.getDimensionPixelSize(R.dimen.folder_label_padding_bottom) 369 + Utilities.calculateTextHeight(res.getDimension(R.dimen.folder_label_text_size)); 370 371 updateFolderCellSize(1f, dm, res); 372 373 // Don't let the folder get too close to the edges of the screen. 374 int folderMargin = edgeMarginPx * 2; 375 Point totalWorkspacePadding = getTotalWorkspacePadding(); 376 377 // Check if the icons fit within the available height. 378 float usedHeight = folderCellHeightPx * inv.numFolderRows + folderBottomPanelSize; 379 int maxHeight = availableHeightPx - totalWorkspacePadding.y - folderMargin; 380 float scaleY = maxHeight / usedHeight; 381 382 // Check if the icons fit within the available width. 383 float usedWidth = folderCellWidthPx * inv.numFolderColumns; 384 int maxWidth = availableWidthPx - totalWorkspacePadding.x - folderMargin; 385 float scaleX = maxWidth / usedWidth; 386 387 float scale = Math.min(scaleX, scaleY); 388 if (scale < 1f) { 389 updateFolderCellSize(scale, dm, res); 390 } 391 } 392 updateFolderCellSize(float scale, DisplayMetrics dm, Resources res)393 private void updateFolderCellSize(float scale, DisplayMetrics dm, Resources res) { 394 folderChildIconSizePx = (int) (ResourceUtils.pxFromDp(inv.iconSize, dm) * scale); 395 folderChildTextSizePx = 396 (int) (res.getDimensionPixelSize(R.dimen.folder_child_text_size) * scale); 397 398 int textHeight = Utilities.calculateTextHeight(folderChildTextSizePx); 399 int cellPaddingX = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_x_padding) * scale); 400 int cellPaddingY = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_y_padding) * scale); 401 402 folderCellWidthPx = folderChildIconSizePx + 2 * cellPaddingX; 403 folderCellHeightPx = folderChildIconSizePx + 2 * cellPaddingY + textHeight; 404 folderChildDrawablePaddingPx = Math.max(0, 405 (folderCellHeightPx - folderChildIconSizePx - textHeight) / 3); 406 } 407 updateInsets(Rect insets)408 public void updateInsets(Rect insets) { 409 mInsets.set(insets); 410 updateWorkspacePadding(); 411 } 412 413 /** 414 * The current device insets. This is generally same as the insets being dispatched to 415 * {@link Insettable} elements, but can differ if the element is using a different profile. 416 */ getInsets()417 public Rect getInsets() { 418 return mInsets; 419 } 420 getCellSize()421 public Point getCellSize() { 422 Point result = new Point(); 423 // Since we are only concerned with the overall padding, layout direction does 424 // not matter. 425 Point padding = getTotalWorkspacePadding(); 426 result.x = calculateCellWidth(availableWidthPx - padding.x 427 - cellLayoutPaddingLeftRightPx * 2, inv.numColumns); 428 result.y = calculateCellHeight(availableHeightPx - padding.y 429 - cellLayoutBottomPaddingPx, inv.numRows); 430 return result; 431 } 432 getTotalWorkspacePadding()433 public Point getTotalWorkspacePadding() { 434 updateWorkspacePadding(); 435 return new Point(workspacePadding.left + workspacePadding.right, 436 workspacePadding.top + workspacePadding.bottom); 437 } 438 439 /** 440 * Updates {@link #workspacePadding} as a result of any internal value change to reflect the 441 * new workspace padding 442 */ updateWorkspacePadding()443 private void updateWorkspacePadding() { 444 Rect padding = workspacePadding; 445 if (isVerticalBarLayout()) { 446 padding.top = 0; 447 padding.bottom = edgeMarginPx; 448 if (isSeascape()) { 449 padding.left = hotseatBarSizePx; 450 padding.right = verticalDragHandleSizePx; 451 } else { 452 padding.left = verticalDragHandleSizePx; 453 padding.right = hotseatBarSizePx; 454 } 455 } else { 456 int paddingBottom = hotseatBarSizePx + verticalDragHandleSizePx 457 - verticalDragHandleOverlapWorkspace; 458 if (isTablet) { 459 // Pad the left and right of the workspace to ensure consistent spacing 460 // between all icons 461 // The amount of screen space available for left/right padding. 462 int availablePaddingX = Math.max(0, widthPx - ((inv.numColumns * cellWidthPx) + 463 ((inv.numColumns - 1) * cellWidthPx))); 464 availablePaddingX = (int) Math.min(availablePaddingX, 465 widthPx * MAX_HORIZONTAL_PADDING_PERCENT); 466 int availablePaddingY = Math.max(0, heightPx - edgeMarginPx - paddingBottom 467 - (2 * inv.numRows * cellHeightPx) - hotseatBarTopPaddingPx 468 - hotseatBarBottomPaddingPx); 469 padding.set(availablePaddingX / 2, edgeMarginPx + availablePaddingY / 2, 470 availablePaddingX / 2, paddingBottom + availablePaddingY / 2); 471 } else { 472 // Pad the top and bottom of the workspace with search/hotseat bar sizes 473 padding.set(desiredWorkspaceLeftRightMarginPx, 474 edgeMarginPx, 475 desiredWorkspaceLeftRightMarginPx, 476 paddingBottom); 477 } 478 } 479 } 480 getHotseatLayoutPadding()481 public Rect getHotseatLayoutPadding() { 482 if (isVerticalBarLayout()) { 483 if (isSeascape()) { 484 mHotseatPadding.set(mInsets.left + hotseatBarSidePaddingStartPx, 485 mInsets.top, hotseatBarSidePaddingEndPx, mInsets.bottom); 486 } else { 487 mHotseatPadding.set(hotseatBarSidePaddingEndPx, mInsets.top, 488 mInsets.right + hotseatBarSidePaddingStartPx, mInsets.bottom); 489 } 490 } else { 491 492 // We want the edges of the hotseat to line up with the edges of the workspace, but the 493 // icons in the hotseat are a different size, and so don't line up perfectly. To account 494 // for this, we pad the left and right of the hotseat with half of the difference of a 495 // workspace cell vs a hotseat cell. 496 float workspaceCellWidth = (float) widthPx / inv.numColumns; 497 float hotseatCellWidth = (float) widthPx / inv.numHotseatIcons; 498 int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2); 499 mHotseatPadding.set( 500 hotseatAdjustment + workspacePadding.left + cellLayoutPaddingLeftRightPx, 501 hotseatBarTopPaddingPx, 502 hotseatAdjustment + workspacePadding.right + cellLayoutPaddingLeftRightPx, 503 hotseatBarBottomPaddingPx + mInsets.bottom + cellLayoutBottomPaddingPx); 504 } 505 return mHotseatPadding; 506 } 507 508 /** 509 * @return the bounds for which the open folders should be contained within 510 */ getAbsoluteOpenFolderBounds()511 public Rect getAbsoluteOpenFolderBounds() { 512 if (isVerticalBarLayout()) { 513 // Folders should only appear right of the drop target bar and left of the hotseat 514 return new Rect(mInsets.left + dropTargetBarSizePx + edgeMarginPx, 515 mInsets.top, 516 mInsets.left + availableWidthPx - hotseatBarSizePx - edgeMarginPx, 517 mInsets.top + availableHeightPx); 518 } else { 519 // Folders should only appear below the drop target bar and above the hotseat 520 return new Rect(mInsets.left + edgeMarginPx, 521 mInsets.top + dropTargetBarSizePx + edgeMarginPx, 522 mInsets.left + availableWidthPx - edgeMarginPx, 523 mInsets.top + availableHeightPx - hotseatBarSizePx 524 - verticalDragHandleSizePx - edgeMarginPx); 525 } 526 } 527 calculateCellWidth(int width, int countX)528 public static int calculateCellWidth(int width, int countX) { 529 return width / countX; 530 } calculateCellHeight(int height, int countY)531 public static int calculateCellHeight(int height, int countY) { 532 return height / countY; 533 } 534 535 /** 536 * When {@code true}, the device is in landscape mode and the hotseat is on the right column. 537 * When {@code false}, either device is in portrait mode or the device is in landscape mode and 538 * the hotseat is on the bottom row. 539 */ isVerticalBarLayout()540 public boolean isVerticalBarLayout() { 541 return isLandscape && transposeLayoutWithOrientation; 542 } 543 544 /** 545 * Updates orientation information and returns true if it has changed from the previous value. 546 */ updateIsSeascape(WindowManager wm)547 public boolean updateIsSeascape(WindowManager wm) { 548 if (isVerticalBarLayout()) { 549 boolean isSeascape = wm.getDefaultDisplay().getRotation() == Surface.ROTATION_270; 550 if (mIsSeascape != isSeascape) { 551 mIsSeascape = isSeascape; 552 return true; 553 } 554 } 555 return false; 556 } 557 isSeascape()558 public boolean isSeascape() { 559 return isVerticalBarLayout() && mIsSeascape; 560 } 561 shouldFadeAdjacentWorkspaceScreens()562 public boolean shouldFadeAdjacentWorkspaceScreens() { 563 return isVerticalBarLayout() || isLargeTablet; 564 } 565 getCellHeight(@ontainerType int containerType)566 public int getCellHeight(@ContainerType int containerType) { 567 switch (containerType) { 568 case CellLayout.WORKSPACE: 569 return cellHeightPx; 570 case CellLayout.FOLDER: 571 return folderCellHeightPx; 572 case CellLayout.HOTSEAT: 573 return hotseatCellHeightPx; 574 default: 575 // ?? 576 return 0; 577 } 578 } 579 getContext(Context c, int orientation)580 private static Context getContext(Context c, int orientation) { 581 Configuration context = new Configuration(c.getResources().getConfiguration()); 582 context.orientation = orientation; 583 return c.createConfigurationContext(context); 584 } 585 586 /** 587 * Callback when a component changes the DeviceProfile associated with it, as a result of 588 * configuration change 589 */ 590 public interface OnDeviceProfileChangeListener { 591 592 /** 593 * Called when the device profile is reassigned. Note that for layout and measurements, it 594 * is sufficient to listen for inset changes. Use this callback when you need to perform 595 * a one time operation. 596 */ onDeviceProfileChanged(DeviceProfile dp)597 void onDeviceProfileChanged(DeviceProfile dp); 598 } 599 } 600