Home | History | Annotate | Download | only in launcher3
      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.Paint;
     24 import android.graphics.Paint.FontMetrics;
     25 import android.graphics.Point;
     26 import android.graphics.Rect;
     27 import android.util.DisplayMetrics;
     28 import android.view.Gravity;
     29 import android.view.View;
     30 import android.view.ViewGroup;
     31 import android.view.ViewGroup.LayoutParams;
     32 import android.view.ViewGroup.MarginLayoutParams;
     33 import android.widget.FrameLayout;
     34 import android.widget.LinearLayout;
     35 
     36 public class DeviceProfile {
     37 
     38     public final InvariantDeviceProfile inv;
     39 
     40     // Device properties
     41     public final boolean isTablet;
     42     public final boolean isLargeTablet;
     43     public final boolean isPhone;
     44     public final boolean transposeLayoutWithOrientation;
     45 
     46     // Device properties in current orientation
     47     public final boolean isLandscape;
     48     public final int widthPx;
     49     public final int heightPx;
     50     public final int availableWidthPx;
     51     public final int availableHeightPx;
     52     /**
     53      * The maximum amount of left/right workspace padding as a percentage of the screen width.
     54      * To be clear, this means that up to 7% of the screen width can be used as left padding, and
     55      * 7% of the screen width can be used as right padding.
     56      */
     57     private static final float MAX_HORIZONTAL_PADDING_PERCENT = 0.14f;
     58 
     59     // Overview mode
     60     private final int overviewModeMinIconZoneHeightPx;
     61     private final int overviewModeMaxIconZoneHeightPx;
     62     private final int overviewModeBarItemWidthPx;
     63     private final int overviewModeBarSpacerWidthPx;
     64     private final float overviewModeIconZoneRatio;
     65 
     66     // Workspace
     67     private int desiredWorkspaceLeftRightMarginPx;
     68     public final int edgeMarginPx;
     69     public final Rect defaultWidgetPadding;
     70     private final int pageIndicatorHeightPx;
     71     private final int defaultPageSpacingPx;
     72     private float dragViewScale;
     73 
     74     // Workspace icons
     75     public int iconSizePx;
     76     public int iconTextSizePx;
     77     public int iconDrawablePaddingPx;
     78     public int iconDrawablePaddingOriginalPx;
     79 
     80     public int cellWidthPx;
     81     public int cellHeightPx;
     82 
     83     // Folder
     84     public int folderBackgroundOffset;
     85     public int folderIconSizePx;
     86     public int folderCellWidthPx;
     87     public int folderCellHeightPx;
     88 
     89     // Hotseat
     90     public int hotseatCellWidthPx;
     91     public int hotseatCellHeightPx;
     92     public int hotseatIconSizePx;
     93     private int normalHotseatBarHeightPx, shortHotseatBarHeightPx;
     94     private int hotseatBarHeightPx; // One of the above.
     95 
     96     // All apps
     97     public int allAppsNumCols;
     98     public int allAppsNumPredictiveCols;
     99     public int allAppsButtonVisualSize;
    100     public final int allAppsIconSizePx;
    101     public final float allAppsIconTextSizeSp;
    102 
    103     // QSB
    104     private int searchBarWidgetInternalPaddingTop, searchBarWidgetInternalPaddingBottom;
    105     private int searchBarTopPaddingPx;
    106     private int tallSearchBarNegativeTopPaddingPx, normalSearchBarTopExtraPaddingPx;
    107     private int searchBarTopExtraPaddingPx; // One of the above.
    108     private int normalSearchBarBottomPaddingPx, tallSearchBarBottomPaddingPx;
    109     private int searchBarBottomPaddingPx; // One of the above.
    110     private int normalSearchBarSpaceHeightPx, tallSearchBarSpaceHeightPx;
    111     private int searchBarSpaceHeightPx; // One of the above.
    112 
    113     public DeviceProfile(Context context, InvariantDeviceProfile inv,
    114             Point minSize, Point maxSize,
    115             int width, int height, boolean isLandscape) {
    116 
    117         this.inv = inv;
    118         this.isLandscape = isLandscape;
    119 
    120         Resources res = context.getResources();
    121         DisplayMetrics dm = res.getDisplayMetrics();
    122 
    123         // Constants from resources
    124         isTablet = res.getBoolean(R.bool.is_tablet);
    125         isLargeTablet = res.getBoolean(R.bool.is_large_tablet);
    126         isPhone = !isTablet && !isLargeTablet;
    127 
    128         // Some more constants
    129         transposeLayoutWithOrientation =
    130                 res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
    131 
    132         ComponentName cn = new ComponentName(context.getPackageName(),
    133                 this.getClass().getName());
    134         defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
    135         edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
    136         desiredWorkspaceLeftRightMarginPx = 2 * edgeMarginPx;
    137         pageIndicatorHeightPx =
    138                 res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height);
    139         defaultPageSpacingPx =
    140                 res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing);
    141         overviewModeMinIconZoneHeightPx =
    142                 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_min_icon_zone_height);
    143         overviewModeMaxIconZoneHeightPx =
    144                 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_max_icon_zone_height);
    145         overviewModeBarItemWidthPx =
    146                 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_item_width);
    147         overviewModeBarSpacerWidthPx =
    148                 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_spacer_width);
    149         overviewModeIconZoneRatio =
    150                 res.getInteger(R.integer.config_dynamic_grid_overview_icon_zone_percentage) / 100f;
    151         iconDrawablePaddingOriginalPx =
    152                 res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);
    153 
    154         // AllApps uses the original non-scaled icon text size
    155         allAppsIconTextSizeSp = inv.iconTextSize;
    156 
    157         // AllApps uses the original non-scaled icon size
    158         allAppsIconSizePx = Utilities.pxFromDp(inv.iconSize, dm);
    159 
    160         // Determine sizes.
    161         widthPx = width;
    162         heightPx = height;
    163         if (isLandscape) {
    164             availableWidthPx = maxSize.x;
    165             availableHeightPx = minSize.y;
    166         } else {
    167             availableWidthPx = minSize.x;
    168             availableHeightPx = maxSize.y;
    169         }
    170 
    171         // Calculate the remaining vars
    172         updateAvailableDimensions(dm, res);
    173         computeAllAppsButtonSize(context);
    174     }
    175 
    176     /**
    177      * Determine the exact visual footprint of the all apps button, taking into account scaling
    178      * and internal padding of the drawable.
    179      */
    180     private void computeAllAppsButtonSize(Context context) {
    181         Resources res = context.getResources();
    182         float padding = res.getInteger(R.integer.config_allAppsButtonPaddingPercent) / 100f;
    183         allAppsButtonVisualSize = (int) (hotseatIconSizePx * (1 - padding)) - context.getResources()
    184                         .getDimensionPixelSize(R.dimen.all_apps_button_scale_down);
    185     }
    186 
    187     private void updateAvailableDimensions(DisplayMetrics dm, Resources res) {
    188         // Check to see if the icons fit in the new available height.  If not, then we need to
    189         // shrink the icon size.
    190         float scale = 1f;
    191         int drawablePadding = iconDrawablePaddingOriginalPx;
    192         updateIconSize(1f, drawablePadding, res, dm);
    193         float usedHeight = (cellHeightPx * inv.numRows);
    194 
    195         // We only care about the top and bottom workspace padding, which is not affected by RTL.
    196         Rect workspacePadding = getWorkspacePadding(false /* isLayoutRtl */);
    197         int maxHeight = (availableHeightPx - workspacePadding.top - workspacePadding.bottom);
    198         if (usedHeight > maxHeight) {
    199             scale = maxHeight / usedHeight;
    200             drawablePadding = 0;
    201         }
    202         updateIconSize(scale, drawablePadding, res, dm);
    203     }
    204 
    205     private void updateIconSize(float scale, int drawablePadding, Resources res,
    206                                 DisplayMetrics dm) {
    207         iconSizePx = (int) (Utilities.pxFromDp(inv.iconSize, dm) * scale);
    208         iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale);
    209         iconDrawablePaddingPx = drawablePadding;
    210         hotseatIconSizePx = (int) (Utilities.pxFromDp(inv.hotseatIconSize, dm) * scale);
    211 
    212         // Search Bar
    213         normalSearchBarSpaceHeightPx = res.getDimensionPixelSize(
    214                 R.dimen.dynamic_grid_search_bar_height);
    215         tallSearchBarSpaceHeightPx = res.getDimensionPixelSize(
    216                 R.dimen.dynamic_grid_search_bar_height_tall);
    217         searchBarWidgetInternalPaddingTop = res.getDimensionPixelSize(
    218                 R.dimen.qsb_internal_padding_top);
    219         searchBarWidgetInternalPaddingBottom = res.getDimensionPixelSize(
    220                 R.dimen.qsb_internal_padding_bottom);
    221         normalSearchBarTopExtraPaddingPx = res.getDimensionPixelSize(
    222                 R.dimen.dynamic_grid_search_bar_extra_top_padding);
    223         tallSearchBarNegativeTopPaddingPx = res.getDimensionPixelSize(
    224                 R.dimen.dynamic_grid_search_bar_negative_top_padding_short);
    225         if (isTablet && !isVerticalBarLayout()) {
    226             searchBarTopPaddingPx = searchBarWidgetInternalPaddingTop;
    227             normalSearchBarBottomPaddingPx = searchBarWidgetInternalPaddingBottom +
    228                     res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_bottom_padding_tablet);
    229             tallSearchBarBottomPaddingPx = normalSearchBarBottomPaddingPx;
    230         } else {
    231             searchBarTopPaddingPx = searchBarWidgetInternalPaddingTop;
    232             normalSearchBarBottomPaddingPx = searchBarWidgetInternalPaddingBottom +
    233                     res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_bottom_padding);
    234             tallSearchBarBottomPaddingPx = searchBarWidgetInternalPaddingBottom
    235                     + res.getDimensionPixelSize(
    236                     R.dimen.dynamic_grid_search_bar_bottom_negative_padding_short);
    237         }
    238 
    239         // Calculate the actual text height
    240         Paint textPaint = new Paint();
    241         textPaint.setTextSize(iconTextSizePx);
    242         FontMetrics fm = textPaint.getFontMetrics();
    243         cellWidthPx = iconSizePx;
    244         cellHeightPx = iconSizePx + iconDrawablePaddingPx + (int) Math.ceil(fm.bottom - fm.top);
    245         final float scaleDps = res.getDimensionPixelSize(R.dimen.dragViewScale);
    246         dragViewScale = (iconSizePx + scaleDps) / iconSizePx;
    247 
    248         // Hotseat
    249         normalHotseatBarHeightPx = iconSizePx + 4 * edgeMarginPx;
    250         shortHotseatBarHeightPx = iconSizePx + 2 * edgeMarginPx;
    251         hotseatCellWidthPx = iconSizePx;
    252         hotseatCellHeightPx = iconSizePx;
    253 
    254         // Folder
    255         int folderCellPadding = isTablet || isLandscape ? 6 * edgeMarginPx : 3 * edgeMarginPx;
    256         // Don't let the folder get too close to the edges of the screen.
    257         folderCellWidthPx = Math.min(cellWidthPx + folderCellPadding,
    258                 (availableWidthPx - 4 * edgeMarginPx) / inv.numFolderColumns);
    259         folderCellHeightPx = cellHeightPx + edgeMarginPx;
    260         folderBackgroundOffset = -edgeMarginPx;
    261         folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
    262     }
    263 
    264     /**
    265      * @param recyclerViewWidth the available width of the AllAppsRecyclerView
    266      */
    267     public void updateAppsViewNumCols(Resources res, int recyclerViewWidth) {
    268         int appsViewLeftMarginPx =
    269                 res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
    270         int allAppsCellWidthGap =
    271                 res.getDimensionPixelSize(R.dimen.all_apps_icon_width_gap);
    272         int availableAppsWidthPx = (recyclerViewWidth > 0) ? recyclerViewWidth : availableWidthPx;
    273         int numAppsCols = (availableAppsWidthPx + allAppsCellWidthGap - appsViewLeftMarginPx) /
    274                 (allAppsIconSizePx + allAppsCellWidthGap);
    275         int numPredictiveAppCols = Math.max(inv.minAllAppsPredictionColumns, numAppsCols);
    276         allAppsNumCols = numAppsCols;
    277         allAppsNumPredictiveCols = numPredictiveAppCols;
    278     }
    279 
    280     /** Returns the amount of extra space to allocate to the search bar for vertical padding. */
    281     private int getSearchBarTotalVerticalPadding() {
    282         return searchBarTopPaddingPx + searchBarTopExtraPaddingPx + searchBarBottomPaddingPx;
    283     }
    284 
    285     /** Returns the width and height of the search bar, ignoring any padding. */
    286     public Point getSearchBarDimensForWidgetOpts(Resources res) {
    287         Rect searchBarBounds = getSearchBarBounds(Utilities.isRtl(res));
    288         if (isVerticalBarLayout()) {
    289             return new Point(searchBarBounds.width(), searchBarBounds.height());
    290         }
    291         int widgetInternalPadding = searchBarWidgetInternalPaddingTop +
    292                 searchBarWidgetInternalPaddingBottom;
    293         return new Point(searchBarBounds.width(), searchBarSpaceHeightPx + widgetInternalPadding);
    294     }
    295 
    296     /** Returns the search bar bounds in the current orientation */
    297     public Rect getSearchBarBounds(boolean isLayoutRtl) {
    298         Rect bounds = new Rect();
    299         if (isVerticalBarLayout()) {
    300             if (isLayoutRtl) {
    301                 bounds.set(availableWidthPx - normalSearchBarSpaceHeightPx, edgeMarginPx,
    302                         availableWidthPx, availableHeightPx - edgeMarginPx);
    303             } else {
    304                 bounds.set(0, edgeMarginPx, normalSearchBarSpaceHeightPx,
    305                         availableHeightPx - edgeMarginPx);
    306             }
    307         } else {
    308             int boundsBottom = searchBarSpaceHeightPx + getSearchBarTotalVerticalPadding();
    309             if (isTablet) {
    310                 // Pad the left and right of the workspace to ensure consistent spacing
    311                 // between all icons
    312                 int width = getCurrentWidth();
    313                 // XXX: If the icon size changes across orientations, we will have to take
    314                 //      that into account here too.
    315                 int gap = (int) ((width - 2 * edgeMarginPx -
    316                         (inv.numColumns * cellWidthPx)) / (2 * (inv.numColumns + 1)));
    317                 bounds.set(edgeMarginPx + gap, 0,
    318                         availableWidthPx - (edgeMarginPx + gap), boundsBottom);
    319             } else {
    320                 bounds.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left,
    321                         0,
    322                         availableWidthPx - (desiredWorkspaceLeftRightMarginPx -
    323                         defaultWidgetPadding.right), boundsBottom);
    324             }
    325         }
    326         return bounds;
    327     }
    328 
    329     /** Returns the workspace padding in the specified orientation */
    330     Rect getWorkspacePadding(boolean isLayoutRtl) {
    331         Rect searchBarBounds = getSearchBarBounds(isLayoutRtl);
    332         Rect padding = new Rect();
    333         if (isVerticalBarLayout()) {
    334             // Pad the left and right of the workspace with search/hotseat bar sizes
    335             if (isLayoutRtl) {
    336                 padding.set(normalHotseatBarHeightPx, edgeMarginPx,
    337                         searchBarBounds.width(), edgeMarginPx);
    338             } else {
    339                 padding.set(searchBarBounds.width(), edgeMarginPx,
    340                         normalHotseatBarHeightPx, edgeMarginPx);
    341             }
    342         } else {
    343             int paddingTop = searchBarBounds.bottom;
    344             int paddingBottom = hotseatBarHeightPx + pageIndicatorHeightPx;
    345             if (isTablet) {
    346                 // Pad the left and right of the workspace to ensure consistent spacing
    347                 // between all icons
    348                 float gapScale = 1f + (dragViewScale - 1f) / 2f;
    349                 int width = getCurrentWidth();
    350                 int height = getCurrentHeight();
    351                 // The amount of screen space available for left/right padding.
    352                 int availablePaddingX = Math.max(0, width - (int) ((inv.numColumns * cellWidthPx) +
    353                         ((inv.numColumns - 1) * gapScale * cellWidthPx)));
    354                 availablePaddingX = (int) Math.min(availablePaddingX,
    355                             width * MAX_HORIZONTAL_PADDING_PERCENT);
    356                 int availablePaddingY = Math.max(0, height - paddingTop - paddingBottom
    357                         - (int) (2 * inv.numRows * cellHeightPx));
    358                 padding.set(availablePaddingX / 2, paddingTop + availablePaddingY / 2,
    359                         availablePaddingX / 2, paddingBottom + availablePaddingY / 2);
    360             } else {
    361                 // Pad the top and bottom of the workspace with search/hotseat bar sizes
    362                 padding.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left,
    363                         paddingTop,
    364                         desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.right,
    365                         paddingBottom);
    366             }
    367         }
    368         return padding;
    369     }
    370 
    371     private int getWorkspacePageSpacing(boolean isLayoutRtl) {
    372         if (isVerticalBarLayout() || isLargeTablet) {
    373             // In landscape mode the page spacing is set to the default.
    374             return defaultPageSpacingPx;
    375         } else {
    376             // In portrait, we want the pages spaced such that there is no
    377             // overhang of the previous / next page into the current page viewport.
    378             // We assume symmetrical padding in portrait mode.
    379             return Math.max(defaultPageSpacingPx, 2 * getWorkspacePadding(isLayoutRtl).left);
    380         }
    381     }
    382 
    383     int getOverviewModeButtonBarHeight() {
    384         int zoneHeight = (int) (overviewModeIconZoneRatio * availableHeightPx);
    385         zoneHeight = Math.min(overviewModeMaxIconZoneHeightPx,
    386                 Math.max(overviewModeMinIconZoneHeightPx, zoneHeight));
    387         return zoneHeight;
    388     }
    389 
    390     // The rect returned will be extended to below the system ui that covers the workspace
    391     Rect getHotseatRect() {
    392         if (isVerticalBarLayout()) {
    393             return new Rect(availableWidthPx - normalHotseatBarHeightPx, 0,
    394                     Integer.MAX_VALUE, availableHeightPx);
    395         } else {
    396             return new Rect(0, availableHeightPx - hotseatBarHeightPx,
    397                     availableWidthPx, Integer.MAX_VALUE);
    398         }
    399     }
    400 
    401     public static int calculateCellWidth(int width, int countX) {
    402         return width / countX;
    403     }
    404     public static int calculateCellHeight(int height, int countY) {
    405         return height / countY;
    406     }
    407 
    408     /**
    409      * When {@code true}, the device is in landscape mode and the hotseat is on the right column.
    410      * When {@code false}, either device is in portrait mode or the device is in landscape mode and
    411      * the hotseat is on the bottom row.
    412      */
    413     boolean isVerticalBarLayout() {
    414         return isLandscape && transposeLayoutWithOrientation;
    415     }
    416 
    417     boolean shouldFadeAdjacentWorkspaceScreens() {
    418         return isVerticalBarLayout() || isLargeTablet;
    419     }
    420 
    421     private int getVisibleChildCount(ViewGroup parent) {
    422         int visibleChildren = 0;
    423         for (int i = 0; i < parent.getChildCount(); i++) {
    424             if (parent.getChildAt(i).getVisibility() != View.GONE) {
    425                 visibleChildren++;
    426             }
    427         }
    428         return visibleChildren;
    429     }
    430 
    431     // TODO(twickham): b/25154513
    432     public void setSearchBarHeight(int searchBarHeight) {
    433         if (searchBarHeight == LauncherCallbacks.SEARCH_BAR_HEIGHT_TALL) {
    434             hotseatBarHeightPx = shortHotseatBarHeightPx;
    435             searchBarSpaceHeightPx = tallSearchBarSpaceHeightPx;
    436             searchBarBottomPaddingPx = tallSearchBarBottomPaddingPx;
    437             searchBarTopExtraPaddingPx = isPhone ? tallSearchBarNegativeTopPaddingPx
    438                     : normalSearchBarTopExtraPaddingPx;
    439         } else {
    440             hotseatBarHeightPx = normalHotseatBarHeightPx;
    441             searchBarSpaceHeightPx = normalSearchBarSpaceHeightPx;
    442             searchBarBottomPaddingPx = normalSearchBarBottomPaddingPx;
    443             searchBarTopExtraPaddingPx = normalSearchBarTopExtraPaddingPx;
    444         }
    445     }
    446 
    447     public void layout(Launcher launcher) {
    448         FrameLayout.LayoutParams lp;
    449         boolean hasVerticalBarLayout = isVerticalBarLayout();
    450         final boolean isLayoutRtl = Utilities.isRtl(launcher.getResources());
    451 
    452         // Layout the search bar space
    453         Rect searchBarBounds = getSearchBarBounds(isLayoutRtl);
    454         View searchBar = launcher.getSearchDropTargetBar();
    455         lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
    456         lp.width = searchBarBounds.width();
    457         lp.height = searchBarBounds.height();
    458         lp.topMargin = searchBarTopExtraPaddingPx;
    459         if (hasVerticalBarLayout) {
    460             // Vertical search bar space -- The search bar is fixed in the layout to be on the left
    461             //                              of the screen regardless of RTL
    462             lp.gravity = Gravity.LEFT;
    463 
    464             LinearLayout targets = (LinearLayout) searchBar.findViewById(R.id.drag_target_bar);
    465             targets.setOrientation(LinearLayout.VERTICAL);
    466             FrameLayout.LayoutParams targetsLp = (FrameLayout.LayoutParams) targets.getLayoutParams();
    467             targetsLp.gravity = Gravity.TOP;
    468             targetsLp.height = LayoutParams.WRAP_CONTENT;
    469 
    470         } else {
    471             // Horizontal search bar space
    472             lp.gravity = Gravity.TOP|Gravity.CENTER_HORIZONTAL;
    473         }
    474         searchBar.setLayoutParams(lp);
    475 
    476         // Layout the workspace
    477         PagedView workspace = (PagedView) launcher.findViewById(R.id.workspace);
    478         lp = (FrameLayout.LayoutParams) workspace.getLayoutParams();
    479         lp.gravity = Gravity.CENTER;
    480         Rect padding = getWorkspacePadding(isLayoutRtl);
    481         workspace.setLayoutParams(lp);
    482         workspace.setPadding(padding.left, padding.top, padding.right, padding.bottom);
    483         workspace.setPageSpacing(getWorkspacePageSpacing(isLayoutRtl));
    484 
    485         // Layout the hotseat
    486         View hotseat = launcher.findViewById(R.id.hotseat);
    487         lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams();
    488         // We want the edges of the hotseat to line up with the edges of the workspace, but the
    489         // icons in the hotseat are a different size, and so don't line up perfectly. To account for
    490         // this, we pad the left and right of the hotseat with half of the difference of a workspace
    491         // cell vs a hotseat cell.
    492         float workspaceCellWidth = (float) getCurrentWidth() / inv.numColumns;
    493         float hotseatCellWidth = (float) getCurrentWidth() / inv.numHotseatIcons;
    494         int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
    495         if (hasVerticalBarLayout) {
    496             // Vertical hotseat -- The hotseat is fixed in the layout to be on the right of the
    497             //                     screen regardless of RTL
    498             lp.gravity = Gravity.RIGHT;
    499             lp.width = normalHotseatBarHeightPx;
    500             lp.height = LayoutParams.MATCH_PARENT;
    501             hotseat.findViewById(R.id.layout).setPadding(0, 2 * edgeMarginPx, 0, 2 * edgeMarginPx);
    502         } else if (isTablet) {
    503             // Pad the hotseat with the workspace padding calculated above
    504             lp.gravity = Gravity.BOTTOM;
    505             lp.width = LayoutParams.MATCH_PARENT;
    506             lp.height = hotseatBarHeightPx;
    507             hotseat.findViewById(R.id.layout).setPadding(
    508                     hotseatAdjustment + padding.left, 0,
    509                     hotseatAdjustment + padding.right, 2 * edgeMarginPx);
    510         } else {
    511             // For phones, layout the hotseat without any bottom margin
    512             // to ensure that we have space for the folders
    513             lp.gravity = Gravity.BOTTOM;
    514             lp.width = LayoutParams.MATCH_PARENT;
    515             lp.height = hotseatBarHeightPx;
    516             hotseat.findViewById(R.id.layout).setPadding(
    517                     hotseatAdjustment + padding.left, 0,
    518                     hotseatAdjustment + padding.right, 0);
    519         }
    520         hotseat.setLayoutParams(lp);
    521 
    522         // Layout the page indicators
    523         View pageIndicator = launcher.findViewById(R.id.page_indicator);
    524         if (pageIndicator != null) {
    525             if (hasVerticalBarLayout) {
    526                 // Hide the page indicators when we have vertical search/hotseat
    527                 pageIndicator.setVisibility(View.GONE);
    528             } else {
    529                 // Put the page indicators above the hotseat
    530                 lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams();
    531                 lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
    532                 lp.width = LayoutParams.WRAP_CONTENT;
    533                 lp.height = LayoutParams.WRAP_CONTENT;
    534                 lp.bottomMargin = hotseatBarHeightPx;
    535                 pageIndicator.setLayoutParams(lp);
    536             }
    537         }
    538 
    539         // Layout the Overview Mode
    540         ViewGroup overviewMode = launcher.getOverviewPanel();
    541         if (overviewMode != null) {
    542             int overviewButtonBarHeight = getOverviewModeButtonBarHeight();
    543             lp = (FrameLayout.LayoutParams) overviewMode.getLayoutParams();
    544             lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
    545 
    546             int visibleChildCount = getVisibleChildCount(overviewMode);
    547             int totalItemWidth = visibleChildCount * overviewModeBarItemWidthPx;
    548             int maxWidth = totalItemWidth + (visibleChildCount-1) * overviewModeBarSpacerWidthPx;
    549 
    550             lp.width = Math.min(availableWidthPx, maxWidth);
    551             lp.height = overviewButtonBarHeight;
    552             overviewMode.setLayoutParams(lp);
    553 
    554             if (lp.width > totalItemWidth && visibleChildCount > 1) {
    555                 // We have enough space. Lets add some margin too.
    556                 int margin = (lp.width - totalItemWidth) / (visibleChildCount-1);
    557                 View lastChild = null;
    558 
    559                 // Set margin of all visible children except the last visible child
    560                 for (int i = 0; i < visibleChildCount; i++) {
    561                     if (lastChild != null) {
    562                         MarginLayoutParams clp = (MarginLayoutParams) lastChild.getLayoutParams();
    563                         if (isLayoutRtl) {
    564                             clp.leftMargin = margin;
    565                         } else {
    566                             clp.rightMargin = margin;
    567                         }
    568                         lastChild.setLayoutParams(clp);
    569                         lastChild = null;
    570                     }
    571                     View thisChild = overviewMode.getChildAt(i);
    572                     if (thisChild.getVisibility() != View.GONE) {
    573                         lastChild = thisChild;
    574                     }
    575                 }
    576             }
    577         }
    578     }
    579 
    580     private int getCurrentWidth() {
    581         return isLandscape
    582                 ? Math.max(widthPx, heightPx)
    583                 : Math.min(widthPx, heightPx);
    584     }
    585 
    586     private int getCurrentHeight() {
    587         return isLandscape
    588                 ? Math.min(widthPx, heightPx)
    589                 : Math.max(widthPx, heightPx);
    590     }
    591 }
    592