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 { onLauncherLayoutChanged()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 DeviceProfile(Context context, InvariantDeviceProfile inv, Point minSize, Point maxSize, int width, int height, boolean isLandscape)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 addLauncherLayoutChangedListener(LauncherLayoutChangeListener listener)208 public void addLauncherLayoutChangedListener(LauncherLayoutChangeListener listener) { 209 if (!mListeners.contains(listener)) { 210 mListeners.add(listener); 211 } 212 } 213 removeLauncherLayoutChangedListener(LauncherLayoutChangeListener listener)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 */ computeAllAppsButtonSize(Context context)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 updateAvailableDimensions(DisplayMetrics dm, Resources res)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 updateIconSize(float scale, int drawablePadding, Resources res, DisplayMetrics dm)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 updateInsets(Rect insets)305 public void updateInsets(Rect insets) { 306 mInsets.set(insets); 307 } 308 updateAppsViewNumCols()309 public void updateAppsViewNumCols() { 310 allAppsNumCols = allAppsNumPredictiveCols = inv.numColumns; 311 } 312 313 /** Returns the width and height of the search bar, ignoring any padding. */ getSearchBarDimensForWidgetOpts()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 getCellSize()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 getTotalWorkspacePadding()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 */ getWorkspacePadding(Rect recycle)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 */ getAbsoluteOpenFolderBounds()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 getWorkspacePageSpacing()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 getOverviewModeButtonBarHeight()426 int getOverviewModeButtonBarHeight() { 427 int zoneHeight = (int) (overviewModeIconZoneRatio * availableHeightPx); 428 zoneHeight = Math.min(overviewModeMaxIconZoneHeightPx, 429 Math.max(overviewModeMinIconZoneHeightPx, zoneHeight)); 430 return zoneHeight; 431 } 432 calculateCellWidth(int width, int countX)433 public static int calculateCellWidth(int width, int countX) { 434 return width / countX; 435 } calculateCellHeight(int height, int countY)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 */ isVerticalBarLayout()445 public boolean isVerticalBarLayout() { 446 return isLandscape && transposeLayoutWithOrientation; 447 } 448 shouldFadeAdjacentWorkspaceScreens()449 boolean shouldFadeAdjacentWorkspaceScreens() { 450 return isVerticalBarLayout() || isLargeTablet; 451 } 452 getVisibleChildCount(ViewGroup parent)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 layout(Launcher launcher, boolean notifyListeners)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 getCurrentWidth()574 private int getCurrentWidth() { 575 return isLandscape 576 ? Math.max(widthPx, heightPx) 577 : Math.min(widthPx, heightPx); 578 } 579 getCurrentHeight()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 */ getContainerPadding(Context context)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