• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.launcher3;
18 
19 import android.appwidget.AppWidgetHostView;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.res.Resources;
23 import android.graphics.Point;
24 import android.graphics.Rect;
25 import android.util.DisplayMetrics;
26 import android.view.Gravity;
27 import android.view.View;
28 import android.view.ViewGroup;
29 import android.view.ViewGroup.LayoutParams;
30 import android.widget.FrameLayout;
31 
32 import com.android.launcher3.config.FeatureFlags;
33 
34 import java.util.ArrayList;
35 
36 public class DeviceProfile {
37 
38     public interface LauncherLayoutChangeListener {
onLauncherLayoutChanged()39         void onLauncherLayoutChanged();
40     }
41 
42     public final InvariantDeviceProfile inv;
43 
44     // Device properties
45     public final boolean isTablet;
46     public final boolean isLargeTablet;
47     public final boolean isPhone;
48     public final boolean transposeLayoutWithOrientation;
49 
50     // Device properties in current orientation
51     public final boolean isLandscape;
52     public final int widthPx;
53     public final int heightPx;
54     public final int availableWidthPx;
55     public final int availableHeightPx;
56     /**
57      * The maximum amount of left/right workspace padding as a percentage of the screen width.
58      * To be clear, this means that up to 7% of the screen width can be used as left padding, and
59      * 7% of the screen width can be used as right padding.
60      */
61     private static final float MAX_HORIZONTAL_PADDING_PERCENT = 0.14f;
62 
63     // Overview mode
64     private final int overviewModeMinIconZoneHeightPx;
65     private final int overviewModeMaxIconZoneHeightPx;
66     private final int overviewModeBarItemWidthPx;
67     private final int overviewModeBarSpacerWidthPx;
68     private final float overviewModeIconZoneRatio;
69 
70     // Workspace
71     private int desiredWorkspaceLeftRightMarginPx;
72     public final int edgeMarginPx;
73     public final Rect defaultWidgetPadding;
74     private final int defaultPageSpacingPx;
75     private final int topWorkspacePadding;
76     private float dragViewScale;
77     public float workspaceSpringLoadShrinkFactor;
78     public final int workspaceSpringLoadedBottomSpace;
79 
80     // Page indicator
81     private final int pageIndicatorHeightPx;
82     private final int pageIndicatorLandGutterLeftNavBarPx;
83     private final int pageIndicatorLandGutterRightNavBarPx;
84     private final int pageIndicatorLandWorkspaceOffsetPx;
85 
86     // Workspace icons
87     public int iconSizePx;
88     public int iconTextSizePx;
89     public int iconDrawablePaddingPx;
90     public int iconDrawablePaddingOriginalPx;
91 
92     public int cellWidthPx;
93     public int cellHeightPx;
94 
95     // Folder
96     public int folderBackgroundOffset;
97     public int folderIconSizePx;
98     public int folderIconPreviewPadding;
99     public int folderCellWidthPx;
100     public int folderCellHeightPx;
101     public int folderChildDrawablePaddingPx;
102 
103     // Hotseat
104     public int hotseatCellWidthPx;
105     public int hotseatCellHeightPx;
106     public int hotseatIconSizePx;
107     private int hotseatBarHeightPx;
108     private int hotseatBarTopPaddingPx;
109     private int hotseatLandGutterPx;
110 
111     // All apps
112     public int allAppsNumCols;
113     public int allAppsNumPredictiveCols;
114     public int allAppsButtonVisualSize;
115     public int allAppsIconSizePx;
116     public int allAppsIconDrawablePaddingPx;
117     public float allAppsIconTextSizePx;
118 
119     // Containers
120     private final int containerLeftPaddingPx;
121     private final int containerRightPaddingPx;
122 
123     // Drop Target
124     public int dropTargetBarSizePx;
125 
126     // Insets
127     private Rect mInsets = new Rect();
128 
129     // Listeners
130     private ArrayList<LauncherLayoutChangeListener> mListeners = new ArrayList<>();
131 
DeviceProfile(Context context, InvariantDeviceProfile inv, Point minSize, Point maxSize, int width, int height, boolean isLandscape)132     public DeviceProfile(Context context, InvariantDeviceProfile inv,
133             Point minSize, Point maxSize,
134             int width, int height, boolean isLandscape) {
135 
136         this.inv = inv;
137         this.isLandscape = isLandscape;
138 
139         Resources res = context.getResources();
140         DisplayMetrics dm = res.getDisplayMetrics();
141 
142         // Constants from resources
143         isTablet = res.getBoolean(R.bool.is_tablet);
144         isLargeTablet = res.getBoolean(R.bool.is_large_tablet);
145         isPhone = !isTablet && !isLargeTablet;
146 
147         // Some more constants
148         transposeLayoutWithOrientation =
149                 res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
150 
151         ComponentName cn = new ComponentName(context.getPackageName(),
152                 this.getClass().getName());
153         defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
154         edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
155         desiredWorkspaceLeftRightMarginPx = edgeMarginPx;
156         pageIndicatorHeightPx =
157                 res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height);
158         pageIndicatorLandGutterLeftNavBarPx = res.getDimensionPixelSize(
159                 R.dimen.dynamic_grid_page_indicator_gutter_width_left_nav_bar);
160         pageIndicatorLandWorkspaceOffsetPx =
161                 res.getDimensionPixelSize(R.dimen.all_apps_caret_workspace_offset);
162         pageIndicatorLandGutterRightNavBarPx = res.getDimensionPixelSize(
163                 R.dimen.dynamic_grid_page_indicator_gutter_width_right_nav_bar);
164         defaultPageSpacingPx =
165                 res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing);
166         topWorkspacePadding =
167                 res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_top_padding);
168         overviewModeMinIconZoneHeightPx =
169                 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_min_icon_zone_height);
170         overviewModeMaxIconZoneHeightPx =
171                 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_max_icon_zone_height);
172         overviewModeBarItemWidthPx =
173                 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_item_width);
174         overviewModeBarSpacerWidthPx =
175                 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_spacer_width);
176         overviewModeIconZoneRatio =
177                 res.getInteger(R.integer.config_dynamic_grid_overview_icon_zone_percentage) / 100f;
178         iconDrawablePaddingOriginalPx =
179                 res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);
180         dropTargetBarSizePx = res.getDimensionPixelSize(R.dimen.dynamic_grid_drop_target_size);
181         workspaceSpringLoadedBottomSpace =
182                 res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space);
183         hotseatBarHeightPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_height);
184         hotseatBarTopPaddingPx =
185                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
186         hotseatLandGutterPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_gutter_width);
187         containerLeftPaddingPx =
188                 res.getDimensionPixelSize(R.dimen.dynamic_grid_container_land_left_padding);
189         containerRightPaddingPx =
190                 res.getDimensionPixelSize(R.dimen.dynamic_grid_container_land_right_padding);
191 
192         // Determine sizes.
193         widthPx = width;
194         heightPx = height;
195         if (isLandscape) {
196             availableWidthPx = maxSize.x;
197             availableHeightPx = minSize.y;
198         } else {
199             availableWidthPx = minSize.x;
200             availableHeightPx = maxSize.y;
201         }
202 
203         // Calculate the remaining vars
204         updateAvailableDimensions(dm, res);
205         computeAllAppsButtonSize(context);
206     }
207 
addLauncherLayoutChangedListener(LauncherLayoutChangeListener listener)208     public void addLauncherLayoutChangedListener(LauncherLayoutChangeListener listener) {
209         if (!mListeners.contains(listener)) {
210             mListeners.add(listener);
211         }
212     }
213 
removeLauncherLayoutChangedListener(LauncherLayoutChangeListener listener)214     public void removeLauncherLayoutChangedListener(LauncherLayoutChangeListener listener) {
215         if (mListeners.contains(listener)) {
216             mListeners.remove(listener);
217         }
218     }
219 
220     /**
221      * Determine the exact visual footprint of the all apps button, taking into account scaling
222      * and internal padding of the drawable.
223      */
computeAllAppsButtonSize(Context context)224     private void computeAllAppsButtonSize(Context context) {
225         Resources res = context.getResources();
226         float padding = res.getInteger(R.integer.config_allAppsButtonPaddingPercent) / 100f;
227         allAppsButtonVisualSize = (int) (hotseatIconSizePx * (1 - padding)) - context.getResources()
228                         .getDimensionPixelSize(R.dimen.all_apps_button_scale_down);
229     }
230 
updateAvailableDimensions(DisplayMetrics dm, Resources res)231     private void updateAvailableDimensions(DisplayMetrics dm, Resources res) {
232         // Check to see if the icons fit in the new available height.  If not, then we need to
233         // shrink the icon size.
234         float scale = 1f;
235         int drawablePadding = iconDrawablePaddingOriginalPx;
236         updateIconSize(1f, drawablePadding, res, dm);
237         float usedHeight = (cellHeightPx * inv.numRows);
238 
239         int maxHeight = (availableHeightPx - getTotalWorkspacePadding().y);
240         if (usedHeight > maxHeight) {
241             scale = maxHeight / usedHeight;
242             drawablePadding = 0;
243         }
244         updateIconSize(scale, drawablePadding, res, dm);
245     }
246 
updateIconSize(float scale, int drawablePadding, Resources res, DisplayMetrics dm)247     private void updateIconSize(float scale, int drawablePadding, Resources res,
248                                 DisplayMetrics dm) {
249         iconSizePx = (int) (Utilities.pxFromDp(inv.iconSize, dm) * scale);
250         iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale);
251         iconDrawablePaddingPx = drawablePadding;
252         hotseatIconSizePx = (int) (Utilities.pxFromDp(inv.hotseatIconSize, dm) * scale);
253         allAppsIconSizePx = iconSizePx;
254         allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
255         allAppsIconTextSizePx = iconTextSizePx;
256 
257         cellWidthPx = iconSizePx;
258         cellHeightPx = iconSizePx + iconDrawablePaddingPx
259                 + Utilities.calculateTextHeight(iconTextSizePx);
260         final float scaleDps = !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND ? 0f
261                 : res.getDimensionPixelSize(R.dimen.dragViewScale);
262         dragViewScale = (iconSizePx + scaleDps) / iconSizePx;
263 
264         // Hotseat
265         hotseatCellWidthPx = iconSizePx;
266         hotseatCellHeightPx = iconSizePx;
267 
268         if (!isVerticalBarLayout()) {
269             int expectedWorkspaceHeight = availableHeightPx - hotseatBarHeightPx
270                     - pageIndicatorHeightPx - topWorkspacePadding;
271             float minRequiredHeight = dropTargetBarSizePx + workspaceSpringLoadedBottomSpace;
272             workspaceSpringLoadShrinkFactor = Math.min(
273                     res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f,
274                     1 - (minRequiredHeight / expectedWorkspaceHeight));
275         } else {
276             workspaceSpringLoadShrinkFactor =
277                     res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
278         }
279 
280         // Folder cell
281         int cellPaddingX = res.getDimensionPixelSize(R.dimen.folder_cell_x_padding);
282         int cellPaddingY = res.getDimensionPixelSize(R.dimen.folder_cell_y_padding);
283         final int folderChildTextSize =
284                 Utilities.calculateTextHeight(res.getDimension(R.dimen.folder_child_text_size));
285 
286         final int folderBottomPanelSize =
287                 res.getDimensionPixelSize(R.dimen.folder_label_padding_top)
288                  + res.getDimensionPixelSize(R.dimen.folder_label_padding_bottom)
289                 + Utilities.calculateTextHeight(res.getDimension(R.dimen.folder_label_text_size));
290 
291         // Don't let the folder get too close to the edges of the screen.
292         folderCellWidthPx = Math.min(iconSizePx + 2 * cellPaddingX,
293                 (availableWidthPx - 4 * edgeMarginPx) / inv.numFolderColumns);
294         folderCellHeightPx = Math.min(iconSizePx + 3 * cellPaddingY + folderChildTextSize,
295                 (availableHeightPx - 4 * edgeMarginPx - folderBottomPanelSize) / inv.numFolderRows);
296         folderChildDrawablePaddingPx = Math.max(0,
297                 (folderCellHeightPx - iconSizePx - folderChildTextSize) / 3);
298 
299         // Folder icon
300         folderBackgroundOffset = -edgeMarginPx;
301         folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
302         folderIconPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
303     }
304 
updateInsets(Rect insets)305     public void updateInsets(Rect insets) {
306         mInsets.set(insets);
307     }
308 
updateAppsViewNumCols()309     public void updateAppsViewNumCols() {
310         allAppsNumCols = allAppsNumPredictiveCols = inv.numColumns;
311     }
312 
313     /** Returns the width and height of the search bar, ignoring any padding. */
getSearchBarDimensForWidgetOpts()314     public Point getSearchBarDimensForWidgetOpts() {
315         if (isVerticalBarLayout()) {
316             return new Point(dropTargetBarSizePx, availableHeightPx - 2 * edgeMarginPx);
317         } else {
318             int gap;
319             if (isTablet) {
320                 // Pad the left and right of the workspace to ensure consistent spacing
321                 // between all icons
322                 int width = getCurrentWidth();
323                 // XXX: If the icon size changes across orientations, we will have to take
324                 //      that into account here too.
325                 gap = ((width - 2 * edgeMarginPx
326                         - (inv.numColumns * cellWidthPx)) / (2 * (inv.numColumns + 1)))
327                         + edgeMarginPx;
328             } else {
329                 gap = desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.right;
330             }
331             return new Point(availableWidthPx - 2 * gap, dropTargetBarSizePx);
332         }
333     }
334 
getCellSize()335     public Point getCellSize() {
336         Point result = new Point();
337         // Since we are only concerned with the overall padding, layout direction does
338         // not matter.
339         Point padding = getTotalWorkspacePadding();
340         result.x = calculateCellWidth(availableWidthPx - padding.x, inv.numColumns);
341         result.y = calculateCellHeight(availableHeightPx - padding.y, inv.numRows);
342         return result;
343     }
344 
getTotalWorkspacePadding()345     public Point getTotalWorkspacePadding() {
346         Rect padding = getWorkspacePadding(null);
347         return new Point(padding.left + padding.right, padding.top + padding.bottom);
348     }
349 
350     /**
351      * Returns the workspace padding in the specified orientation.
352      * Note that it assumes that while in verticalBarLayout, the nav bar is on the right, as such
353      * this value is not reliable.
354      * Use {@link #getTotalWorkspacePadding()} instead.
355      */
getWorkspacePadding(Rect recycle)356     public Rect getWorkspacePadding(Rect recycle) {
357         Rect padding = recycle == null ? new Rect() : recycle;
358         if (isVerticalBarLayout()) {
359             if (mInsets.left > 0) {
360                 padding.set(mInsets.left + pageIndicatorLandGutterLeftNavBarPx, 0,
361                         hotseatBarHeightPx + hotseatLandGutterPx - mInsets.left, 2 * edgeMarginPx);
362             } else {
363                 padding.set(pageIndicatorLandGutterRightNavBarPx, 0,
364                         hotseatBarHeightPx + hotseatLandGutterPx, 2 * edgeMarginPx);
365             }
366         } else {
367             int paddingBottom = hotseatBarHeightPx + pageIndicatorHeightPx;
368             if (isTablet) {
369                 // Pad the left and right of the workspace to ensure consistent spacing
370                 // between all icons
371                 float gapScale = 1f + (dragViewScale - 1f) / 2f;
372                 int width = getCurrentWidth();
373                 int height = getCurrentHeight();
374                 // The amount of screen space available for left/right padding.
375                 int availablePaddingX = Math.max(0, width - (int) ((inv.numColumns * cellWidthPx) +
376                         ((inv.numColumns - 1) * gapScale * cellWidthPx)));
377                 availablePaddingX = (int) Math.min(availablePaddingX,
378                             width * MAX_HORIZONTAL_PADDING_PERCENT);
379                 int availablePaddingY = Math.max(0, height - topWorkspacePadding - paddingBottom
380                         - (int) (2 * inv.numRows * cellHeightPx));
381                 padding.set(availablePaddingX / 2, topWorkspacePadding + availablePaddingY / 2,
382                         availablePaddingX / 2, paddingBottom + availablePaddingY / 2);
383             } else {
384                 // Pad the top and bottom of the workspace with search/hotseat bar sizes
385                 padding.set(desiredWorkspaceLeftRightMarginPx,
386                         topWorkspacePadding,
387                         desiredWorkspaceLeftRightMarginPx,
388                         paddingBottom);
389             }
390         }
391         return padding;
392     }
393 
394     /**
395      * @return the bounds for which the open folders should be contained within
396      */
getAbsoluteOpenFolderBounds()397     public Rect getAbsoluteOpenFolderBounds() {
398         if (isVerticalBarLayout()) {
399             // Folders should only appear right of the drop target bar and left of the hotseat
400             return new Rect(mInsets.left + dropTargetBarSizePx + edgeMarginPx,
401                     mInsets.top,
402                     mInsets.left + availableWidthPx - hotseatBarHeightPx - edgeMarginPx,
403                     mInsets.top + availableHeightPx);
404         } else {
405             // Folders should only appear below the drop target bar and above the hotseat
406             return new Rect(mInsets.left,
407                     mInsets.top + dropTargetBarSizePx + edgeMarginPx,
408                     mInsets.left + availableWidthPx,
409                     mInsets.top + availableHeightPx - hotseatBarHeightPx - pageIndicatorHeightPx -
410                             edgeMarginPx);
411         }
412     }
413 
getWorkspacePageSpacing()414     private int getWorkspacePageSpacing() {
415         if (isVerticalBarLayout() || isLargeTablet) {
416             // In landscape mode the page spacing is set to the default.
417             return defaultPageSpacingPx;
418         } else {
419             // In portrait, we want the pages spaced such that there is no
420             // overhang of the previous / next page into the current page viewport.
421             // We assume symmetrical padding in portrait mode.
422             return Math.max(defaultPageSpacingPx, getWorkspacePadding(null).left + 1);
423         }
424     }
425 
getOverviewModeButtonBarHeight()426     int getOverviewModeButtonBarHeight() {
427         int zoneHeight = (int) (overviewModeIconZoneRatio * availableHeightPx);
428         zoneHeight = Math.min(overviewModeMaxIconZoneHeightPx,
429                 Math.max(overviewModeMinIconZoneHeightPx, zoneHeight));
430         return zoneHeight;
431     }
432 
calculateCellWidth(int width, int countX)433     public static int calculateCellWidth(int width, int countX) {
434         return width / countX;
435     }
calculateCellHeight(int height, int countY)436     public static int calculateCellHeight(int height, int countY) {
437         return height / countY;
438     }
439 
440     /**
441      * When {@code true}, the device is in landscape mode and the hotseat is on the right column.
442      * When {@code false}, either device is in portrait mode or the device is in landscape mode and
443      * the hotseat is on the bottom row.
444      */
isVerticalBarLayout()445     public boolean isVerticalBarLayout() {
446         return isLandscape && transposeLayoutWithOrientation;
447     }
448 
shouldFadeAdjacentWorkspaceScreens()449     boolean shouldFadeAdjacentWorkspaceScreens() {
450         return isVerticalBarLayout() || isLargeTablet;
451     }
452 
getVisibleChildCount(ViewGroup parent)453     private int getVisibleChildCount(ViewGroup parent) {
454         int visibleChildren = 0;
455         for (int i = 0; i < parent.getChildCount(); i++) {
456             if (parent.getChildAt(i).getVisibility() != View.GONE) {
457                 visibleChildren++;
458             }
459         }
460         return visibleChildren;
461     }
462 
layout(Launcher launcher, boolean notifyListeners)463     public void layout(Launcher launcher, boolean notifyListeners) {
464         FrameLayout.LayoutParams lp;
465         boolean hasVerticalBarLayout = isVerticalBarLayout();
466         final boolean isLayoutRtl = Utilities.isRtl(launcher.getResources());
467 
468         // Layout the search bar space
469         Point searchBarBounds = getSearchBarDimensForWidgetOpts();
470         View searchBar = launcher.getDropTargetBar();
471         lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
472         lp.width = searchBarBounds.x;
473         lp.height = searchBarBounds.y;
474         lp.topMargin = mInsets.top + edgeMarginPx;
475         searchBar.setLayoutParams(lp);
476 
477         // Layout the workspace
478         PagedView workspace = (PagedView) launcher.findViewById(R.id.workspace);
479         Rect workspacePadding = getWorkspacePadding(null);
480         workspace.setPadding(workspacePadding.left, workspacePadding.top, workspacePadding.right,
481                 workspacePadding.bottom);
482         workspace.setPageSpacing(getWorkspacePageSpacing());
483 
484         View qsbContainer = launcher.getQsbContainer();
485         lp = (FrameLayout.LayoutParams) qsbContainer.getLayoutParams();
486         lp.topMargin = mInsets.top + workspacePadding.top;
487         qsbContainer.setLayoutParams(lp);
488 
489         // Layout the hotseat
490         Hotseat hotseat = (Hotseat) launcher.findViewById(R.id.hotseat);
491         lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams();
492         // We want the edges of the hotseat to line up with the edges of the workspace, but the
493         // icons in the hotseat are a different size, and so don't line up perfectly. To account for
494         // this, we pad the left and right of the hotseat with half of the difference of a workspace
495         // cell vs a hotseat cell.
496         float workspaceCellWidth = (float) getCurrentWidth() / inv.numColumns;
497         float hotseatCellWidth = (float) getCurrentWidth() / inv.numHotseatIcons;
498         int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
499         if (hasVerticalBarLayout) {
500             // Vertical hotseat -- The hotseat is fixed in the layout to be on the right of the
501             //                     screen regardless of RTL
502             lp.gravity = Gravity.RIGHT;
503             lp.width = hotseatBarHeightPx + mInsets.left + mInsets.right;
504             lp.height = LayoutParams.MATCH_PARENT;
505             hotseat.getLayout().setPadding(mInsets.left, mInsets.top, mInsets.right,
506                     workspacePadding.bottom);
507         } else if (isTablet) {
508             // Pad the hotseat with the workspace padding calculated above
509             lp.gravity = Gravity.BOTTOM;
510             lp.width = LayoutParams.MATCH_PARENT;
511             lp.height = hotseatBarHeightPx + mInsets.bottom;
512             hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left,
513                     hotseatBarTopPaddingPx, hotseatAdjustment + workspacePadding.right,
514                     mInsets.bottom);
515         } else {
516             // For phones, layout the hotseat without any bottom margin
517             // to ensure that we have space for the folders
518             lp.gravity = Gravity.BOTTOM;
519             lp.width = LayoutParams.MATCH_PARENT;
520             lp.height = hotseatBarHeightPx + mInsets.bottom;
521             hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left,
522                     hotseatBarTopPaddingPx, hotseatAdjustment + workspacePadding.right,
523                     mInsets.bottom);
524         }
525         hotseat.setLayoutParams(lp);
526 
527         // Layout the page indicators
528         View pageIndicator = launcher.findViewById(R.id.page_indicator);
529         if (pageIndicator != null) {
530             lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams();
531             if (isVerticalBarLayout()) {
532                 if (mInsets.left > 0) {
533                     lp.leftMargin = mInsets.left + pageIndicatorLandGutterLeftNavBarPx -
534                             lp.width - pageIndicatorLandWorkspaceOffsetPx;
535                 } else if (mInsets.right > 0) {
536                     lp.leftMargin = pageIndicatorLandGutterRightNavBarPx - lp.width -
537                             pageIndicatorLandWorkspaceOffsetPx;
538                 }
539                 lp.bottomMargin = workspacePadding.bottom;
540             } else {
541                 // Put the page indicators above the hotseat
542                 lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
543                 lp.height = pageIndicatorHeightPx;
544                 lp.bottomMargin = hotseatBarHeightPx + mInsets.bottom;
545             }
546             pageIndicator.setLayoutParams(lp);
547         }
548 
549         // Layout the Overview Mode
550         ViewGroup overviewMode = launcher.getOverviewPanel();
551         if (overviewMode != null) {
552             lp = (FrameLayout.LayoutParams) overviewMode.getLayoutParams();
553             lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
554 
555             int visibleChildCount = getVisibleChildCount(overviewMode);
556             int totalItemWidth = visibleChildCount * overviewModeBarItemWidthPx;
557             int maxWidth = totalItemWidth + (visibleChildCount-1) * overviewModeBarSpacerWidthPx;
558 
559             lp.width = Math.min(availableWidthPx, maxWidth);
560             lp.height = getOverviewModeButtonBarHeight();
561             // Center the overview buttons on the workspace page
562             lp.leftMargin = workspacePadding.left + (availableWidthPx -
563                     workspacePadding.left - workspacePadding.right - lp.width) / 2;
564             overviewMode.setLayoutParams(lp);
565         }
566 
567         if (notifyListeners) {
568             for (int i = mListeners.size() - 1; i >= 0; i--) {
569                 mListeners.get(i).onLauncherLayoutChanged();
570             }
571         }
572     }
573 
getCurrentWidth()574     private int getCurrentWidth() {
575         return isLandscape
576                 ? Math.max(widthPx, heightPx)
577                 : Math.min(widthPx, heightPx);
578     }
579 
getCurrentHeight()580     private int getCurrentHeight() {
581         return isLandscape
582                 ? Math.min(widthPx, heightPx)
583                 : Math.max(widthPx, heightPx);
584     }
585 
586 
587     /**
588      * @return the left/right paddings for all containers.
589      */
getContainerPadding(Context context)590     public final int[] getContainerPadding(Context context) {
591         Resources res = context.getResources();
592 
593         // No paddings for portrait phone
594         if (isPhone && !isVerticalBarLayout()) {
595             return new int[] {0, 0};
596         }
597 
598         // In landscape, we match the width of the workspace
599         int padding = (pageIndicatorLandGutterRightNavBarPx +
600                 hotseatBarHeightPx + hotseatLandGutterPx + mInsets.left) / 2;
601         return new int[]{ padding, padding };
602     }
603 }
604