blob: 626ec42cbafbf1eaa6bc00fa9cc27f146c4d2e02 [file] [log] [blame]
Winson Chungb3800242013-10-24 11:01:54 -07001/*
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}