DeviceProfile.java revision 495f44d5fcd877786ffa5e8eea055e18f28e3d85
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 17package com.android.launcher3; 18 19import android.appwidget.AppWidgetHostView; 20import android.content.ComponentName; 21import android.content.Context; 22import android.content.res.Configuration; 23import android.content.res.Resources; 24import android.graphics.Paint; 25import android.graphics.Paint.FontMetrics; 26import android.graphics.Point; 27import android.graphics.PointF; 28import android.graphics.Rect; 29import android.util.DisplayMetrics; 30import android.view.Display; 31import android.view.Gravity; 32import android.view.Surface; 33import android.view.View; 34import android.view.ViewGroup.LayoutParams; 35import android.view.WindowManager; 36import android.widget.FrameLayout; 37 38import java.util.ArrayList; 39import java.util.Collections; 40import java.util.Comparator; 41 42 43class DeviceProfileQuery { 44 float widthDps; 45 float heightDps; 46 float value; 47 PointF dimens; 48 49 DeviceProfileQuery(float w, float h, float v) { 50 widthDps = w; 51 heightDps = h; 52 value = v; 53 dimens = new PointF(w, h); 54 } 55} 56 57public class DeviceProfile { 58 public static interface DeviceProfileCallbacks { 59 public void onAvailableSizeChanged(DeviceProfile grid); 60 } 61 62 String name; 63 float minWidthDps; 64 float minHeightDps; 65 float numRows; 66 float numColumns; 67 float numHotseatIcons; 68 private float iconSize; 69 private float iconTextSize; 70 private int iconDrawablePaddingOriginalPx; 71 private float hotseatIconSize; 72 73 boolean isLandscape; 74 boolean isTablet; 75 boolean isLargeTablet; 76 boolean transposeLayoutWithOrientation; 77 78 int desiredWorkspaceLeftRightMarginPx; 79 int edgeMarginPx; 80 Rect defaultWidgetPadding; 81 82 int widthPx; 83 int heightPx; 84 int availableWidthPx; 85 int availableHeightPx; 86 int defaultPageSpacingPx; 87 88 int overviewModeMinIconZoneHeightPx; 89 int overviewModeMaxIconZoneHeightPx; 90 int overviewModeMaxBarWidthPx; 91 float overviewModeIconZoneRatio; 92 float overviewModeScaleFactor; 93 94 int iconSizePx; 95 int iconTextSizePx; 96 int iconDrawablePaddingPx; 97 int cellWidthPx; 98 int cellHeightPx; 99 int allAppsIconSizePx; 100 int allAppsIconTextSizePx; 101 int allAppsCellWidthPx; 102 int allAppsCellHeightPx; 103 int allAppsCellPaddingPx; 104 int folderBackgroundOffset; 105 int folderIconSizePx; 106 int folderCellWidthPx; 107 int folderCellHeightPx; 108 int hotseatCellWidthPx; 109 int hotseatCellHeightPx; 110 int hotseatIconSizePx; 111 int hotseatBarHeightPx; 112 int hotseatAllAppsRank; 113 int allAppsNumRows; 114 int allAppsNumCols; 115 int searchBarSpaceWidthPx; 116 int searchBarSpaceMaxWidthPx; 117 int searchBarSpaceHeightPx; 118 int searchBarHeightPx; 119 int pageIndicatorHeightPx; 120 121 private ArrayList<DeviceProfileCallbacks> mCallbacks = new ArrayList<DeviceProfileCallbacks>(); 122 123 DeviceProfile(String n, float w, float h, float r, float c, 124 float is, float its, float hs, float his) { 125 // Ensure that we have an odd number of hotseat items (since we need to place all apps) 126 if (!AppsCustomizePagedView.DISABLE_ALL_APPS && hs % 2 == 0) { 127 throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces"); 128 } 129 130 name = n; 131 minWidthDps = w; 132 minHeightDps = h; 133 numRows = r; 134 numColumns = c; 135 iconSize = is; 136 iconTextSize = its; 137 numHotseatIcons = hs; 138 hotseatIconSize = his; 139 } 140 141 DeviceProfile(Context context, 142 ArrayList<DeviceProfile> profiles, 143 float minWidth, float minHeight, 144 int wPx, int hPx, 145 int awPx, int ahPx, 146 Resources res) { 147 DisplayMetrics dm = res.getDisplayMetrics(); 148 ArrayList<DeviceProfileQuery> points = 149 new ArrayList<DeviceProfileQuery>(); 150 transposeLayoutWithOrientation = 151 res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation); 152 minWidthDps = minWidth; 153 minHeightDps = minHeight; 154 155 ComponentName cn = new ComponentName(context.getPackageName(), 156 this.getClass().getName()); 157 defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null); 158 edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin); 159 desiredWorkspaceLeftRightMarginPx = 2 * edgeMarginPx; 160 pageIndicatorHeightPx = 161 res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height); 162 defaultPageSpacingPx = 163 res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing); 164 allAppsCellPaddingPx = 165 res.getDimensionPixelSize(R.dimen.dynamic_grid_all_apps_cell_padding); 166 overviewModeMinIconZoneHeightPx = 167 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_min_icon_zone_height); 168 overviewModeMaxIconZoneHeightPx = 169 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_max_icon_zone_height); 170 overviewModeMaxBarWidthPx = 171 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_max_width); 172 overviewModeIconZoneRatio = 173 res.getInteger(R.integer.config_dynamic_grid_overview_icon_zone_percentage) / 100f; 174 overviewModeScaleFactor = 175 res.getInteger(R.integer.config_dynamic_grid_overview_scale_percentage) / 100f; 176 177 // Interpolate the rows 178 for (DeviceProfile p : profiles) { 179 points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numRows)); 180 } 181 numRows = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points)); 182 // Interpolate the columns 183 points.clear(); 184 for (DeviceProfile p : profiles) { 185 points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numColumns)); 186 } 187 numColumns = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points)); 188 // Interpolate the hotseat length 189 points.clear(); 190 for (DeviceProfile p : profiles) { 191 points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numHotseatIcons)); 192 } 193 numHotseatIcons = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points)); 194 hotseatAllAppsRank = (int) (numHotseatIcons / 2); 195 196 // Interpolate the icon size 197 points.clear(); 198 for (DeviceProfile p : profiles) { 199 points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.iconSize)); 200 } 201 iconSize = invDistWeightedInterpolate(minWidth, minHeight, points); 202 // AllApps uses the original non-scaled icon size 203 allAppsIconSizePx = DynamicGrid.pxFromDp(iconSize, dm); 204 205 // Interpolate the icon text size 206 points.clear(); 207 for (DeviceProfile p : profiles) { 208 points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.iconTextSize)); 209 } 210 iconTextSize = invDistWeightedInterpolate(minWidth, minHeight, points); 211 iconDrawablePaddingOriginalPx = 212 res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding); 213 // AllApps uses the original non-scaled icon text size 214 allAppsIconTextSizePx = DynamicGrid.pxFromDp(iconTextSize, dm); 215 216 // Interpolate the hotseat icon size 217 points.clear(); 218 for (DeviceProfile p : profiles) { 219 points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.hotseatIconSize)); 220 } 221 // Hotseat 222 hotseatIconSize = invDistWeightedInterpolate(minWidth, minHeight, points); 223 224 // Calculate the remaining vars 225 updateFromConfiguration(context, res, wPx, hPx, awPx, ahPx); 226 updateAvailableDimensions(context); 227 } 228 229 void addCallback(DeviceProfileCallbacks cb) { 230 mCallbacks.add(cb); 231 cb.onAvailableSizeChanged(this); 232 } 233 void removeCallback(DeviceProfileCallbacks cb) { 234 mCallbacks.remove(cb); 235 } 236 237 private int getDeviceOrientation(Context context) { 238 WindowManager windowManager = (WindowManager) 239 context.getSystemService(Context.WINDOW_SERVICE); 240 Resources resources = context.getResources(); 241 DisplayMetrics dm = resources.getDisplayMetrics(); 242 Configuration config = resources.getConfiguration(); 243 int rotation = windowManager.getDefaultDisplay().getRotation(); 244 245 boolean isLandscape = (config.orientation == Configuration.ORIENTATION_LANDSCAPE) && 246 (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180); 247 boolean isRotatedPortrait = (config.orientation == Configuration.ORIENTATION_PORTRAIT) && 248 (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270); 249 if (isLandscape || isRotatedPortrait) { 250 return CellLayout.LANDSCAPE; 251 } else { 252 return CellLayout.PORTRAIT; 253 } 254 } 255 256 private void updateAvailableDimensions(Context context) { 257 WindowManager windowManager = (WindowManager) 258 context.getSystemService(Context.WINDOW_SERVICE); 259 Display display = windowManager.getDefaultDisplay(); 260 Resources resources = context.getResources(); 261 DisplayMetrics dm = resources.getDisplayMetrics(); 262 Configuration config = resources.getConfiguration(); 263 264 // There are three possible configurations that the dynamic grid accounts for, portrait, 265 // landscape with the nav bar at the bottom, and landscape with the nav bar at the side. 266 // To prevent waiting for fitSystemWindows(), we make the observation that in landscape, 267 // the height is the smallest height (either with the nav bar at the bottom or to the 268 // side) and otherwise, the height is simply the largest possible height for a portrait 269 // device. 270 Point size = new Point(); 271 Point smallestSize = new Point(); 272 Point largestSize = new Point(); 273 display.getSize(size); 274 display.getCurrentSizeRange(smallestSize, largestSize); 275 availableWidthPx = size.x; 276 if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) { 277 availableHeightPx = smallestSize.y; 278 } else { 279 availableHeightPx = largestSize.y; 280 } 281 282 // Check to see if the icons fit in the new available height. If not, then we need to 283 // shrink the icon size. 284 Rect workspacePadding = getWorkspacePadding(); 285 float scale = 1f; 286 int drawablePadding = iconDrawablePaddingOriginalPx; 287 updateIconSize(1f, drawablePadding, resources, dm); 288 float usedHeight = (cellHeightPx * numRows); 289 int maxHeight = (availableHeightPx - workspacePadding.top - workspacePadding.bottom); 290 if (usedHeight > maxHeight) { 291 scale = maxHeight / usedHeight; 292 drawablePadding = 0; 293 } 294 updateIconSize(scale, drawablePadding, resources, dm); 295 296 // Make the callbacks 297 for (DeviceProfileCallbacks cb : mCallbacks) { 298 cb.onAvailableSizeChanged(this); 299 } 300 } 301 302 private void updateIconSize(float scale, int drawablePadding, Resources resources, 303 DisplayMetrics dm) { 304 iconSizePx = (int) (DynamicGrid.pxFromDp(iconSize, dm) * scale); 305 iconTextSizePx = (int) (DynamicGrid.pxFromSp(iconTextSize, dm) * scale); 306 iconDrawablePaddingPx = drawablePadding; 307 hotseatIconSizePx = (int) (DynamicGrid.pxFromDp(hotseatIconSize, dm) * scale); 308 309 // Search Bar 310 searchBarSpaceMaxWidthPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_max_width); 311 searchBarHeightPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_height); 312 searchBarSpaceWidthPx = Math.min(searchBarSpaceMaxWidthPx, widthPx); 313 searchBarSpaceHeightPx = searchBarHeightPx + getSearchBarTopOffset(); 314 315 // Calculate the actual text height 316 Paint textPaint = new Paint(); 317 textPaint.setTextSize(iconTextSizePx); 318 FontMetrics fm = textPaint.getFontMetrics(); 319 cellWidthPx = iconSizePx; 320 cellHeightPx = iconSizePx + iconDrawablePaddingPx + (int) Math.ceil(fm.bottom - fm.top); 321 322 // Hotseat 323 hotseatBarHeightPx = iconSizePx + 4 * edgeMarginPx; 324 hotseatCellWidthPx = iconSizePx; 325 hotseatCellHeightPx = iconSizePx; 326 327 // Folder 328 folderCellWidthPx = cellWidthPx + 3 * edgeMarginPx; 329 folderCellHeightPx = cellHeightPx + edgeMarginPx; 330 folderBackgroundOffset = -edgeMarginPx; 331 folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset; 332 333 // All Apps 334 Rect padding = getWorkspacePadding(isLandscape ? 335 CellLayout.LANDSCAPE : CellLayout.PORTRAIT); 336 int pageIndicatorOffset = 337 resources.getDimensionPixelSize(R.dimen.apps_customize_page_indicator_offset); 338 allAppsCellWidthPx = allAppsIconSizePx; 339 allAppsCellHeightPx = allAppsIconSizePx + drawablePadding + iconTextSizePx; 340 int maxLongEdgeCellCount = 341 resources.getInteger(R.integer.config_dynamic_grid_max_long_edge_cell_count); 342 int maxShortEdgeCellCount = 343 resources.getInteger(R.integer.config_dynamic_grid_max_short_edge_cell_count); 344 int minEdgeCellCount = 345 resources.getInteger(R.integer.config_dynamic_grid_min_edge_cell_count); 346 int maxRows = (isLandscape ? maxShortEdgeCellCount : maxLongEdgeCellCount); 347 int maxCols = (isLandscape ? maxLongEdgeCellCount : maxShortEdgeCellCount); 348 349 allAppsNumRows = (availableHeightPx - pageIndicatorHeightPx) / 350 (allAppsCellHeightPx + allAppsCellPaddingPx); 351 allAppsNumRows = Math.max(minEdgeCellCount, Math.min(maxRows, allAppsNumRows)); 352 allAppsNumCols = (availableWidthPx) / 353 (allAppsCellWidthPx + allAppsCellPaddingPx); 354 allAppsNumCols = Math.max(minEdgeCellCount, Math.min(maxCols, allAppsNumCols)); 355 } 356 357 void updateFromConfiguration(Context context, Resources resources, int wPx, int hPx, 358 int awPx, int ahPx) { 359 isLandscape = (resources.getConfiguration().orientation == 360 Configuration.ORIENTATION_LANDSCAPE); 361 isTablet = resources.getBoolean(R.bool.is_tablet); 362 isLargeTablet = resources.getBoolean(R.bool.is_large_tablet); 363 widthPx = wPx; 364 heightPx = hPx; 365 availableWidthPx = awPx; 366 availableHeightPx = ahPx; 367 368 updateAvailableDimensions(context); 369 } 370 371 private float dist(PointF p0, PointF p1) { 372 return (float) Math.sqrt((p1.x - p0.x)*(p1.x-p0.x) + 373 (p1.y-p0.y)*(p1.y-p0.y)); 374 } 375 376 private float weight(PointF a, PointF b, 377 float pow) { 378 float d = dist(a, b); 379 if (d == 0f) { 380 return Float.POSITIVE_INFINITY; 381 } 382 return (float) (1f / Math.pow(d, pow)); 383 } 384 385 private float invDistWeightedInterpolate(float width, float height, 386 ArrayList<DeviceProfileQuery> points) { 387 float sum = 0; 388 float weights = 0; 389 float pow = 5; 390 float kNearestNeighbors = 3; 391 final PointF xy = new PointF(width, height); 392 393 ArrayList<DeviceProfileQuery> pointsByNearness = points; 394 Collections.sort(pointsByNearness, new Comparator<DeviceProfileQuery>() { 395 public int compare(DeviceProfileQuery a, DeviceProfileQuery b) { 396 return (int) (dist(xy, a.dimens) - dist(xy, b.dimens)); 397 } 398 }); 399 400 for (int i = 0; i < pointsByNearness.size(); ++i) { 401 DeviceProfileQuery p = pointsByNearness.get(i); 402 if (i < kNearestNeighbors) { 403 float w = weight(xy, p.dimens, pow); 404 if (w == Float.POSITIVE_INFINITY) { 405 return p.value; 406 } 407 weights += w; 408 } 409 } 410 411 for (int i = 0; i < pointsByNearness.size(); ++i) { 412 DeviceProfileQuery p = pointsByNearness.get(i); 413 if (i < kNearestNeighbors) { 414 float w = weight(xy, p.dimens, pow); 415 sum += w * p.value / weights; 416 } 417 } 418 419 return sum; 420 } 421 422 /** Returns the search bar top offset */ 423 int getSearchBarTopOffset() { 424 if (isTablet() && !isVerticalBarLayout()) { 425 return 4 * edgeMarginPx; 426 } else { 427 return 2 * edgeMarginPx; 428 } 429 } 430 431 /** Returns the search bar bounds in the current orientation */ 432 Rect getSearchBarBounds() { 433 return getSearchBarBounds(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT); 434 } 435 /** Returns the search bar bounds in the specified orientation */ 436 Rect getSearchBarBounds(int orientation) { 437 Rect bounds = new Rect(); 438 if (orientation == CellLayout.LANDSCAPE && 439 transposeLayoutWithOrientation) { 440 bounds.set(0, edgeMarginPx, searchBarSpaceHeightPx, availableHeightPx - edgeMarginPx); 441 } else { 442 if (isTablet()) { 443 // Pad the left and right of the workspace to ensure consistent spacing 444 // between all icons 445 int width = (orientation == CellLayout.LANDSCAPE) 446 ? Math.max(widthPx, heightPx) 447 : Math.min(widthPx, heightPx); 448 // XXX: If the icon size changes across orientations, we will have to take 449 // that into account here too. 450 int gap = (int) ((width - 2 * edgeMarginPx - 451 (numColumns * cellWidthPx)) / (2 * (numColumns + 1))); 452 bounds.set(edgeMarginPx + gap, getSearchBarTopOffset(), 453 availableWidthPx - (edgeMarginPx + gap), 454 searchBarSpaceHeightPx); 455 } else { 456 bounds.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left, 457 getSearchBarTopOffset(), 458 availableWidthPx - (desiredWorkspaceLeftRightMarginPx - 459 defaultWidgetPadding.right), searchBarSpaceHeightPx); 460 } 461 } 462 return bounds; 463 } 464 465 /** Returns the workspace padding in the specified orientation */ 466 Rect getWorkspacePadding() { 467 return getWorkspacePadding(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT); 468 } 469 Rect getWorkspacePadding(int orientation) { 470 Rect searchBarBounds = getSearchBarBounds(orientation); 471 Rect padding = new Rect(); 472 if (orientation == CellLayout.LANDSCAPE && 473 transposeLayoutWithOrientation) { 474 // Pad the left and right of the workspace with search/hotseat bar sizes 475 padding.set(searchBarBounds.right, edgeMarginPx, 476 hotseatBarHeightPx, edgeMarginPx); 477 } else { 478 if (isTablet()) { 479 // Pad the left and right of the workspace to ensure consistent spacing 480 // between all icons 481 int width = (orientation == CellLayout.LANDSCAPE) 482 ? Math.max(widthPx, heightPx) 483 : Math.min(widthPx, heightPx); 484 // XXX: If the icon size changes across orientations, we will have to take 485 // that into account here too. 486 int gap = (int) ((width - 2 * edgeMarginPx - 487 (numColumns * cellWidthPx)) / (2 * (numColumns + 1))); 488 padding.set(edgeMarginPx + gap, 489 searchBarBounds.bottom, 490 edgeMarginPx + gap, 491 hotseatBarHeightPx + pageIndicatorHeightPx); 492 } else { 493 // Pad the top and bottom of the workspace with search/hotseat bar sizes 494 padding.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left, 495 searchBarBounds.bottom, 496 desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.right, 497 hotseatBarHeightPx + pageIndicatorHeightPx); 498 } 499 } 500 return padding; 501 } 502 503 int getWorkspacePageSpacing(int orientation) { 504 if (orientation == CellLayout.LANDSCAPE && 505 transposeLayoutWithOrientation) { 506 // In landscape mode the page spacing is set to the default. 507 return defaultPageSpacingPx; 508 } else { 509 // In portrait, we want the pages spaced such that there is no 510 // overhang of the previous / next page into the current page viewport. 511 // We assume symmetrical padding in portrait mode. 512 return 2 * getWorkspacePadding().left; 513 } 514 } 515 516 Rect getOverviewModeButtonBarRect() { 517 int zoneHeight = (int) (overviewModeIconZoneRatio * availableHeightPx); 518 zoneHeight = Math.min(overviewModeMaxIconZoneHeightPx, 519 Math.max(overviewModeMinIconZoneHeightPx, zoneHeight)); 520 return new Rect(0, availableHeightPx - zoneHeight, 0, availableHeightPx); 521 } 522 523 float getOverviewModeScale() { 524 Rect workspacePadding = getWorkspacePadding(); 525 Rect overviewBar = getOverviewModeButtonBarRect(); 526 int pageSpace = availableHeightPx - workspacePadding.top - workspacePadding.bottom; 527 return (overviewModeScaleFactor * (pageSpace - overviewBar.height())) / pageSpace; 528 } 529 530 // The rect returned will be extended to below the system ui that covers the workspace 531 Rect getHotseatRect() { 532 if (isVerticalBarLayout()) { 533 return new Rect(availableWidthPx - hotseatBarHeightPx, 0, 534 Integer.MAX_VALUE, availableHeightPx); 535 } else { 536 return new Rect(0, availableHeightPx - hotseatBarHeightPx, 537 availableWidthPx, Integer.MAX_VALUE); 538 } 539 } 540 541 int calculateCellWidth(int width, int countX) { 542 return width / countX; 543 } 544 int calculateCellHeight(int height, int countY) { 545 return height / countY; 546 } 547 548 boolean isPhone() { 549 return !isTablet && !isLargeTablet; 550 } 551 boolean isTablet() { 552 return isTablet; 553 } 554 boolean isLargeTablet() { 555 return isLargeTablet; 556 } 557 558 boolean isVerticalBarLayout() { 559 return isLandscape && transposeLayoutWithOrientation; 560 } 561 562 boolean shouldFadeAdjacentWorkspaceScreens() { 563 return isVerticalBarLayout() || isLargeTablet(); 564 } 565 566 public void layout(Launcher launcher) { 567 FrameLayout.LayoutParams lp; 568 Resources res = launcher.getResources(); 569 boolean hasVerticalBarLayout = isVerticalBarLayout(); 570 571 // Layout the search bar space 572 View searchBar = launcher.getSearchBar(); 573 lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams(); 574 if (hasVerticalBarLayout) { 575 // Vertical search bar space 576 lp.gravity = Gravity.TOP | Gravity.LEFT; 577 lp.width = searchBarSpaceHeightPx; 578 lp.height = LayoutParams.MATCH_PARENT; 579 searchBar.setPadding( 580 0, 2 * edgeMarginPx, 0, 581 2 * edgeMarginPx); 582 } else { 583 // Horizontal search bar space 584 lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; 585 lp.width = searchBarSpaceWidthPx; 586 lp.height = searchBarSpaceHeightPx; 587 searchBar.setPadding( 588 2 * edgeMarginPx, 589 getSearchBarTopOffset(), 590 2 * edgeMarginPx, 0); 591 } 592 searchBar.setLayoutParams(lp); 593 594 // Layout the search bar 595 View qsbBar = launcher.getQsbBar(); 596 LayoutParams vglp = qsbBar.getLayoutParams(); 597 vglp.width = LayoutParams.MATCH_PARENT; 598 vglp.height = LayoutParams.MATCH_PARENT; 599 qsbBar.setLayoutParams(vglp); 600 601 // Layout the voice proxy 602 View voiceButtonProxy = launcher.findViewById(R.id.voice_button_proxy); 603 if (voiceButtonProxy != null) { 604 if (hasVerticalBarLayout) { 605 // TODO: MOVE THIS INTO SEARCH BAR MEASURE 606 } else { 607 lp = (FrameLayout.LayoutParams) voiceButtonProxy.getLayoutParams(); 608 lp.gravity = Gravity.TOP | Gravity.END; 609 lp.width = (widthPx - searchBarSpaceWidthPx) / 2 + 610 2 * iconSizePx; 611 lp.height = searchBarSpaceHeightPx; 612 } 613 } 614 615 // Layout the workspace 616 PagedView workspace = (PagedView) launcher.findViewById(R.id.workspace); 617 lp = (FrameLayout.LayoutParams) workspace.getLayoutParams(); 618 lp.gravity = Gravity.CENTER; 619 int orientation = isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT; 620 Rect padding = getWorkspacePadding(orientation); 621 workspace.setLayoutParams(lp); 622 workspace.setPadding(padding.left, padding.top, padding.right, padding.bottom); 623 workspace.setPageSpacing(getWorkspacePageSpacing(orientation)); 624 625 // Layout the hotseat 626 View hotseat = launcher.findViewById(R.id.hotseat); 627 lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams(); 628 if (hasVerticalBarLayout) { 629 // Vertical hotseat 630 lp.gravity = Gravity.RIGHT; 631 lp.width = hotseatBarHeightPx; 632 lp.height = LayoutParams.MATCH_PARENT; 633 hotseat.findViewById(R.id.layout).setPadding(0, 2 * edgeMarginPx, 0, 2 * edgeMarginPx); 634 } else if (isTablet()) { 635 // Pad the hotseat with the grid gap calculated above 636 int gridGap = (int) ((widthPx - 2 * edgeMarginPx - 637 (numColumns * cellWidthPx)) / (2 * (numColumns + 1))); 638 int gridWidth = (int) ((numColumns * cellWidthPx) + 639 ((numColumns - 1) * gridGap)); 640 int hotseatGap = (int) Math.max(0, 641 (gridWidth - (numHotseatIcons * hotseatCellWidthPx)) 642 / (numHotseatIcons - 1)); 643 lp.gravity = Gravity.BOTTOM; 644 lp.width = LayoutParams.MATCH_PARENT; 645 lp.height = hotseatBarHeightPx; 646 hotseat.setPadding(2 * edgeMarginPx + gridGap + hotseatGap, 0, 647 2 * edgeMarginPx + gridGap + hotseatGap, 648 2 * edgeMarginPx); 649 } else { 650 // For phones, layout the hotseat without any bottom margin 651 // to ensure that we have space for the folders 652 lp.gravity = Gravity.BOTTOM; 653 lp.width = LayoutParams.MATCH_PARENT; 654 lp.height = hotseatBarHeightPx; 655 hotseat.findViewById(R.id.layout).setPadding(2 * edgeMarginPx, 0, 656 2 * edgeMarginPx, 0); 657 } 658 hotseat.setLayoutParams(lp); 659 660 // Layout the page indicators 661 View pageIndicator = launcher.findViewById(R.id.page_indicator); 662 if (pageIndicator != null) { 663 if (hasVerticalBarLayout) { 664 // Hide the page indicators when we have vertical search/hotseat 665 pageIndicator.setVisibility(View.GONE); 666 } else { 667 // Put the page indicators above the hotseat 668 lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams(); 669 lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; 670 lp.width = LayoutParams.WRAP_CONTENT; 671 lp.height = LayoutParams.WRAP_CONTENT; 672 lp.bottomMargin = hotseatBarHeightPx; 673 pageIndicator.setLayoutParams(lp); 674 } 675 } 676 677 // Layout AllApps 678 AppsCustomizeTabHost host = (AppsCustomizeTabHost) 679 launcher.findViewById(R.id.apps_customize_pane); 680 if (host != null) { 681 // Center the all apps page indicator 682 int pageIndicatorHeight = (int) (pageIndicatorHeightPx * Math.min(1f, 683 (allAppsIconSizePx / DynamicGrid.DEFAULT_ICON_SIZE_PX))); 684 pageIndicator = host.findViewById(R.id.apps_customize_page_indicator); 685 if (pageIndicator != null) { 686 lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams(); 687 lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; 688 lp.width = LayoutParams.WRAP_CONTENT; 689 lp.height = pageIndicatorHeight; 690 pageIndicator.setLayoutParams(lp); 691 } 692 693 AppsCustomizePagedView pagedView = (AppsCustomizePagedView) 694 host.findViewById(R.id.apps_customize_pane_content); 695 padding = new Rect(); 696 if (pagedView != null) { 697 // Constrain the dimensions of all apps so that it does not span the full width 698 int paddingLR = (availableWidthPx - (allAppsCellWidthPx * allAppsNumCols)) / 699 (2 * (allAppsNumCols + 1)); 700 int paddingTB = (availableHeightPx - (allAppsCellHeightPx * allAppsNumRows)) / 701 (2 * (allAppsNumRows + 1)); 702 paddingLR = Math.min(paddingLR, (int)((paddingLR + paddingTB) * 0.75f)); 703 paddingTB = Math.min(paddingTB, (int)((paddingLR + paddingTB) * 0.75f)); 704 int maxAllAppsWidth = (allAppsNumCols * (allAppsCellWidthPx + 2 * paddingLR)); 705 int gridPaddingLR = (availableWidthPx - maxAllAppsWidth) / 2; 706 // Only adjust the side paddings on landscape phones, or tablets 707 if ((isTablet() || isLandscape) && gridPaddingLR > (allAppsCellWidthPx / 4)) { 708 padding.left = padding.right = gridPaddingLR; 709 } 710 // The icons are centered, so we can't just offset by the page indicator height 711 // because the empty space will actually be pageIndicatorHeight + paddingTB 712 padding.bottom = Math.max(0, pageIndicatorHeight - paddingTB); 713 pagedView.setAllAppsPadding(padding); 714 pagedView.setWidgetsPageIndicatorPadding(pageIndicatorHeight); 715 } 716 } 717 718 // Layout the Overview Mode 719 View overviewMode = launcher.getOverviewPanel(); 720 if (overviewMode != null) { 721 Rect r = getOverviewModeButtonBarRect(); 722 lp = (FrameLayout.LayoutParams) overviewMode.getLayoutParams(); 723 lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; 724 lp.width = Math.min(availableWidthPx, overviewModeMaxBarWidthPx); 725 lp.height = r.height(); 726 overviewMode.setLayoutParams(lp); 727 } 728 } 729} 730