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 DeviceProfile(Context context, InvariantDeviceProfile inv, Point minSize, Point maxSize, int width, int height, boolean isLandscape)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 */ computeAllAppsButtonSize(Context context)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 updateAvailableDimensions(DisplayMetrics dm, Resources res)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 updateIconSize(float scale, int drawablePadding, Resources res, DisplayMetrics dm)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 */ updateAppsViewNumCols(Resources res, int recyclerViewWidth)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. */ getSearchBarTotalVerticalPadding()281 private int getSearchBarTotalVerticalPadding() { 282 return searchBarTopPaddingPx + searchBarTopExtraPaddingPx + searchBarBottomPaddingPx; 283 } 284 285 /** Returns the width and height of the search bar, ignoring any padding. */ getSearchBarDimensForWidgetOpts(Resources res)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 */ getSearchBarBounds(boolean isLayoutRtl)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 */ getWorkspacePadding(boolean isLayoutRtl)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 getWorkspacePageSpacing(boolean isLayoutRtl)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 getOverviewModeButtonBarHeight()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 getHotseatRect()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 calculateCellWidth(int width, int countX)401 public static int calculateCellWidth(int width, int countX) { 402 return width / countX; 403 } calculateCellHeight(int height, int countY)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 */ isVerticalBarLayout()413 boolean isVerticalBarLayout() { 414 return isLandscape && transposeLayoutWithOrientation; 415 } 416 shouldFadeAdjacentWorkspaceScreens()417 boolean shouldFadeAdjacentWorkspaceScreens() { 418 return isVerticalBarLayout() || isLargeTablet; 419 } 420 getVisibleChildCount(ViewGroup parent)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 setSearchBarHeight(int searchBarHeight)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 layout(Launcher launcher)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 getCurrentWidth()580 private int getCurrentWidth() { 581 return isLandscape 582 ? Math.max(widthPx, heightPx) 583 : Math.min(widthPx, heightPx); 584 } 585 getCurrentHeight()586 private int getCurrentHeight() { 587 return isLandscape 588 ? Math.min(widthPx, heightPx) 589 : Math.max(widthPx, heightPx); 590 } 591 } 592