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