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.Paint; 24 import android.graphics.Paint.FontMetrics; 25 import android.graphics.Point; 26 import android.graphics.Rect; 27 import android.util.DisplayMetrics; 28 import android.view.Gravity; 29 import android.view.View; 30 import android.view.ViewGroup; 31 import android.view.ViewGroup.LayoutParams; 32 import android.view.ViewGroup.MarginLayoutParams; 33 import android.widget.FrameLayout; 34 import android.widget.LinearLayout; 35 36 public class DeviceProfile { 37 38 public final InvariantDeviceProfile inv; 39 40 // Device properties 41 public final boolean isTablet; 42 public final boolean isLargeTablet; 43 public final boolean isPhone; 44 public final boolean transposeLayoutWithOrientation; 45 46 // Device properties in current orientation 47 public final boolean isLandscape; 48 public final int widthPx; 49 public final int heightPx; 50 public final int availableWidthPx; 51 public final int availableHeightPx; 52 /** 53 * The maximum amount of left/right workspace padding as a percentage of the screen width. 54 * To be clear, this means that up to 7% of the screen width can be used as left padding, and 55 * 7% of the screen width can be used as right padding. 56 */ 57 private static final float MAX_HORIZONTAL_PADDING_PERCENT = 0.14f; 58 59 // Overview mode 60 private final int overviewModeMinIconZoneHeightPx; 61 private final int overviewModeMaxIconZoneHeightPx; 62 private final int overviewModeBarItemWidthPx; 63 private final int overviewModeBarSpacerWidthPx; 64 private final float overviewModeIconZoneRatio; 65 66 // Workspace 67 private int desiredWorkspaceLeftRightMarginPx; 68 public final int edgeMarginPx; 69 public final Rect defaultWidgetPadding; 70 private final int pageIndicatorHeightPx; 71 private final int defaultPageSpacingPx; 72 private float dragViewScale; 73 74 // Workspace icons 75 public int iconSizePx; 76 public int iconTextSizePx; 77 public int iconDrawablePaddingPx; 78 public int iconDrawablePaddingOriginalPx; 79 80 public int cellWidthPx; 81 public int cellHeightPx; 82 83 // Folder 84 public int folderBackgroundOffset; 85 public int folderIconSizePx; 86 public int folderCellWidthPx; 87 public int folderCellHeightPx; 88 89 // Hotseat 90 public int hotseatCellWidthPx; 91 public int hotseatCellHeightPx; 92 public int hotseatIconSizePx; 93 private int normalHotseatBarHeightPx, shortHotseatBarHeightPx; 94 private int hotseatBarHeightPx; // One of the above. 95 96 // All apps 97 public int allAppsNumCols; 98 public int allAppsNumPredictiveCols; 99 public int allAppsButtonVisualSize; 100 public final int allAppsIconSizePx; 101 public final float allAppsIconTextSizeSp; 102 103 // QSB 104 private int searchBarWidgetInternalPaddingTop, searchBarWidgetInternalPaddingBottom; 105 private int searchBarTopPaddingPx; 106 private int tallSearchBarNegativeTopPaddingPx, normalSearchBarTopExtraPaddingPx; 107 private int searchBarTopExtraPaddingPx; // One of the above. 108 private int normalSearchBarBottomPaddingPx, tallSearchBarBottomPaddingPx; 109 private int searchBarBottomPaddingPx; // One of the above. 110 private int normalSearchBarSpaceHeightPx, tallSearchBarSpaceHeightPx; 111 private int searchBarSpaceHeightPx; // One of the above. 112 113 public DeviceProfile(Context context, InvariantDeviceProfile inv, 114 Point minSize, Point maxSize, 115 int width, int height, boolean isLandscape) { 116 117 this.inv = inv; 118 this.isLandscape = isLandscape; 119 120 Resources res = context.getResources(); 121 DisplayMetrics dm = res.getDisplayMetrics(); 122 123 // Constants from resources 124 isTablet = res.getBoolean(R.bool.is_tablet); 125 isLargeTablet = res.getBoolean(R.bool.is_large_tablet); 126 isPhone = !isTablet && !isLargeTablet; 127 128 // Some more constants 129 transposeLayoutWithOrientation = 130 res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation); 131 132 ComponentName cn = new ComponentName(context.getPackageName(), 133 this.getClass().getName()); 134 defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null); 135 edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin); 136 desiredWorkspaceLeftRightMarginPx = 2 * edgeMarginPx; 137 pageIndicatorHeightPx = 138 res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height); 139 defaultPageSpacingPx = 140 res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing); 141 overviewModeMinIconZoneHeightPx = 142 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_min_icon_zone_height); 143 overviewModeMaxIconZoneHeightPx = 144 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_max_icon_zone_height); 145 overviewModeBarItemWidthPx = 146 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_item_width); 147 overviewModeBarSpacerWidthPx = 148 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_spacer_width); 149 overviewModeIconZoneRatio = 150 res.getInteger(R.integer.config_dynamic_grid_overview_icon_zone_percentage) / 100f; 151 iconDrawablePaddingOriginalPx = 152 res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding); 153 154 // AllApps uses the original non-scaled icon text size 155 allAppsIconTextSizeSp = inv.iconTextSize; 156 157 // AllApps uses the original non-scaled icon size 158 allAppsIconSizePx = Utilities.pxFromDp(inv.iconSize, dm); 159 160 // Determine sizes. 161 widthPx = width; 162 heightPx = height; 163 if (isLandscape) { 164 availableWidthPx = maxSize.x; 165 availableHeightPx = minSize.y; 166 } else { 167 availableWidthPx = minSize.x; 168 availableHeightPx = maxSize.y; 169 } 170 171 // Calculate the remaining vars 172 updateAvailableDimensions(dm, res); 173 computeAllAppsButtonSize(context); 174 } 175 176 /** 177 * Determine the exact visual footprint of the all apps button, taking into account scaling 178 * and internal padding of the drawable. 179 */ 180 private void computeAllAppsButtonSize(Context context) { 181 Resources res = context.getResources(); 182 float padding = res.getInteger(R.integer.config_allAppsButtonPaddingPercent) / 100f; 183 allAppsButtonVisualSize = (int) (hotseatIconSizePx * (1 - padding)) - context.getResources() 184 .getDimensionPixelSize(R.dimen.all_apps_button_scale_down); 185 } 186 187 private void updateAvailableDimensions(DisplayMetrics dm, Resources res) { 188 // Check to see if the icons fit in the new available height. If not, then we need to 189 // shrink the icon size. 190 float scale = 1f; 191 int drawablePadding = iconDrawablePaddingOriginalPx; 192 updateIconSize(1f, drawablePadding, res, dm); 193 float usedHeight = (cellHeightPx * inv.numRows); 194 195 // We only care about the top and bottom workspace padding, which is not affected by RTL. 196 Rect workspacePadding = getWorkspacePadding(false /* isLayoutRtl */); 197 int maxHeight = (availableHeightPx - workspacePadding.top - workspacePadding.bottom); 198 if (usedHeight > maxHeight) { 199 scale = maxHeight / usedHeight; 200 drawablePadding = 0; 201 } 202 updateIconSize(scale, drawablePadding, res, dm); 203 } 204 205 private void updateIconSize(float scale, int drawablePadding, Resources res, 206 DisplayMetrics dm) { 207 iconSizePx = (int) (Utilities.pxFromDp(inv.iconSize, dm) * scale); 208 iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale); 209 iconDrawablePaddingPx = drawablePadding; 210 hotseatIconSizePx = (int) (Utilities.pxFromDp(inv.hotseatIconSize, dm) * scale); 211 212 // Search Bar 213 normalSearchBarSpaceHeightPx = res.getDimensionPixelSize( 214 R.dimen.dynamic_grid_search_bar_height); 215 tallSearchBarSpaceHeightPx = res.getDimensionPixelSize( 216 R.dimen.dynamic_grid_search_bar_height_tall); 217 searchBarWidgetInternalPaddingTop = res.getDimensionPixelSize( 218 R.dimen.qsb_internal_padding_top); 219 searchBarWidgetInternalPaddingBottom = res.getDimensionPixelSize( 220 R.dimen.qsb_internal_padding_bottom); 221 normalSearchBarTopExtraPaddingPx = res.getDimensionPixelSize( 222 R.dimen.dynamic_grid_search_bar_extra_top_padding); 223 tallSearchBarNegativeTopPaddingPx = res.getDimensionPixelSize( 224 R.dimen.dynamic_grid_search_bar_negative_top_padding_short); 225 if (isTablet && !isVerticalBarLayout()) { 226 searchBarTopPaddingPx = searchBarWidgetInternalPaddingTop; 227 normalSearchBarBottomPaddingPx = searchBarWidgetInternalPaddingBottom + 228 res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_bottom_padding_tablet); 229 tallSearchBarBottomPaddingPx = normalSearchBarBottomPaddingPx; 230 } else { 231 searchBarTopPaddingPx = searchBarWidgetInternalPaddingTop; 232 normalSearchBarBottomPaddingPx = searchBarWidgetInternalPaddingBottom + 233 res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_bottom_padding); 234 tallSearchBarBottomPaddingPx = searchBarWidgetInternalPaddingBottom 235 + res.getDimensionPixelSize( 236 R.dimen.dynamic_grid_search_bar_bottom_negative_padding_short); 237 } 238 239 // Calculate the actual text height 240 Paint textPaint = new Paint(); 241 textPaint.setTextSize(iconTextSizePx); 242 FontMetrics fm = textPaint.getFontMetrics(); 243 cellWidthPx = iconSizePx; 244 cellHeightPx = iconSizePx + iconDrawablePaddingPx + (int) Math.ceil(fm.bottom - fm.top); 245 final float scaleDps = res.getDimensionPixelSize(R.dimen.dragViewScale); 246 dragViewScale = (iconSizePx + scaleDps) / iconSizePx; 247 248 // Hotseat 249 normalHotseatBarHeightPx = iconSizePx + 4 * edgeMarginPx; 250 shortHotseatBarHeightPx = iconSizePx + 2 * edgeMarginPx; 251 hotseatCellWidthPx = iconSizePx; 252 hotseatCellHeightPx = iconSizePx; 253 254 // Folder 255 int folderCellPadding = isTablet || isLandscape ? 6 * edgeMarginPx : 3 * edgeMarginPx; 256 // Don't let the folder get too close to the edges of the screen. 257 folderCellWidthPx = Math.min(cellWidthPx + folderCellPadding, 258 (availableWidthPx - 4 * edgeMarginPx) / inv.numFolderColumns); 259 folderCellHeightPx = cellHeightPx + edgeMarginPx; 260 folderBackgroundOffset = -edgeMarginPx; 261 folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset; 262 } 263 264 /** 265 * @param recyclerViewWidth the available width of the AllAppsRecyclerView 266 */ 267 public void updateAppsViewNumCols(Resources res, int recyclerViewWidth) { 268 int appsViewLeftMarginPx = 269 res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin); 270 int allAppsCellWidthGap = 271 res.getDimensionPixelSize(R.dimen.all_apps_icon_width_gap); 272 int availableAppsWidthPx = (recyclerViewWidth > 0) ? recyclerViewWidth : availableWidthPx; 273 int numAppsCols = (availableAppsWidthPx + allAppsCellWidthGap - appsViewLeftMarginPx) / 274 (allAppsIconSizePx + allAppsCellWidthGap); 275 int numPredictiveAppCols = Math.max(inv.minAllAppsPredictionColumns, numAppsCols); 276 allAppsNumCols = numAppsCols; 277 allAppsNumPredictiveCols = numPredictiveAppCols; 278 } 279 280 /** Returns the amount of extra space to allocate to the search bar for vertical padding. */ 281 private int getSearchBarTotalVerticalPadding() { 282 return searchBarTopPaddingPx + searchBarTopExtraPaddingPx + searchBarBottomPaddingPx; 283 } 284 285 /** Returns the width and height of the search bar, ignoring any padding. */ 286 public Point getSearchBarDimensForWidgetOpts(Resources res) { 287 Rect searchBarBounds = getSearchBarBounds(Utilities.isRtl(res)); 288 if (isVerticalBarLayout()) { 289 return new Point(searchBarBounds.width(), searchBarBounds.height()); 290 } 291 int widgetInternalPadding = searchBarWidgetInternalPaddingTop + 292 searchBarWidgetInternalPaddingBottom; 293 return new Point(searchBarBounds.width(), searchBarSpaceHeightPx + widgetInternalPadding); 294 } 295 296 /** Returns the search bar bounds in the current orientation */ 297 public Rect getSearchBarBounds(boolean isLayoutRtl) { 298 Rect bounds = new Rect(); 299 if (isVerticalBarLayout()) { 300 if (isLayoutRtl) { 301 bounds.set(availableWidthPx - normalSearchBarSpaceHeightPx, edgeMarginPx, 302 availableWidthPx, availableHeightPx - edgeMarginPx); 303 } else { 304 bounds.set(0, edgeMarginPx, normalSearchBarSpaceHeightPx, 305 availableHeightPx - edgeMarginPx); 306 } 307 } else { 308 int boundsBottom = searchBarSpaceHeightPx + getSearchBarTotalVerticalPadding(); 309 if (isTablet) { 310 // Pad the left and right of the workspace to ensure consistent spacing 311 // between all icons 312 int width = getCurrentWidth(); 313 // XXX: If the icon size changes across orientations, we will have to take 314 // that into account here too. 315 int gap = (int) ((width - 2 * edgeMarginPx - 316 (inv.numColumns * cellWidthPx)) / (2 * (inv.numColumns + 1))); 317 bounds.set(edgeMarginPx + gap, 0, 318 availableWidthPx - (edgeMarginPx + gap), boundsBottom); 319 } else { 320 bounds.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left, 321 0, 322 availableWidthPx - (desiredWorkspaceLeftRightMarginPx - 323 defaultWidgetPadding.right), boundsBottom); 324 } 325 } 326 return bounds; 327 } 328 329 /** Returns the workspace padding in the specified orientation */ 330 Rect getWorkspacePadding(boolean isLayoutRtl) { 331 Rect searchBarBounds = getSearchBarBounds(isLayoutRtl); 332 Rect padding = new Rect(); 333 if (isVerticalBarLayout()) { 334 // Pad the left and right of the workspace with search/hotseat bar sizes 335 if (isLayoutRtl) { 336 padding.set(normalHotseatBarHeightPx, edgeMarginPx, 337 searchBarBounds.width(), edgeMarginPx); 338 } else { 339 padding.set(searchBarBounds.width(), edgeMarginPx, 340 normalHotseatBarHeightPx, edgeMarginPx); 341 } 342 } else { 343 int paddingTop = searchBarBounds.bottom; 344 int paddingBottom = hotseatBarHeightPx + pageIndicatorHeightPx; 345 if (isTablet) { 346 // Pad the left and right of the workspace to ensure consistent spacing 347 // between all icons 348 float gapScale = 1f + (dragViewScale - 1f) / 2f; 349 int width = getCurrentWidth(); 350 int height = getCurrentHeight(); 351 // The amount of screen space available for left/right padding. 352 int availablePaddingX = Math.max(0, width - (int) ((inv.numColumns * cellWidthPx) + 353 ((inv.numColumns - 1) * gapScale * cellWidthPx))); 354 availablePaddingX = (int) Math.min(availablePaddingX, 355 width * MAX_HORIZONTAL_PADDING_PERCENT); 356 int availablePaddingY = Math.max(0, height - paddingTop - paddingBottom 357 - (int) (2 * inv.numRows * cellHeightPx)); 358 padding.set(availablePaddingX / 2, paddingTop + availablePaddingY / 2, 359 availablePaddingX / 2, paddingBottom + availablePaddingY / 2); 360 } else { 361 // Pad the top and bottom of the workspace with search/hotseat bar sizes 362 padding.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left, 363 paddingTop, 364 desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.right, 365 paddingBottom); 366 } 367 } 368 return padding; 369 } 370 371 private int getWorkspacePageSpacing(boolean isLayoutRtl) { 372 if (isVerticalBarLayout() || isLargeTablet) { 373 // In landscape mode the page spacing is set to the default. 374 return defaultPageSpacingPx; 375 } else { 376 // In portrait, we want the pages spaced such that there is no 377 // overhang of the previous / next page into the current page viewport. 378 // We assume symmetrical padding in portrait mode. 379 return Math.max(defaultPageSpacingPx, 2 * getWorkspacePadding(isLayoutRtl).left); 380 } 381 } 382 383 int getOverviewModeButtonBarHeight() { 384 int zoneHeight = (int) (overviewModeIconZoneRatio * availableHeightPx); 385 zoneHeight = Math.min(overviewModeMaxIconZoneHeightPx, 386 Math.max(overviewModeMinIconZoneHeightPx, zoneHeight)); 387 return zoneHeight; 388 } 389 390 // The rect returned will be extended to below the system ui that covers the workspace 391 Rect getHotseatRect() { 392 if (isVerticalBarLayout()) { 393 return new Rect(availableWidthPx - normalHotseatBarHeightPx, 0, 394 Integer.MAX_VALUE, availableHeightPx); 395 } else { 396 return new Rect(0, availableHeightPx - hotseatBarHeightPx, 397 availableWidthPx, Integer.MAX_VALUE); 398 } 399 } 400 401 public static int calculateCellWidth(int width, int countX) { 402 return width / countX; 403 } 404 public static int calculateCellHeight(int height, int countY) { 405 return height / countY; 406 } 407 408 /** 409 * When {@code true}, the device is in landscape mode and the hotseat is on the right column. 410 * When {@code false}, either device is in portrait mode or the device is in landscape mode and 411 * the hotseat is on the bottom row. 412 */ 413 boolean isVerticalBarLayout() { 414 return isLandscape && transposeLayoutWithOrientation; 415 } 416 417 boolean shouldFadeAdjacentWorkspaceScreens() { 418 return isVerticalBarLayout() || isLargeTablet; 419 } 420 421 private int getVisibleChildCount(ViewGroup parent) { 422 int visibleChildren = 0; 423 for (int i = 0; i < parent.getChildCount(); i++) { 424 if (parent.getChildAt(i).getVisibility() != View.GONE) { 425 visibleChildren++; 426 } 427 } 428 return visibleChildren; 429 } 430 431 // TODO(twickham): b/25154513 432 public void setSearchBarHeight(int searchBarHeight) { 433 if (searchBarHeight == LauncherCallbacks.SEARCH_BAR_HEIGHT_TALL) { 434 hotseatBarHeightPx = shortHotseatBarHeightPx; 435 searchBarSpaceHeightPx = tallSearchBarSpaceHeightPx; 436 searchBarBottomPaddingPx = tallSearchBarBottomPaddingPx; 437 searchBarTopExtraPaddingPx = isPhone ? tallSearchBarNegativeTopPaddingPx 438 : normalSearchBarTopExtraPaddingPx; 439 } else { 440 hotseatBarHeightPx = normalHotseatBarHeightPx; 441 searchBarSpaceHeightPx = normalSearchBarSpaceHeightPx; 442 searchBarBottomPaddingPx = normalSearchBarBottomPaddingPx; 443 searchBarTopExtraPaddingPx = normalSearchBarTopExtraPaddingPx; 444 } 445 } 446 447 public void layout(Launcher launcher) { 448 FrameLayout.LayoutParams lp; 449 boolean hasVerticalBarLayout = isVerticalBarLayout(); 450 final boolean isLayoutRtl = Utilities.isRtl(launcher.getResources()); 451 452 // Layout the search bar space 453 Rect searchBarBounds = getSearchBarBounds(isLayoutRtl); 454 View searchBar = launcher.getSearchDropTargetBar(); 455 lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams(); 456 lp.width = searchBarBounds.width(); 457 lp.height = searchBarBounds.height(); 458 lp.topMargin = searchBarTopExtraPaddingPx; 459 if (hasVerticalBarLayout) { 460 // Vertical search bar space -- The search bar is fixed in the layout to be on the left 461 // of the screen regardless of RTL 462 lp.gravity = Gravity.LEFT; 463 464 LinearLayout targets = (LinearLayout) searchBar.findViewById(R.id.drag_target_bar); 465 targets.setOrientation(LinearLayout.VERTICAL); 466 FrameLayout.LayoutParams targetsLp = (FrameLayout.LayoutParams) targets.getLayoutParams(); 467 targetsLp.gravity = Gravity.TOP; 468 targetsLp.height = LayoutParams.WRAP_CONTENT; 469 470 } else { 471 // Horizontal search bar space 472 lp.gravity = Gravity.TOP|Gravity.CENTER_HORIZONTAL; 473 } 474 searchBar.setLayoutParams(lp); 475 476 // Layout the workspace 477 PagedView workspace = (PagedView) launcher.findViewById(R.id.workspace); 478 lp = (FrameLayout.LayoutParams) workspace.getLayoutParams(); 479 lp.gravity = Gravity.CENTER; 480 Rect padding = getWorkspacePadding(isLayoutRtl); 481 workspace.setLayoutParams(lp); 482 workspace.setPadding(padding.left, padding.top, padding.right, padding.bottom); 483 workspace.setPageSpacing(getWorkspacePageSpacing(isLayoutRtl)); 484 485 // Layout the hotseat 486 View hotseat = launcher.findViewById(R.id.hotseat); 487 lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams(); 488 // We want the edges of the hotseat to line up with the edges of the workspace, but the 489 // icons in the hotseat are a different size, and so don't line up perfectly. To account for 490 // this, we pad the left and right of the hotseat with half of the difference of a workspace 491 // cell vs a hotseat cell. 492 float workspaceCellWidth = (float) getCurrentWidth() / inv.numColumns; 493 float hotseatCellWidth = (float) getCurrentWidth() / inv.numHotseatIcons; 494 int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2); 495 if (hasVerticalBarLayout) { 496 // Vertical hotseat -- The hotseat is fixed in the layout to be on the right of the 497 // screen regardless of RTL 498 lp.gravity = Gravity.RIGHT; 499 lp.width = normalHotseatBarHeightPx; 500 lp.height = LayoutParams.MATCH_PARENT; 501 hotseat.findViewById(R.id.layout).setPadding(0, 2 * edgeMarginPx, 0, 2 * edgeMarginPx); 502 } else if (isTablet) { 503 // Pad the hotseat with the workspace padding calculated above 504 lp.gravity = Gravity.BOTTOM; 505 lp.width = LayoutParams.MATCH_PARENT; 506 lp.height = hotseatBarHeightPx; 507 hotseat.findViewById(R.id.layout).setPadding( 508 hotseatAdjustment + padding.left, 0, 509 hotseatAdjustment + padding.right, 2 * edgeMarginPx); 510 } else { 511 // For phones, layout the hotseat without any bottom margin 512 // to ensure that we have space for the folders 513 lp.gravity = Gravity.BOTTOM; 514 lp.width = LayoutParams.MATCH_PARENT; 515 lp.height = hotseatBarHeightPx; 516 hotseat.findViewById(R.id.layout).setPadding( 517 hotseatAdjustment + padding.left, 0, 518 hotseatAdjustment + padding.right, 0); 519 } 520 hotseat.setLayoutParams(lp); 521 522 // Layout the page indicators 523 View pageIndicator = launcher.findViewById(R.id.page_indicator); 524 if (pageIndicator != null) { 525 if (hasVerticalBarLayout) { 526 // Hide the page indicators when we have vertical search/hotseat 527 pageIndicator.setVisibility(View.GONE); 528 } else { 529 // Put the page indicators above the hotseat 530 lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams(); 531 lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; 532 lp.width = LayoutParams.WRAP_CONTENT; 533 lp.height = LayoutParams.WRAP_CONTENT; 534 lp.bottomMargin = hotseatBarHeightPx; 535 pageIndicator.setLayoutParams(lp); 536 } 537 } 538 539 // Layout the Overview Mode 540 ViewGroup overviewMode = launcher.getOverviewPanel(); 541 if (overviewMode != null) { 542 int overviewButtonBarHeight = getOverviewModeButtonBarHeight(); 543 lp = (FrameLayout.LayoutParams) overviewMode.getLayoutParams(); 544 lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; 545 546 int visibleChildCount = getVisibleChildCount(overviewMode); 547 int totalItemWidth = visibleChildCount * overviewModeBarItemWidthPx; 548 int maxWidth = totalItemWidth + (visibleChildCount-1) * overviewModeBarSpacerWidthPx; 549 550 lp.width = Math.min(availableWidthPx, maxWidth); 551 lp.height = overviewButtonBarHeight; 552 overviewMode.setLayoutParams(lp); 553 554 if (lp.width > totalItemWidth && visibleChildCount > 1) { 555 // We have enough space. Lets add some margin too. 556 int margin = (lp.width - totalItemWidth) / (visibleChildCount-1); 557 View lastChild = null; 558 559 // Set margin of all visible children except the last visible child 560 for (int i = 0; i < visibleChildCount; i++) { 561 if (lastChild != null) { 562 MarginLayoutParams clp = (MarginLayoutParams) lastChild.getLayoutParams(); 563 if (isLayoutRtl) { 564 clp.leftMargin = margin; 565 } else { 566 clp.rightMargin = margin; 567 } 568 lastChild.setLayoutParams(clp); 569 lastChild = null; 570 } 571 View thisChild = overviewMode.getChildAt(i); 572 if (thisChild.getVisibility() != View.GONE) { 573 lastChild = thisChild; 574 } 575 } 576 } 577 } 578 } 579 580 private int getCurrentWidth() { 581 return isLandscape 582 ? Math.max(widthPx, heightPx) 583 : Math.min(widthPx, heightPx); 584 } 585 586 private int getCurrentHeight() { 587 return isLandscape 588 ? Math.min(widthPx, heightPx) 589 : Math.max(widthPx, heightPx); 590 } 591 } 592