DeviceProfile.java revision b38002419dcb456b51f5d320b224737f16a07088
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 + 2 * edgeMarginPx; 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 bounds in the current orientation */ 423 Rect getSearchBarBounds() { 424 return getSearchBarBounds(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT); 425 } 426 /** Returns the search bar bounds in the specified orientation */ 427 Rect getSearchBarBounds(int orientation) { 428 Rect bounds = new Rect(); 429 if (orientation == CellLayout.LANDSCAPE && 430 transposeLayoutWithOrientation) { 431 bounds.set(0, edgeMarginPx, searchBarSpaceHeightPx, availableHeightPx - edgeMarginPx); 432 } else { 433 if (isTablet()) { 434 // Pad the left and right of the workspace to ensure consistent spacing 435 // between all icons 436 int width = (orientation == CellLayout.LANDSCAPE) 437 ? Math.max(widthPx, heightPx) 438 : Math.min(widthPx, heightPx); 439 // XXX: If the icon size changes across orientations, we will have to take 440 // that into account here too. 441 int gap = (int) ((width - 2 * edgeMarginPx - 442 (numColumns * cellWidthPx)) / (2 * (numColumns + 1))); 443 bounds.set(edgeMarginPx + gap, 0, availableWidthPx - (edgeMarginPx + gap), 444 searchBarSpaceHeightPx); 445 } else { 446 bounds.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left, 0, 447 availableWidthPx - (desiredWorkspaceLeftRightMarginPx - 448 defaultWidgetPadding.right), searchBarSpaceHeightPx); 449 } 450 } 451 return bounds; 452 } 453 454 /** Returns the workspace padding in the specified orientation */ 455 Rect getWorkspacePadding() { 456 return getWorkspacePadding(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT); 457 } 458 Rect getWorkspacePadding(int orientation) { 459 Rect searchBarBounds = getSearchBarBounds(orientation); 460 Rect padding = new Rect(); 461 if (orientation == CellLayout.LANDSCAPE && 462 transposeLayoutWithOrientation) { 463 // Pad the left and right of the workspace with search/hotseat bar sizes 464 padding.set(searchBarBounds.right, edgeMarginPx, 465 hotseatBarHeightPx, edgeMarginPx); 466 } else { 467 if (isTablet()) { 468 // Pad the left and right of the workspace to ensure consistent spacing 469 // between all icons 470 int width = (orientation == CellLayout.LANDSCAPE) 471 ? Math.max(widthPx, heightPx) 472 : Math.min(widthPx, heightPx); 473 // XXX: If the icon size changes across orientations, we will have to take 474 // that into account here too. 475 int gap = (int) ((width - 2 * edgeMarginPx - 476 (numColumns * cellWidthPx)) / (2 * (numColumns + 1))); 477 padding.set(edgeMarginPx + gap, 478 searchBarBounds.bottom, 479 edgeMarginPx + gap, 480 hotseatBarHeightPx + pageIndicatorHeightPx); 481 } else { 482 // Pad the top and bottom of the workspace with search/hotseat bar sizes 483 padding.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left, 484 searchBarBounds.bottom, 485 desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.right, 486 hotseatBarHeightPx + pageIndicatorHeightPx); 487 } 488 } 489 return padding; 490 } 491 492 int getWorkspacePageSpacing(int orientation) { 493 if (orientation == CellLayout.LANDSCAPE && 494 transposeLayoutWithOrientation) { 495 // In landscape mode the page spacing is set to the default. 496 return defaultPageSpacingPx; 497 } else { 498 // In portrait, we want the pages spaced such that there is no 499 // overhang of the previous / next page into the current page viewport. 500 // We assume symmetrical padding in portrait mode. 501 return 2 * getWorkspacePadding().left; 502 } 503 } 504 505 Rect getOverviewModeButtonBarRect() { 506 int zoneHeight = (int) (overviewModeIconZoneRatio * availableHeightPx); 507 zoneHeight = Math.min(overviewModeMaxIconZoneHeightPx, 508 Math.max(overviewModeMinIconZoneHeightPx, zoneHeight)); 509 return new Rect(0, availableHeightPx - zoneHeight, 0, availableHeightPx); 510 } 511 512 float getOverviewModeScale() { 513 Rect workspacePadding = getWorkspacePadding(); 514 Rect overviewBar = getOverviewModeButtonBarRect(); 515 int pageSpace = availableHeightPx - workspacePadding.top - workspacePadding.bottom; 516 return (overviewModeScaleFactor * (pageSpace - overviewBar.height())) / pageSpace; 517 } 518 519 // The rect returned will be extended to below the system ui that covers the workspace 520 Rect getHotseatRect() { 521 if (isVerticalBarLayout()) { 522 return new Rect(availableWidthPx - hotseatBarHeightPx, 0, 523 Integer.MAX_VALUE, availableHeightPx); 524 } else { 525 return new Rect(0, availableHeightPx - hotseatBarHeightPx, 526 availableWidthPx, Integer.MAX_VALUE); 527 } 528 } 529 530 int calculateCellWidth(int width, int countX) { 531 return width / countX; 532 } 533 int calculateCellHeight(int height, int countY) { 534 return height / countY; 535 } 536 537 boolean isPhone() { 538 return !isTablet && !isLargeTablet; 539 } 540 boolean isTablet() { 541 return isTablet; 542 } 543 boolean isLargeTablet() { 544 return isLargeTablet; 545 } 546 547 boolean isVerticalBarLayout() { 548 return isLandscape && transposeLayoutWithOrientation; 549 } 550 551 boolean shouldFadeAdjacentWorkspaceScreens() { 552 return isVerticalBarLayout() || isLargeTablet(); 553 } 554 555 public void layout(Launcher launcher) { 556 FrameLayout.LayoutParams lp; 557 Resources res = launcher.getResources(); 558 boolean hasVerticalBarLayout = isVerticalBarLayout(); 559 560 // Layout the search bar space 561 View searchBar = launcher.getSearchBar(); 562 lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams(); 563 if (hasVerticalBarLayout) { 564 // Vertical search bar 565 lp.gravity = Gravity.TOP | Gravity.LEFT; 566 lp.width = searchBarSpaceHeightPx; 567 lp.height = LayoutParams.MATCH_PARENT; 568 searchBar.setPadding( 569 0, 2 * edgeMarginPx, 0, 570 2 * edgeMarginPx); 571 } else { 572 // Horizontal search bar 573 lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; 574 lp.width = searchBarSpaceWidthPx; 575 lp.height = searchBarSpaceHeightPx; 576 searchBar.setPadding( 577 2 * edgeMarginPx, 578 2 * edgeMarginPx, 579 2 * edgeMarginPx, 0); 580 } 581 searchBar.setLayoutParams(lp); 582 583 // Layout the search bar 584 View qsbBar = launcher.getQsbBar(); 585 LayoutParams vglp = qsbBar.getLayoutParams(); 586 vglp.width = LayoutParams.MATCH_PARENT; 587 vglp.height = LayoutParams.MATCH_PARENT; 588 qsbBar.setLayoutParams(vglp); 589 590 // Layout the voice proxy 591 View voiceButtonProxy = launcher.findViewById(R.id.voice_button_proxy); 592 if (voiceButtonProxy != null) { 593 if (hasVerticalBarLayout) { 594 // TODO: MOVE THIS INTO SEARCH BAR MEASURE 595 } else { 596 lp = (FrameLayout.LayoutParams) voiceButtonProxy.getLayoutParams(); 597 lp.gravity = Gravity.TOP | Gravity.END; 598 lp.width = (widthPx - searchBarSpaceWidthPx) / 2 + 599 2 * iconSizePx; 600 lp.height = searchBarSpaceHeightPx; 601 } 602 } 603 604 // Layout the workspace 605 PagedView workspace = (PagedView) launcher.findViewById(R.id.workspace); 606 lp = (FrameLayout.LayoutParams) workspace.getLayoutParams(); 607 lp.gravity = Gravity.CENTER; 608 int orientation = isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT; 609 Rect padding = getWorkspacePadding(orientation); 610 workspace.setLayoutParams(lp); 611 workspace.setPadding(padding.left, padding.top, padding.right, padding.bottom); 612 workspace.setPageSpacing(getWorkspacePageSpacing(orientation)); 613 614 // Layout the hotseat 615 View hotseat = launcher.findViewById(R.id.hotseat); 616 lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams(); 617 if (hasVerticalBarLayout) { 618 // Vertical hotseat 619 lp.gravity = Gravity.RIGHT; 620 lp.width = hotseatBarHeightPx; 621 lp.height = LayoutParams.MATCH_PARENT; 622 hotseat.findViewById(R.id.layout).setPadding(0, 2 * edgeMarginPx, 0, 2 * edgeMarginPx); 623 } else if (isTablet()) { 624 // Pad the hotseat with the grid gap calculated above 625 int gridGap = (int) ((widthPx - 2 * edgeMarginPx - 626 (numColumns * cellWidthPx)) / (2 * (numColumns + 1))); 627 int gridWidth = (int) ((numColumns * cellWidthPx) + 628 ((numColumns - 1) * gridGap)); 629 int hotseatGap = (int) Math.max(0, 630 (gridWidth - (numHotseatIcons * hotseatCellWidthPx)) 631 / (numHotseatIcons - 1)); 632 lp.gravity = Gravity.BOTTOM; 633 lp.width = LayoutParams.MATCH_PARENT; 634 lp.height = hotseatBarHeightPx; 635 hotseat.setPadding(2 * edgeMarginPx + gridGap + hotseatGap, 0, 636 2 * edgeMarginPx + gridGap + hotseatGap, 637 2 * edgeMarginPx); 638 } else { 639 // For phones, layout the hotseat without any bottom margin 640 // to ensure that we have space for the folders 641 lp.gravity = Gravity.BOTTOM; 642 lp.width = LayoutParams.MATCH_PARENT; 643 lp.height = hotseatBarHeightPx; 644 hotseat.findViewById(R.id.layout).setPadding(2 * edgeMarginPx, 0, 645 2 * edgeMarginPx, 0); 646 } 647 hotseat.setLayoutParams(lp); 648 649 // Layout the page indicators 650 View pageIndicator = launcher.findViewById(R.id.page_indicator); 651 if (pageIndicator != null) { 652 if (hasVerticalBarLayout) { 653 // Hide the page indicators when we have vertical search/hotseat 654 pageIndicator.setVisibility(View.GONE); 655 } else { 656 // Put the page indicators above the hotseat 657 lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams(); 658 lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; 659 lp.width = LayoutParams.WRAP_CONTENT; 660 lp.height = LayoutParams.WRAP_CONTENT; 661 lp.bottomMargin = hotseatBarHeightPx; 662 pageIndicator.setLayoutParams(lp); 663 } 664 } 665 666 // Layout AllApps 667 AppsCustomizeTabHost host = (AppsCustomizeTabHost) 668 launcher.findViewById(R.id.apps_customize_pane); 669 if (host != null) { 670 // Center the all apps page indicator 671 int pageIndicatorHeight = (int) (pageIndicatorHeightPx * Math.min(1f, 672 (allAppsIconSizePx / DynamicGrid.DEFAULT_ICON_SIZE_PX))); 673 pageIndicator = host.findViewById(R.id.apps_customize_page_indicator); 674 if (pageIndicator != null) { 675 lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams(); 676 lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; 677 lp.width = LayoutParams.WRAP_CONTENT; 678 lp.height = pageIndicatorHeight; 679 pageIndicator.setLayoutParams(lp); 680 } 681 682 AppsCustomizePagedView pagedView = (AppsCustomizePagedView) 683 host.findViewById(R.id.apps_customize_pane_content); 684 padding = new Rect(); 685 if (pagedView != null) { 686 // Constrain the dimensions of all apps so that it does not span the full width 687 int paddingLR = (availableWidthPx - (allAppsCellWidthPx * allAppsNumCols)) / 688 (2 * (allAppsNumCols + 1)); 689 int paddingTB = (availableHeightPx - (allAppsCellHeightPx * allAppsNumRows)) / 690 (2 * (allAppsNumRows + 1)); 691 paddingLR = Math.min(paddingLR, (int)((paddingLR + paddingTB) * 0.75f)); 692 paddingTB = Math.min(paddingTB, (int)((paddingLR + paddingTB) * 0.75f)); 693 int maxAllAppsWidth = (allAppsNumCols * (allAppsCellWidthPx + 2 * paddingLR)); 694 int gridPaddingLR = (availableWidthPx - maxAllAppsWidth) / 2; 695 if (gridPaddingLR > (allAppsCellWidthPx / 4)) { 696 padding.left = padding.right = gridPaddingLR; 697 } 698 // The icons are centered, so we can't just offset by the page indicator height 699 // because the empty space will actually be pageIndicatorHeight + paddingTB 700 padding.bottom = Math.max(0, pageIndicatorHeight - paddingTB); 701 pagedView.setAllAppsPadding(padding); 702 pagedView.setWidgetsPageIndicatorPadding(pageIndicatorHeight); 703 } 704 } 705 706 // Layout the Overview Mode 707 View overviewMode = launcher.getOverviewPanel(); 708 if (overviewMode != null) { 709 Rect r = getOverviewModeButtonBarRect(); 710 lp = (FrameLayout.LayoutParams) overviewMode.getLayoutParams(); 711 lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; 712 lp.width = Math.min(availableWidthPx, overviewModeMaxBarWidthPx); 713 lp.height = r.height(); 714 overviewMode.setLayoutParams(lp); 715 } 716 } 717} 718