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.appwidget.AppWidgetHostView; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.res.Resources; 23 import android.graphics.Point; 24 import android.graphics.Rect; 25 import android.util.DisplayMetrics; 26 import android.view.Gravity; 27 import android.view.View; 28 import android.view.ViewGroup; 29 import android.view.ViewGroup.LayoutParams; 30 import android.widget.FrameLayout; 31 32 import com.android.launcher3.config.FeatureFlags; 33 34 import java.util.ArrayList; 35 36 public class DeviceProfile { 37 38 public interface LauncherLayoutChangeListener { 39 void onLauncherLayoutChanged(); 40 } 41 42 public final InvariantDeviceProfile inv; 43 44 // Device properties 45 public final boolean isTablet; 46 public final boolean isLargeTablet; 47 public final boolean isPhone; 48 public final boolean transposeLayoutWithOrientation; 49 50 // Device properties in current orientation 51 public final boolean isLandscape; 52 public final int widthPx; 53 public final int heightPx; 54 public final int availableWidthPx; 55 public final int availableHeightPx; 56 /** 57 * The maximum amount of left/right workspace padding as a percentage of the screen width. 58 * To be clear, this means that up to 7% of the screen width can be used as left padding, and 59 * 7% of the screen width can be used as right padding. 60 */ 61 private static final float MAX_HORIZONTAL_PADDING_PERCENT = 0.14f; 62 63 // Overview mode 64 private final int overviewModeMinIconZoneHeightPx; 65 private final int overviewModeMaxIconZoneHeightPx; 66 private final int overviewModeBarItemWidthPx; 67 private final int overviewModeBarSpacerWidthPx; 68 private final float overviewModeIconZoneRatio; 69 70 // Workspace 71 private int desiredWorkspaceLeftRightMarginPx; 72 public final int edgeMarginPx; 73 public final Rect defaultWidgetPadding; 74 private final int defaultPageSpacingPx; 75 private final int topWorkspacePadding; 76 private float dragViewScale; 77 public float workspaceSpringLoadShrinkFactor; 78 public final int workspaceSpringLoadedBottomSpace; 79 80 // Page indicator 81 private final int pageIndicatorHeightPx; 82 private final int pageIndicatorLandGutterLeftNavBarPx; 83 private final int pageIndicatorLandGutterRightNavBarPx; 84 private final int pageIndicatorLandWorkspaceOffsetPx; 85 86 // Workspace icons 87 public int iconSizePx; 88 public int iconTextSizePx; 89 public int iconDrawablePaddingPx; 90 public int iconDrawablePaddingOriginalPx; 91 92 public int cellWidthPx; 93 public int cellHeightPx; 94 95 // Folder 96 public int folderBackgroundOffset; 97 public int folderIconSizePx; 98 public int folderIconPreviewPadding; 99 public int folderCellWidthPx; 100 public int folderCellHeightPx; 101 public int folderChildDrawablePaddingPx; 102 103 // Hotseat 104 public int hotseatCellWidthPx; 105 public int hotseatCellHeightPx; 106 public int hotseatIconSizePx; 107 private int hotseatBarHeightPx; 108 private int hotseatBarTopPaddingPx; 109 private int hotseatLandGutterPx; 110 111 // All apps 112 public int allAppsNumCols; 113 public int allAppsNumPredictiveCols; 114 public int allAppsButtonVisualSize; 115 public int allAppsIconSizePx; 116 public int allAppsIconDrawablePaddingPx; 117 public float allAppsIconTextSizePx; 118 119 // Containers 120 private final int containerLeftPaddingPx; 121 private final int containerRightPaddingPx; 122 123 // Drop Target 124 public int dropTargetBarSizePx; 125 126 // Insets 127 private Rect mInsets = new Rect(); 128 129 // Listeners 130 private ArrayList<LauncherLayoutChangeListener> mListeners = new ArrayList<>(); 131 132 public DeviceProfile(Context context, InvariantDeviceProfile inv, 133 Point minSize, Point maxSize, 134 int width, int height, boolean isLandscape) { 135 136 this.inv = inv; 137 this.isLandscape = isLandscape; 138 139 Resources res = context.getResources(); 140 DisplayMetrics dm = res.getDisplayMetrics(); 141 142 // Constants from resources 143 isTablet = res.getBoolean(R.bool.is_tablet); 144 isLargeTablet = res.getBoolean(R.bool.is_large_tablet); 145 isPhone = !isTablet && !isLargeTablet; 146 147 // Some more constants 148 transposeLayoutWithOrientation = 149 res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation); 150 151 ComponentName cn = new ComponentName(context.getPackageName(), 152 this.getClass().getName()); 153 defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null); 154 edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin); 155 desiredWorkspaceLeftRightMarginPx = edgeMarginPx; 156 pageIndicatorHeightPx = 157 res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height); 158 pageIndicatorLandGutterLeftNavBarPx = res.getDimensionPixelSize( 159 R.dimen.dynamic_grid_page_indicator_gutter_width_left_nav_bar); 160 pageIndicatorLandWorkspaceOffsetPx = 161 res.getDimensionPixelSize(R.dimen.all_apps_caret_workspace_offset); 162 pageIndicatorLandGutterRightNavBarPx = res.getDimensionPixelSize( 163 R.dimen.dynamic_grid_page_indicator_gutter_width_right_nav_bar); 164 defaultPageSpacingPx = 165 res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing); 166 topWorkspacePadding = 167 res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_top_padding); 168 overviewModeMinIconZoneHeightPx = 169 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_min_icon_zone_height); 170 overviewModeMaxIconZoneHeightPx = 171 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_max_icon_zone_height); 172 overviewModeBarItemWidthPx = 173 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_item_width); 174 overviewModeBarSpacerWidthPx = 175 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_spacer_width); 176 overviewModeIconZoneRatio = 177 res.getInteger(R.integer.config_dynamic_grid_overview_icon_zone_percentage) / 100f; 178 iconDrawablePaddingOriginalPx = 179 res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding); 180 dropTargetBarSizePx = res.getDimensionPixelSize(R.dimen.dynamic_grid_drop_target_size); 181 workspaceSpringLoadedBottomSpace = 182 res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space); 183 hotseatBarHeightPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_height); 184 hotseatBarTopPaddingPx = 185 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding); 186 hotseatLandGutterPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_gutter_width); 187 containerLeftPaddingPx = 188 res.getDimensionPixelSize(R.dimen.dynamic_grid_container_land_left_padding); 189 containerRightPaddingPx = 190 res.getDimensionPixelSize(R.dimen.dynamic_grid_container_land_right_padding); 191 192 // Determine sizes. 193 widthPx = width; 194 heightPx = height; 195 if (isLandscape) { 196 availableWidthPx = maxSize.x; 197 availableHeightPx = minSize.y; 198 } else { 199 availableWidthPx = minSize.x; 200 availableHeightPx = maxSize.y; 201 } 202 203 // Calculate the remaining vars 204 updateAvailableDimensions(dm, res); 205 computeAllAppsButtonSize(context); 206 } 207 208 public void addLauncherLayoutChangedListener(LauncherLayoutChangeListener listener) { 209 if (!mListeners.contains(listener)) { 210 mListeners.add(listener); 211 } 212 } 213 214 public void removeLauncherLayoutChangedListener(LauncherLayoutChangeListener listener) { 215 if (mListeners.contains(listener)) { 216 mListeners.remove(listener); 217 } 218 } 219 220 /** 221 * Determine the exact visual footprint of the all apps button, taking into account scaling 222 * and internal padding of the drawable. 223 */ 224 private void computeAllAppsButtonSize(Context context) { 225 Resources res = context.getResources(); 226 float padding = res.getInteger(R.integer.config_allAppsButtonPaddingPercent) / 100f; 227 allAppsButtonVisualSize = (int) (hotseatIconSizePx * (1 - padding)) - context.getResources() 228 .getDimensionPixelSize(R.dimen.all_apps_button_scale_down); 229 } 230 231 private void updateAvailableDimensions(DisplayMetrics dm, Resources res) { 232 // Check to see if the icons fit in the new available height. If not, then we need to 233 // shrink the icon size. 234 float scale = 1f; 235 int drawablePadding = iconDrawablePaddingOriginalPx; 236 updateIconSize(1f, drawablePadding, res, dm); 237 float usedHeight = (cellHeightPx * inv.numRows); 238 239 int maxHeight = (availableHeightPx - getTotalWorkspacePadding().y); 240 if (usedHeight > maxHeight) { 241 scale = maxHeight / usedHeight; 242 drawablePadding = 0; 243 } 244 updateIconSize(scale, drawablePadding, res, dm); 245 } 246 247 private void updateIconSize(float scale, int drawablePadding, Resources res, 248 DisplayMetrics dm) { 249 iconSizePx = (int) (Utilities.pxFromDp(inv.iconSize, dm) * scale); 250 iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale); 251 iconDrawablePaddingPx = drawablePadding; 252 hotseatIconSizePx = (int) (Utilities.pxFromDp(inv.hotseatIconSize, dm) * scale); 253 allAppsIconSizePx = iconSizePx; 254 allAppsIconDrawablePaddingPx = iconDrawablePaddingPx; 255 allAppsIconTextSizePx = iconTextSizePx; 256 257 cellWidthPx = iconSizePx; 258 cellHeightPx = iconSizePx + iconDrawablePaddingPx 259 + Utilities.calculateTextHeight(iconTextSizePx); 260 final float scaleDps = !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND ? 0f 261 : res.getDimensionPixelSize(R.dimen.dragViewScale); 262 dragViewScale = (iconSizePx + scaleDps) / iconSizePx; 263 264 // Hotseat 265 hotseatCellWidthPx = iconSizePx; 266 hotseatCellHeightPx = iconSizePx; 267 268 if (!isVerticalBarLayout()) { 269 int expectedWorkspaceHeight = availableHeightPx - hotseatBarHeightPx 270 - pageIndicatorHeightPx - topWorkspacePadding; 271 float minRequiredHeight = dropTargetBarSizePx + workspaceSpringLoadedBottomSpace; 272 workspaceSpringLoadShrinkFactor = Math.min( 273 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f, 274 1 - (minRequiredHeight / expectedWorkspaceHeight)); 275 } else { 276 workspaceSpringLoadShrinkFactor = 277 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f; 278 } 279 280 // Folder cell 281 int cellPaddingX = res.getDimensionPixelSize(R.dimen.folder_cell_x_padding); 282 int cellPaddingY = res.getDimensionPixelSize(R.dimen.folder_cell_y_padding); 283 final int folderChildTextSize = 284 Utilities.calculateTextHeight(res.getDimension(R.dimen.folder_child_text_size)); 285 286 final int folderBottomPanelSize = 287 res.getDimensionPixelSize(R.dimen.folder_label_padding_top) 288 + res.getDimensionPixelSize(R.dimen.folder_label_padding_bottom) 289 + Utilities.calculateTextHeight(res.getDimension(R.dimen.folder_label_text_size)); 290 291 // Don't let the folder get too close to the edges of the screen. 292 folderCellWidthPx = Math.min(iconSizePx + 2 * cellPaddingX, 293 (availableWidthPx - 4 * edgeMarginPx) / inv.numFolderColumns); 294 folderCellHeightPx = Math.min(iconSizePx + 3 * cellPaddingY + folderChildTextSize, 295 (availableHeightPx - 4 * edgeMarginPx - folderBottomPanelSize) / inv.numFolderRows); 296 folderChildDrawablePaddingPx = Math.max(0, 297 (folderCellHeightPx - iconSizePx - folderChildTextSize) / 3); 298 299 // Folder icon 300 folderBackgroundOffset = -edgeMarginPx; 301 folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset; 302 folderIconPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding); 303 } 304 305 public void updateInsets(Rect insets) { 306 mInsets.set(insets); 307 } 308 309 public void updateAppsViewNumCols() { 310 allAppsNumCols = allAppsNumPredictiveCols = inv.numColumns; 311 } 312 313 /** Returns the width and height of the search bar, ignoring any padding. */ 314 public Point getSearchBarDimensForWidgetOpts() { 315 if (isVerticalBarLayout()) { 316 return new Point(dropTargetBarSizePx, availableHeightPx - 2 * edgeMarginPx); 317 } else { 318 int gap; 319 if (isTablet) { 320 // Pad the left and right of the workspace to ensure consistent spacing 321 // between all icons 322 int width = getCurrentWidth(); 323 // XXX: If the icon size changes across orientations, we will have to take 324 // that into account here too. 325 gap = ((width - 2 * edgeMarginPx 326 - (inv.numColumns * cellWidthPx)) / (2 * (inv.numColumns + 1))) 327 + edgeMarginPx; 328 } else { 329 gap = desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.right; 330 } 331 return new Point(availableWidthPx - 2 * gap, dropTargetBarSizePx); 332 } 333 } 334 335 public Point getCellSize() { 336 Point result = new Point(); 337 // Since we are only concerned with the overall padding, layout direction does 338 // not matter. 339 Point padding = getTotalWorkspacePadding(); 340 result.x = calculateCellWidth(availableWidthPx - padding.x, inv.numColumns); 341 result.y = calculateCellHeight(availableHeightPx - padding.y, inv.numRows); 342 return result; 343 } 344 345 public Point getTotalWorkspacePadding() { 346 Rect padding = getWorkspacePadding(null); 347 return new Point(padding.left + padding.right, padding.top + padding.bottom); 348 } 349 350 /** 351 * Returns the workspace padding in the specified orientation. 352 * Note that it assumes that while in verticalBarLayout, the nav bar is on the right, as such 353 * this value is not reliable. 354 * Use {@link #getTotalWorkspacePadding()} instead. 355 */ 356 public Rect getWorkspacePadding(Rect recycle) { 357 Rect padding = recycle == null ? new Rect() : recycle; 358 if (isVerticalBarLayout()) { 359 if (mInsets.left > 0) { 360 padding.set(mInsets.left + pageIndicatorLandGutterLeftNavBarPx, 0, 361 hotseatBarHeightPx + hotseatLandGutterPx - mInsets.left, 2 * edgeMarginPx); 362 } else { 363 padding.set(pageIndicatorLandGutterRightNavBarPx, 0, 364 hotseatBarHeightPx + hotseatLandGutterPx, 2 * edgeMarginPx); 365 } 366 } else { 367 int paddingBottom = hotseatBarHeightPx + pageIndicatorHeightPx; 368 if (isTablet) { 369 // Pad the left and right of the workspace to ensure consistent spacing 370 // between all icons 371 float gapScale = 1f + (dragViewScale - 1f) / 2f; 372 int width = getCurrentWidth(); 373 int height = getCurrentHeight(); 374 // The amount of screen space available for left/right padding. 375 int availablePaddingX = Math.max(0, width - (int) ((inv.numColumns * cellWidthPx) + 376 ((inv.numColumns - 1) * gapScale * cellWidthPx))); 377 availablePaddingX = (int) Math.min(availablePaddingX, 378 width * MAX_HORIZONTAL_PADDING_PERCENT); 379 int availablePaddingY = Math.max(0, height - topWorkspacePadding - paddingBottom 380 - (int) (2 * inv.numRows * cellHeightPx)); 381 padding.set(availablePaddingX / 2, topWorkspacePadding + availablePaddingY / 2, 382 availablePaddingX / 2, paddingBottom + availablePaddingY / 2); 383 } else { 384 // Pad the top and bottom of the workspace with search/hotseat bar sizes 385 padding.set(desiredWorkspaceLeftRightMarginPx, 386 topWorkspacePadding, 387 desiredWorkspaceLeftRightMarginPx, 388 paddingBottom); 389 } 390 } 391 return padding; 392 } 393 394 /** 395 * @return the bounds for which the open folders should be contained within 396 */ 397 public Rect getAbsoluteOpenFolderBounds() { 398 if (isVerticalBarLayout()) { 399 // Folders should only appear right of the drop target bar and left of the hotseat 400 return new Rect(mInsets.left + dropTargetBarSizePx + edgeMarginPx, 401 mInsets.top, 402 mInsets.left + availableWidthPx - hotseatBarHeightPx - edgeMarginPx, 403 mInsets.top + availableHeightPx); 404 } else { 405 // Folders should only appear below the drop target bar and above the hotseat 406 return new Rect(mInsets.left, 407 mInsets.top + dropTargetBarSizePx + edgeMarginPx, 408 mInsets.left + availableWidthPx, 409 mInsets.top + availableHeightPx - hotseatBarHeightPx - pageIndicatorHeightPx - 410 edgeMarginPx); 411 } 412 } 413 414 private int getWorkspacePageSpacing() { 415 if (isVerticalBarLayout() || isLargeTablet) { 416 // In landscape mode the page spacing is set to the default. 417 return defaultPageSpacingPx; 418 } else { 419 // In portrait, we want the pages spaced such that there is no 420 // overhang of the previous / next page into the current page viewport. 421 // We assume symmetrical padding in portrait mode. 422 return Math.max(defaultPageSpacingPx, getWorkspacePadding(null).left + 1); 423 } 424 } 425 426 int getOverviewModeButtonBarHeight() { 427 int zoneHeight = (int) (overviewModeIconZoneRatio * availableHeightPx); 428 zoneHeight = Math.min(overviewModeMaxIconZoneHeightPx, 429 Math.max(overviewModeMinIconZoneHeightPx, zoneHeight)); 430 return zoneHeight; 431 } 432 433 public static int calculateCellWidth(int width, int countX) { 434 return width / countX; 435 } 436 public static int calculateCellHeight(int height, int countY) { 437 return height / countY; 438 } 439 440 /** 441 * When {@code true}, the device is in landscape mode and the hotseat is on the right column. 442 * When {@code false}, either device is in portrait mode or the device is in landscape mode and 443 * the hotseat is on the bottom row. 444 */ 445 public boolean isVerticalBarLayout() { 446 return isLandscape && transposeLayoutWithOrientation; 447 } 448 449 boolean shouldFadeAdjacentWorkspaceScreens() { 450 return isVerticalBarLayout() || isLargeTablet; 451 } 452 453 private int getVisibleChildCount(ViewGroup parent) { 454 int visibleChildren = 0; 455 for (int i = 0; i < parent.getChildCount(); i++) { 456 if (parent.getChildAt(i).getVisibility() != View.GONE) { 457 visibleChildren++; 458 } 459 } 460 return visibleChildren; 461 } 462 463 public void layout(Launcher launcher, boolean notifyListeners) { 464 FrameLayout.LayoutParams lp; 465 boolean hasVerticalBarLayout = isVerticalBarLayout(); 466 final boolean isLayoutRtl = Utilities.isRtl(launcher.getResources()); 467 468 // Layout the search bar space 469 Point searchBarBounds = getSearchBarDimensForWidgetOpts(); 470 View searchBar = launcher.getDropTargetBar(); 471 lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams(); 472 lp.width = searchBarBounds.x; 473 lp.height = searchBarBounds.y; 474 lp.topMargin = mInsets.top + edgeMarginPx; 475 searchBar.setLayoutParams(lp); 476 477 // Layout the workspace 478 PagedView workspace = (PagedView) launcher.findViewById(R.id.workspace); 479 Rect workspacePadding = getWorkspacePadding(null); 480 workspace.setPadding(workspacePadding.left, workspacePadding.top, workspacePadding.right, 481 workspacePadding.bottom); 482 workspace.setPageSpacing(getWorkspacePageSpacing()); 483 484 View qsbContainer = launcher.getQsbContainer(); 485 lp = (FrameLayout.LayoutParams) qsbContainer.getLayoutParams(); 486 lp.topMargin = mInsets.top + workspacePadding.top; 487 qsbContainer.setLayoutParams(lp); 488 489 // Layout the hotseat 490 Hotseat hotseat = (Hotseat) launcher.findViewById(R.id.hotseat); 491 lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams(); 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 for 494 // this, we pad the left and right of the hotseat with half of the difference of a workspace 495 // cell vs a hotseat cell. 496 float workspaceCellWidth = (float) getCurrentWidth() / inv.numColumns; 497 float hotseatCellWidth = (float) getCurrentWidth() / inv.numHotseatIcons; 498 int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2); 499 if (hasVerticalBarLayout) { 500 // Vertical hotseat -- The hotseat is fixed in the layout to be on the right of the 501 // screen regardless of RTL 502 lp.gravity = Gravity.RIGHT; 503 lp.width = hotseatBarHeightPx + mInsets.left + mInsets.right; 504 lp.height = LayoutParams.MATCH_PARENT; 505 hotseat.getLayout().setPadding(mInsets.left, mInsets.top, mInsets.right, 506 workspacePadding.bottom); 507 } else if (isTablet) { 508 // Pad the hotseat with the workspace padding calculated above 509 lp.gravity = Gravity.BOTTOM; 510 lp.width = LayoutParams.MATCH_PARENT; 511 lp.height = hotseatBarHeightPx + mInsets.bottom; 512 hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left, 513 hotseatBarTopPaddingPx, hotseatAdjustment + workspacePadding.right, 514 mInsets.bottom); 515 } else { 516 // For phones, layout the hotseat without any bottom margin 517 // to ensure that we have space for the folders 518 lp.gravity = Gravity.BOTTOM; 519 lp.width = LayoutParams.MATCH_PARENT; 520 lp.height = hotseatBarHeightPx + mInsets.bottom; 521 hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left, 522 hotseatBarTopPaddingPx, hotseatAdjustment + workspacePadding.right, 523 mInsets.bottom); 524 } 525 hotseat.setLayoutParams(lp); 526 527 // Layout the page indicators 528 View pageIndicator = launcher.findViewById(R.id.page_indicator); 529 if (pageIndicator != null) { 530 lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams(); 531 if (isVerticalBarLayout()) { 532 if (mInsets.left > 0) { 533 lp.leftMargin = mInsets.left + pageIndicatorLandGutterLeftNavBarPx - 534 lp.width - pageIndicatorLandWorkspaceOffsetPx; 535 } else if (mInsets.right > 0) { 536 lp.leftMargin = pageIndicatorLandGutterRightNavBarPx - lp.width - 537 pageIndicatorLandWorkspaceOffsetPx; 538 } 539 lp.bottomMargin = workspacePadding.bottom; 540 } else { 541 // Put the page indicators above the hotseat 542 lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; 543 lp.height = pageIndicatorHeightPx; 544 lp.bottomMargin = hotseatBarHeightPx + mInsets.bottom; 545 } 546 pageIndicator.setLayoutParams(lp); 547 } 548 549 // Layout the Overview Mode 550 ViewGroup overviewMode = launcher.getOverviewPanel(); 551 if (overviewMode != null) { 552 lp = (FrameLayout.LayoutParams) overviewMode.getLayoutParams(); 553 lp.gravity = Gravity.LEFT | Gravity.BOTTOM; 554 555 int visibleChildCount = getVisibleChildCount(overviewMode); 556 int totalItemWidth = visibleChildCount * overviewModeBarItemWidthPx; 557 int maxWidth = totalItemWidth + (visibleChildCount-1) * overviewModeBarSpacerWidthPx; 558 559 lp.width = Math.min(availableWidthPx, maxWidth); 560 lp.height = getOverviewModeButtonBarHeight(); 561 // Center the overview buttons on the workspace page 562 lp.leftMargin = workspacePadding.left + (availableWidthPx - 563 workspacePadding.left - workspacePadding.right - lp.width) / 2; 564 overviewMode.setLayoutParams(lp); 565 } 566 567 if (notifyListeners) { 568 for (int i = mListeners.size() - 1; i >= 0; i--) { 569 mListeners.get(i).onLauncherLayoutChanged(); 570 } 571 } 572 } 573 574 private int getCurrentWidth() { 575 return isLandscape 576 ? Math.max(widthPx, heightPx) 577 : Math.min(widthPx, heightPx); 578 } 579 580 private int getCurrentHeight() { 581 return isLandscape 582 ? Math.min(widthPx, heightPx) 583 : Math.max(widthPx, heightPx); 584 } 585 586 587 /** 588 * @return the left/right paddings for all containers. 589 */ 590 public final int[] getContainerPadding(Context context) { 591 Resources res = context.getResources(); 592 593 // No paddings for portrait phone 594 if (isPhone && !isVerticalBarLayout()) { 595 return new int[] {0, 0}; 596 } 597 598 // In landscape, we match the width of the workspace 599 int padding = (pageIndicatorLandGutterRightNavBarPx + 600 hotseatBarHeightPx + hotseatLandGutterPx + mInsets.left) / 2; 601 return new int[]{ padding, padding }; 602 } 603 } 604