blob: ddd300257d1e95a4a13274ecdfe25cd5de2d0388 [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;
Jorim Jaggid017f882014-01-14 17:08:48 -080034import android.view.ViewGroup;
Winson Chungb3800242013-10-24 11:01:54 -070035import android.view.ViewGroup.LayoutParams;
Sunny Goyal8dfe2da2014-10-24 16:26:52 -070036import android.view.ViewGroup.MarginLayoutParams;
Winson Chungb3800242013-10-24 11:01:54 -070037import android.view.WindowManager;
38import android.widget.FrameLayout;
Adam Cohen24ce0b32014-01-14 16:18:14 -080039import android.widget.LinearLayout;
Winson Chungb3800242013-10-24 11:01:54 -070040
41import java.util.ArrayList;
42import java.util.Collections;
43import java.util.Comparator;
44
45
46class DeviceProfileQuery {
Winson Chungbe876472014-05-14 14:15:20 -070047 DeviceProfile profile;
Winson Chungb3800242013-10-24 11:01:54 -070048 float widthDps;
49 float heightDps;
50 float value;
51 PointF dimens;
52
Winson Chungbe876472014-05-14 14:15:20 -070053 DeviceProfileQuery(DeviceProfile p, float v) {
54 widthDps = p.minWidthDps;
55 heightDps = p.minHeightDps;
Winson Chungb3800242013-10-24 11:01:54 -070056 value = v;
Winson Chungbe876472014-05-14 14:15:20 -070057 dimens = new PointF(widthDps, heightDps);
58 profile = p;
Winson Chungb3800242013-10-24 11:01:54 -070059 }
60}
61
62public class DeviceProfile {
63 public static interface DeviceProfileCallbacks {
64 public void onAvailableSizeChanged(DeviceProfile grid);
65 }
66
67 String name;
68 float minWidthDps;
69 float minHeightDps;
Adam Cohen59400422014-03-05 18:07:04 -080070 public float numRows;
71 public float numColumns;
Winson Chungb3800242013-10-24 11:01:54 -070072 float numHotseatIcons;
Adam Cohen4ae96ce2014-08-29 15:05:48 -070073 float iconSize;
Winson Chungb3800242013-10-24 11:01:54 -070074 private float iconTextSize;
75 private int iconDrawablePaddingOriginalPx;
76 private float hotseatIconSize;
77
Winson Chungbe876472014-05-14 14:15:20 -070078 int defaultLayoutId;
Winson Chungbe876472014-05-14 14:15:20 -070079
Winson Chungb3800242013-10-24 11:01:54 -070080 boolean isLandscape;
81 boolean isTablet;
82 boolean isLargeTablet;
Winson Chung42b3c062013-12-04 12:09:59 -080083 boolean isLayoutRtl;
Winson Chungb3800242013-10-24 11:01:54 -070084 boolean transposeLayoutWithOrientation;
85
86 int desiredWorkspaceLeftRightMarginPx;
Adam Cohen59400422014-03-05 18:07:04 -080087 public int edgeMarginPx;
Winson Chungb3800242013-10-24 11:01:54 -070088 Rect defaultWidgetPadding;
89
90 int widthPx;
91 int heightPx;
Adam Cohen59400422014-03-05 18:07:04 -080092 public int availableWidthPx;
93 public int availableHeightPx;
Winson Chungb3800242013-10-24 11:01:54 -070094 int defaultPageSpacingPx;
95
96 int overviewModeMinIconZoneHeightPx;
97 int overviewModeMaxIconZoneHeightPx;
Jorim Jaggid017f882014-01-14 17:08:48 -080098 int overviewModeBarItemWidthPx;
99 int overviewModeBarSpacerWidthPx;
Winson Chungb3800242013-10-24 11:01:54 -0700100 float overviewModeIconZoneRatio;
101 float overviewModeScaleFactor;
102
Adam Cohen59400422014-03-05 18:07:04 -0800103 public int cellWidthPx;
104 public int cellHeightPx;
105
Winson Chungb3800242013-10-24 11:01:54 -0700106 int iconSizePx;
107 int iconTextSizePx;
108 int iconDrawablePaddingPx;
Winson Chungb3800242013-10-24 11:01:54 -0700109 int allAppsIconSizePx;
110 int allAppsIconTextSizePx;
111 int allAppsCellWidthPx;
112 int allAppsCellHeightPx;
113 int allAppsCellPaddingPx;
114 int folderBackgroundOffset;
115 int folderIconSizePx;
116 int folderCellWidthPx;
117 int folderCellHeightPx;
118 int hotseatCellWidthPx;
119 int hotseatCellHeightPx;
120 int hotseatIconSizePx;
121 int hotseatBarHeightPx;
122 int hotseatAllAppsRank;
123 int allAppsNumRows;
124 int allAppsNumCols;
Winson Chung93f98ea2015-03-10 16:28:47 -0700125 int appsViewNumCols;
Winson Chungb3800242013-10-24 11:01:54 -0700126 int searchBarSpaceWidthPx;
Winson Chungb3800242013-10-24 11:01:54 -0700127 int searchBarSpaceHeightPx;
Winson Chungb3800242013-10-24 11:01:54 -0700128 int pageIndicatorHeightPx;
Adam Cohen63f1ec02014-08-12 09:23:13 -0700129 int allAppsButtonVisualSize;
Winson Chungb3800242013-10-24 11:01:54 -0700130
Winson Chung59a488a2013-12-10 12:32:14 -0800131 float dragViewScale;
132
Adam Cohen4ae96ce2014-08-29 15:05:48 -0700133 int allAppsShortEdgeCount = -1;
134 int allAppsLongEdgeCount = -1;
135
Winson Chungb3800242013-10-24 11:01:54 -0700136 private ArrayList<DeviceProfileCallbacks> mCallbacks = new ArrayList<DeviceProfileCallbacks>();
137
138 DeviceProfile(String n, float w, float h, float r, float c,
Adam Cohencee8c662014-10-16 09:49:52 -0700139 float is, float its, float hs, float his, int dlId) {
Winson Chungb3800242013-10-24 11:01:54 -0700140 // Ensure that we have an odd number of hotseat items (since we need to place all apps)
Sunny Goyalc9acdd52015-02-26 12:34:42 -0800141 if (hs % 2 == 0) {
Winson Chungb3800242013-10-24 11:01:54 -0700142 throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces");
143 }
144
145 name = n;
146 minWidthDps = w;
147 minHeightDps = h;
148 numRows = r;
149 numColumns = c;
150 iconSize = is;
151 iconTextSize = its;
152 numHotseatIcons = hs;
153 hotseatIconSize = his;
Winson Chungbe876472014-05-14 14:15:20 -0700154 defaultLayoutId = dlId;
Winson Chungb3800242013-10-24 11:01:54 -0700155 }
156
Adam Cohen4ae96ce2014-08-29 15:05:48 -0700157 DeviceProfile() {
158 }
159
Winson Chungb3800242013-10-24 11:01:54 -0700160 DeviceProfile(Context context,
161 ArrayList<DeviceProfile> profiles,
162 float minWidth, float minHeight,
163 int wPx, int hPx,
164 int awPx, int ahPx,
165 Resources res) {
166 DisplayMetrics dm = res.getDisplayMetrics();
167 ArrayList<DeviceProfileQuery> points =
168 new ArrayList<DeviceProfileQuery>();
169 transposeLayoutWithOrientation =
170 res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
171 minWidthDps = minWidth;
172 minHeightDps = minHeight;
173
174 ComponentName cn = new ComponentName(context.getPackageName(),
175 this.getClass().getName());
176 defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
177 edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
178 desiredWorkspaceLeftRightMarginPx = 2 * edgeMarginPx;
179 pageIndicatorHeightPx =
180 res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height);
181 defaultPageSpacingPx =
182 res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing);
183 allAppsCellPaddingPx =
184 res.getDimensionPixelSize(R.dimen.dynamic_grid_all_apps_cell_padding);
185 overviewModeMinIconZoneHeightPx =
186 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_min_icon_zone_height);
187 overviewModeMaxIconZoneHeightPx =
188 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_max_icon_zone_height);
Jorim Jaggid017f882014-01-14 17:08:48 -0800189 overviewModeBarItemWidthPx =
190 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_item_width);
191 overviewModeBarSpacerWidthPx =
192 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_spacer_width);
Winson Chungb3800242013-10-24 11:01:54 -0700193 overviewModeIconZoneRatio =
194 res.getInteger(R.integer.config_dynamic_grid_overview_icon_zone_percentage) / 100f;
195 overviewModeScaleFactor =
196 res.getInteger(R.integer.config_dynamic_grid_overview_scale_percentage) / 100f;
197
Winson Chungbe876472014-05-14 14:15:20 -0700198 // Find the closes profile given the width/height
Winson Chungb3800242013-10-24 11:01:54 -0700199 for (DeviceProfile p : profiles) {
Winson Chungbe876472014-05-14 14:15:20 -0700200 points.add(new DeviceProfileQuery(p, 0f));
Winson Chungb3800242013-10-24 11:01:54 -0700201 }
Winson Chungbe876472014-05-14 14:15:20 -0700202 DeviceProfile closestProfile = findClosestDeviceProfile(minWidth, minHeight, points);
203
204 // Snap to the closest row count
205 numRows = closestProfile.numRows;
206
207 // Snap to the closest column count
208 numColumns = closestProfile.numColumns;
209
210 // Snap to the closest hotseat size
211 numHotseatIcons = closestProfile.numHotseatIcons;
Winson Chungb3800242013-10-24 11:01:54 -0700212 hotseatAllAppsRank = (int) (numHotseatIcons / 2);
213
Winson Chungbe876472014-05-14 14:15:20 -0700214 // Snap to the closest default layout id
215 defaultLayoutId = closestProfile.defaultLayoutId;
216
Winson Chungb3800242013-10-24 11:01:54 -0700217 // Interpolate the icon size
218 points.clear();
219 for (DeviceProfile p : profiles) {
Winson Chungbe876472014-05-14 14:15:20 -0700220 points.add(new DeviceProfileQuery(p, p.iconSize));
Winson Chungb3800242013-10-24 11:01:54 -0700221 }
222 iconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
Adam Cohen4ae96ce2014-08-29 15:05:48 -0700223
Winson Chungb3800242013-10-24 11:01:54 -0700224 // AllApps uses the original non-scaled icon size
225 allAppsIconSizePx = DynamicGrid.pxFromDp(iconSize, dm);
226
227 // Interpolate the icon text size
228 points.clear();
229 for (DeviceProfile p : profiles) {
Winson Chungbe876472014-05-14 14:15:20 -0700230 points.add(new DeviceProfileQuery(p, p.iconTextSize));
Winson Chungb3800242013-10-24 11:01:54 -0700231 }
232 iconTextSize = invDistWeightedInterpolate(minWidth, minHeight, points);
233 iconDrawablePaddingOriginalPx =
234 res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);
235 // AllApps uses the original non-scaled icon text size
236 allAppsIconTextSizePx = DynamicGrid.pxFromDp(iconTextSize, dm);
237
238 // Interpolate the hotseat icon size
239 points.clear();
240 for (DeviceProfile p : profiles) {
Winson Chungbe876472014-05-14 14:15:20 -0700241 points.add(new DeviceProfileQuery(p, p.hotseatIconSize));
Winson Chungb3800242013-10-24 11:01:54 -0700242 }
243 // Hotseat
244 hotseatIconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
245
Adam Cohen4ae96ce2014-08-29 15:05:48 -0700246 // If the partner customization apk contains any grid overrides, apply them
247 applyPartnerDeviceProfileOverrides(context, dm);
248
Winson Chungb3800242013-10-24 11:01:54 -0700249 // Calculate the remaining vars
250 updateFromConfiguration(context, res, wPx, hPx, awPx, ahPx);
251 updateAvailableDimensions(context);
Adam Cohen63f1ec02014-08-12 09:23:13 -0700252 computeAllAppsButtonSize(context);
253 }
254
255 /**
Adam Cohen4ae96ce2014-08-29 15:05:48 -0700256 * Apply any Partner customization grid overrides.
257 *
258 * Currently we support: all apps row / column count.
259 */
260 private void applyPartnerDeviceProfileOverrides(Context ctx, DisplayMetrics dm) {
261 Partner p = Partner.get(ctx.getPackageManager());
262 if (p != null) {
263 DeviceProfile partnerDp = p.getDeviceProfileOverride(dm);
264 if (partnerDp != null) {
265 if (partnerDp.numRows > 0 && partnerDp.numColumns > 0) {
266 numRows = partnerDp.numRows;
267 numColumns = partnerDp.numColumns;
268 }
269 if (partnerDp.allAppsShortEdgeCount > 0 && partnerDp.allAppsLongEdgeCount > 0) {
270 allAppsShortEdgeCount = partnerDp.allAppsShortEdgeCount;
271 allAppsLongEdgeCount = partnerDp.allAppsLongEdgeCount;
272 }
273 if (partnerDp.iconSize > 0) {
274 iconSize = partnerDp.iconSize;
275 // AllApps uses the original non-scaled icon size
276 allAppsIconSizePx = DynamicGrid.pxFromDp(iconSize, dm);
277 }
278 }
279 }
280 }
281
282 /**
Adam Cohen63f1ec02014-08-12 09:23:13 -0700283 * Determine the exact visual footprint of the all apps button, taking into account scaling
284 * and internal padding of the drawable.
285 */
286 private void computeAllAppsButtonSize(Context context) {
287 Resources res = context.getResources();
288 float padding = res.getInteger(R.integer.config_allAppsButtonPaddingPercent) / 100f;
289 LauncherAppState app = LauncherAppState.getInstance();
290 allAppsButtonVisualSize = (int) (hotseatIconSizePx * (1 - padding));
Winson Chungb3800242013-10-24 11:01:54 -0700291 }
292
293 void addCallback(DeviceProfileCallbacks cb) {
294 mCallbacks.add(cb);
295 cb.onAvailableSizeChanged(this);
296 }
297 void removeCallback(DeviceProfileCallbacks cb) {
298 mCallbacks.remove(cb);
299 }
300
301 private int getDeviceOrientation(Context context) {
302 WindowManager windowManager = (WindowManager)
303 context.getSystemService(Context.WINDOW_SERVICE);
304 Resources resources = context.getResources();
305 DisplayMetrics dm = resources.getDisplayMetrics();
306 Configuration config = resources.getConfiguration();
307 int rotation = windowManager.getDefaultDisplay().getRotation();
308
309 boolean isLandscape = (config.orientation == Configuration.ORIENTATION_LANDSCAPE) &&
310 (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180);
311 boolean isRotatedPortrait = (config.orientation == Configuration.ORIENTATION_PORTRAIT) &&
312 (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
313 if (isLandscape || isRotatedPortrait) {
314 return CellLayout.LANDSCAPE;
315 } else {
316 return CellLayout.PORTRAIT;
317 }
318 }
319
320 private void updateAvailableDimensions(Context context) {
321 WindowManager windowManager = (WindowManager)
322 context.getSystemService(Context.WINDOW_SERVICE);
323 Display display = windowManager.getDefaultDisplay();
324 Resources resources = context.getResources();
325 DisplayMetrics dm = resources.getDisplayMetrics();
326 Configuration config = resources.getConfiguration();
327
328 // There are three possible configurations that the dynamic grid accounts for, portrait,
329 // landscape with the nav bar at the bottom, and landscape with the nav bar at the side.
330 // To prevent waiting for fitSystemWindows(), we make the observation that in landscape,
331 // the height is the smallest height (either with the nav bar at the bottom or to the
332 // side) and otherwise, the height is simply the largest possible height for a portrait
333 // device.
334 Point size = new Point();
335 Point smallestSize = new Point();
336 Point largestSize = new Point();
337 display.getSize(size);
338 display.getCurrentSizeRange(smallestSize, largestSize);
339 availableWidthPx = size.x;
340 if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
341 availableHeightPx = smallestSize.y;
342 } else {
343 availableHeightPx = largestSize.y;
344 }
345
346 // Check to see if the icons fit in the new available height. If not, then we need to
347 // shrink the icon size.
Winson Chungb3800242013-10-24 11:01:54 -0700348 float scale = 1f;
349 int drawablePadding = iconDrawablePaddingOriginalPx;
350 updateIconSize(1f, drawablePadding, resources, dm);
351 float usedHeight = (cellHeightPx * numRows);
Winson Chung59a488a2013-12-10 12:32:14 -0800352
353 Rect workspacePadding = getWorkspacePadding();
Winson Chungb3800242013-10-24 11:01:54 -0700354 int maxHeight = (availableHeightPx - workspacePadding.top - workspacePadding.bottom);
355 if (usedHeight > maxHeight) {
356 scale = maxHeight / usedHeight;
357 drawablePadding = 0;
358 }
359 updateIconSize(scale, drawablePadding, resources, dm);
360
361 // Make the callbacks
362 for (DeviceProfileCallbacks cb : mCallbacks) {
363 cb.onAvailableSizeChanged(this);
364 }
365 }
366
Winson Chung93f98ea2015-03-10 16:28:47 -0700367 private void updateIconSize(float scale, int drawablePadding, Resources res,
Winson Chungb3800242013-10-24 11:01:54 -0700368 DisplayMetrics dm) {
369 iconSizePx = (int) (DynamicGrid.pxFromDp(iconSize, dm) * scale);
370 iconTextSizePx = (int) (DynamicGrid.pxFromSp(iconTextSize, dm) * scale);
371 iconDrawablePaddingPx = drawablePadding;
372 hotseatIconSizePx = (int) (DynamicGrid.pxFromDp(hotseatIconSize, dm) * scale);
373
374 // Search Bar
Sunny Goyal594d76d2014-11-06 10:12:54 -0800375 searchBarSpaceWidthPx = Math.min(widthPx,
Winson Chung93f98ea2015-03-10 16:28:47 -0700376 res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_max_width));
Sunny Goyal594d76d2014-11-06 10:12:54 -0800377 searchBarSpaceHeightPx = getSearchBarTopOffset()
Winson Chung93f98ea2015-03-10 16:28:47 -0700378 + res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_height);
Winson Chungb3800242013-10-24 11:01:54 -0700379
380 // Calculate the actual text height
381 Paint textPaint = new Paint();
382 textPaint.setTextSize(iconTextSizePx);
383 FontMetrics fm = textPaint.getFontMetrics();
384 cellWidthPx = iconSizePx;
385 cellHeightPx = iconSizePx + iconDrawablePaddingPx + (int) Math.ceil(fm.bottom - fm.top);
Winson Chung93f98ea2015-03-10 16:28:47 -0700386 final float scaleDps = res.getDimensionPixelSize(R.dimen.dragViewScale);
Winson Chung59a488a2013-12-10 12:32:14 -0800387 dragViewScale = (iconSizePx + scaleDps) / iconSizePx;
Winson Chungb3800242013-10-24 11:01:54 -0700388
389 // Hotseat
390 hotseatBarHeightPx = iconSizePx + 4 * edgeMarginPx;
391 hotseatCellWidthPx = iconSizePx;
392 hotseatCellHeightPx = iconSizePx;
393
394 // Folder
395 folderCellWidthPx = cellWidthPx + 3 * edgeMarginPx;
396 folderCellHeightPx = cellHeightPx + edgeMarginPx;
397 folderBackgroundOffset = -edgeMarginPx;
398 folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
399
400 // All Apps
Winson Chungb3800242013-10-24 11:01:54 -0700401 allAppsCellWidthPx = allAppsIconSizePx;
402 allAppsCellHeightPx = allAppsIconSizePx + drawablePadding + iconTextSizePx;
403 int maxLongEdgeCellCount =
Winson Chung93f98ea2015-03-10 16:28:47 -0700404 res.getInteger(R.integer.config_dynamic_grid_max_long_edge_cell_count);
Winson Chungb3800242013-10-24 11:01:54 -0700405 int maxShortEdgeCellCount =
Winson Chung93f98ea2015-03-10 16:28:47 -0700406 res.getInteger(R.integer.config_dynamic_grid_max_short_edge_cell_count);
Winson Chungb3800242013-10-24 11:01:54 -0700407 int minEdgeCellCount =
Winson Chung93f98ea2015-03-10 16:28:47 -0700408 res.getInteger(R.integer.config_dynamic_grid_min_edge_cell_count);
Winson Chungb3800242013-10-24 11:01:54 -0700409 int maxRows = (isLandscape ? maxShortEdgeCellCount : maxLongEdgeCellCount);
410 int maxCols = (isLandscape ? maxLongEdgeCellCount : maxShortEdgeCellCount);
411
Adam Cohen4ae96ce2014-08-29 15:05:48 -0700412 if (allAppsShortEdgeCount > 0 && allAppsLongEdgeCount > 0) {
413 allAppsNumRows = isLandscape ? allAppsShortEdgeCount : allAppsLongEdgeCount;
414 allAppsNumCols = isLandscape ? allAppsLongEdgeCount : allAppsShortEdgeCount;
415 } else {
416 allAppsNumRows = (availableHeightPx - pageIndicatorHeightPx) /
417 (allAppsCellHeightPx + allAppsCellPaddingPx);
418 allAppsNumRows = Math.max(minEdgeCellCount, Math.min(maxRows, allAppsNumRows));
Winson Chung93f98ea2015-03-10 16:28:47 -0700419 allAppsNumCols = (availableWidthPx) / (allAppsCellWidthPx + allAppsCellPaddingPx);
Adam Cohen4ae96ce2014-08-29 15:05:48 -0700420 allAppsNumCols = Math.max(minEdgeCellCount, Math.min(maxCols, allAppsNumCols));
421 }
Winson Chung93f98ea2015-03-10 16:28:47 -0700422
423 int appsContainerViewPx = res.getDimensionPixelSize(R.dimen.apps_container_width);
424 int appsViewLeftMarginPx =
425 res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin);
426 int availableAppsWidthPx = (appsContainerViewPx > 0) ? appsContainerViewPx :
427 availableWidthPx;
428 appsViewNumCols = (availableAppsWidthPx - appsViewLeftMarginPx) /
429 (allAppsCellWidthPx + allAppsCellPaddingPx);
Winson Chungb3800242013-10-24 11:01:54 -0700430 }
431
432 void updateFromConfiguration(Context context, Resources resources, int wPx, int hPx,
433 int awPx, int ahPx) {
Winson Chung42b3c062013-12-04 12:09:59 -0800434 Configuration configuration = resources.getConfiguration();
435 isLandscape = (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE);
Winson Chungb3800242013-10-24 11:01:54 -0700436 isTablet = resources.getBoolean(R.bool.is_tablet);
437 isLargeTablet = resources.getBoolean(R.bool.is_large_tablet);
Winson Chung6033ceb2014-02-05 12:37:42 -0800438 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
439 isLayoutRtl = (configuration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
440 } else {
441 isLayoutRtl = false;
442 }
Winson Chungb3800242013-10-24 11:01:54 -0700443 widthPx = wPx;
444 heightPx = hPx;
445 availableWidthPx = awPx;
446 availableHeightPx = ahPx;
447
448 updateAvailableDimensions(context);
449 }
450
451 private float dist(PointF p0, PointF p1) {
452 return (float) Math.sqrt((p1.x - p0.x)*(p1.x-p0.x) +
453 (p1.y-p0.y)*(p1.y-p0.y));
454 }
455
456 private float weight(PointF a, PointF b,
457 float pow) {
458 float d = dist(a, b);
459 if (d == 0f) {
460 return Float.POSITIVE_INFINITY;
461 }
462 return (float) (1f / Math.pow(d, pow));
463 }
464
Winson Chungbe876472014-05-14 14:15:20 -0700465 /** Returns the closest device profile given the width and height and a list of profiles */
466 private DeviceProfile findClosestDeviceProfile(float width, float height,
467 ArrayList<DeviceProfileQuery> points) {
468 return findClosestDeviceProfiles(width, height, points).get(0).profile;
469 }
470
471 /** Returns the closest device profiles ordered by closeness to the specified width and height */
472 private ArrayList<DeviceProfileQuery> findClosestDeviceProfiles(float width, float height,
473 ArrayList<DeviceProfileQuery> points) {
474 final PointF xy = new PointF(width, height);
475
476 // Sort the profiles by their closeness to the dimensions
477 ArrayList<DeviceProfileQuery> pointsByNearness = points;
478 Collections.sort(pointsByNearness, new Comparator<DeviceProfileQuery>() {
479 public int compare(DeviceProfileQuery a, DeviceProfileQuery b) {
480 return (int) (dist(xy, a.dimens) - dist(xy, b.dimens));
481 }
482 });
483
484 return pointsByNearness;
485 }
486
Winson Chungb3800242013-10-24 11:01:54 -0700487 private float invDistWeightedInterpolate(float width, float height,
488 ArrayList<DeviceProfileQuery> points) {
489 float sum = 0;
490 float weights = 0;
491 float pow = 5;
492 float kNearestNeighbors = 3;
493 final PointF xy = new PointF(width, height);
494
Winson Chungbe876472014-05-14 14:15:20 -0700495 ArrayList<DeviceProfileQuery> pointsByNearness = findClosestDeviceProfiles(width, height,
496 points);
Winson Chungb3800242013-10-24 11:01:54 -0700497
498 for (int i = 0; i < pointsByNearness.size(); ++i) {
499 DeviceProfileQuery p = pointsByNearness.get(i);
500 if (i < kNearestNeighbors) {
501 float w = weight(xy, p.dimens, pow);
502 if (w == Float.POSITIVE_INFINITY) {
503 return p.value;
504 }
505 weights += w;
506 }
507 }
508
509 for (int i = 0; i < pointsByNearness.size(); ++i) {
510 DeviceProfileQuery p = pointsByNearness.get(i);
511 if (i < kNearestNeighbors) {
512 float w = weight(xy, p.dimens, pow);
513 sum += w * p.value / weights;
514 }
515 }
516
517 return sum;
518 }
519
Winson Chung69e04ea2013-12-02 14:43:44 -0800520 /** Returns the search bar top offset */
521 int getSearchBarTopOffset() {
522 if (isTablet() && !isVerticalBarLayout()) {
523 return 4 * edgeMarginPx;
524 } else {
525 return 2 * edgeMarginPx;
526 }
527 }
528
Winson Chungb3800242013-10-24 11:01:54 -0700529 /** Returns the search bar bounds in the current orientation */
530 Rect getSearchBarBounds() {
531 return getSearchBarBounds(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
532 }
533 /** Returns the search bar bounds in the specified orientation */
534 Rect getSearchBarBounds(int orientation) {
535 Rect bounds = new Rect();
536 if (orientation == CellLayout.LANDSCAPE &&
537 transposeLayoutWithOrientation) {
Winson Chung42b3c062013-12-04 12:09:59 -0800538 if (isLayoutRtl) {
539 bounds.set(availableWidthPx - searchBarSpaceHeightPx, edgeMarginPx,
540 availableWidthPx, availableHeightPx - edgeMarginPx);
541 } else {
542 bounds.set(0, edgeMarginPx, searchBarSpaceHeightPx,
543 availableHeightPx - edgeMarginPx);
544 }
Winson Chungb3800242013-10-24 11:01:54 -0700545 } else {
546 if (isTablet()) {
547 // Pad the left and right of the workspace to ensure consistent spacing
548 // between all icons
549 int width = (orientation == CellLayout.LANDSCAPE)
550 ? Math.max(widthPx, heightPx)
551 : Math.min(widthPx, heightPx);
552 // XXX: If the icon size changes across orientations, we will have to take
553 // that into account here too.
554 int gap = (int) ((width - 2 * edgeMarginPx -
555 (numColumns * cellWidthPx)) / (2 * (numColumns + 1)));
Winson Chung2cb24712013-12-02 15:00:39 -0800556 bounds.set(edgeMarginPx + gap, getSearchBarTopOffset(),
557 availableWidthPx - (edgeMarginPx + gap),
Winson Chungb3800242013-10-24 11:01:54 -0700558 searchBarSpaceHeightPx);
559 } else {
Winson Chung2cb24712013-12-02 15:00:39 -0800560 bounds.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left,
561 getSearchBarTopOffset(),
Winson Chungb3800242013-10-24 11:01:54 -0700562 availableWidthPx - (desiredWorkspaceLeftRightMarginPx -
563 defaultWidgetPadding.right), searchBarSpaceHeightPx);
564 }
565 }
566 return bounds;
567 }
568
Winson Chunga6945242014-01-08 14:04:34 -0800569 /** Returns the bounds of the workspace page indicators. */
570 Rect getWorkspacePageIndicatorBounds(Rect insets) {
571 Rect workspacePadding = getWorkspacePadding();
Winson Chung205cd772014-01-15 14:31:59 -0800572 if (isLandscape && transposeLayoutWithOrientation) {
573 if (isLayoutRtl) {
574 return new Rect(workspacePadding.left, workspacePadding.top,
575 workspacePadding.left + pageIndicatorHeightPx,
576 heightPx - workspacePadding.bottom - insets.bottom);
577 } else {
578 int pageIndicatorLeft = widthPx - workspacePadding.right;
579 return new Rect(pageIndicatorLeft, workspacePadding.top,
580 pageIndicatorLeft + pageIndicatorHeightPx,
581 heightPx - workspacePadding.bottom - insets.bottom);
582 }
583 } else {
584 int pageIndicatorTop = heightPx - insets.bottom - workspacePadding.bottom;
585 return new Rect(workspacePadding.left, pageIndicatorTop,
586 widthPx - workspacePadding.right, pageIndicatorTop + pageIndicatorHeightPx);
587 }
Winson Chunga6945242014-01-08 14:04:34 -0800588 }
589
Adam Cohen59400422014-03-05 18:07:04 -0800590 public int getWorkspaceGridHeight() {
591 Rect p = getWorkspacePadding();
592 return availableHeightPx - p.top - p.bottom;
593 }
594
595 public int getWorkspaceGridWidth() {
596 Rect p = getWorkspacePadding();
597 return availableWidthPx - p.left - p.right;
598 }
599
Winson Chungb3800242013-10-24 11:01:54 -0700600 /** Returns the workspace padding in the specified orientation */
601 Rect getWorkspacePadding() {
602 return getWorkspacePadding(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
603 }
604 Rect getWorkspacePadding(int orientation) {
605 Rect searchBarBounds = getSearchBarBounds(orientation);
606 Rect padding = new Rect();
607 if (orientation == CellLayout.LANDSCAPE &&
608 transposeLayoutWithOrientation) {
609 // Pad the left and right of the workspace with search/hotseat bar sizes
Winson Chung42b3c062013-12-04 12:09:59 -0800610 if (isLayoutRtl) {
611 padding.set(hotseatBarHeightPx, edgeMarginPx,
612 searchBarBounds.width(), edgeMarginPx);
613 } else {
614 padding.set(searchBarBounds.width(), edgeMarginPx,
615 hotseatBarHeightPx, edgeMarginPx);
616 }
Winson Chungb3800242013-10-24 11:01:54 -0700617 } else {
618 if (isTablet()) {
619 // Pad the left and right of the workspace to ensure consistent spacing
620 // between all icons
Winson Chung59a488a2013-12-10 12:32:14 -0800621 float gapScale = 1f + (dragViewScale - 1f) / 2f;
Winson Chungb3800242013-10-24 11:01:54 -0700622 int width = (orientation == CellLayout.LANDSCAPE)
623 ? Math.max(widthPx, heightPx)
624 : Math.min(widthPx, heightPx);
Winson Chung59a488a2013-12-10 12:32:14 -0800625 int height = (orientation != CellLayout.LANDSCAPE)
626 ? Math.max(widthPx, heightPx)
627 : Math.min(widthPx, heightPx);
628 int paddingTop = searchBarBounds.bottom;
629 int paddingBottom = hotseatBarHeightPx + pageIndicatorHeightPx;
630 int availableWidth = Math.max(0, width - (int) ((numColumns * cellWidthPx) +
631 (numColumns * gapScale * cellWidthPx)));
632 int availableHeight = Math.max(0, height - paddingTop - paddingBottom
633 - (int) (2 * numRows * cellHeightPx));
634 padding.set(availableWidth / 2, paddingTop + availableHeight / 2,
635 availableWidth / 2, paddingBottom + availableHeight / 2);
Winson Chungb3800242013-10-24 11:01:54 -0700636 } else {
637 // Pad the top and bottom of the workspace with search/hotseat bar sizes
638 padding.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left,
639 searchBarBounds.bottom,
640 desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.right,
641 hotseatBarHeightPx + pageIndicatorHeightPx);
642 }
643 }
644 return padding;
645 }
646
647 int getWorkspacePageSpacing(int orientation) {
Winson Chung59a488a2013-12-10 12:32:14 -0800648 if ((orientation == CellLayout.LANDSCAPE &&
649 transposeLayoutWithOrientation) || isLargeTablet()) {
Winson Chungb3800242013-10-24 11:01:54 -0700650 // In landscape mode the page spacing is set to the default.
651 return defaultPageSpacingPx;
652 } else {
653 // In portrait, we want the pages spaced such that there is no
654 // overhang of the previous / next page into the current page viewport.
655 // We assume symmetrical padding in portrait mode.
Adam Cohenefb31e32014-01-16 16:07:50 -0800656 return Math.max(defaultPageSpacingPx, 2 * getWorkspacePadding().left);
Winson Chungb3800242013-10-24 11:01:54 -0700657 }
658 }
659
660 Rect getOverviewModeButtonBarRect() {
661 int zoneHeight = (int) (overviewModeIconZoneRatio * availableHeightPx);
662 zoneHeight = Math.min(overviewModeMaxIconZoneHeightPx,
663 Math.max(overviewModeMinIconZoneHeightPx, zoneHeight));
664 return new Rect(0, availableHeightPx - zoneHeight, 0, availableHeightPx);
665 }
666
667 float getOverviewModeScale() {
668 Rect workspacePadding = getWorkspacePadding();
669 Rect overviewBar = getOverviewModeButtonBarRect();
670 int pageSpace = availableHeightPx - workspacePadding.top - workspacePadding.bottom;
671 return (overviewModeScaleFactor * (pageSpace - overviewBar.height())) / pageSpace;
672 }
673
674 // The rect returned will be extended to below the system ui that covers the workspace
675 Rect getHotseatRect() {
676 if (isVerticalBarLayout()) {
677 return new Rect(availableWidthPx - hotseatBarHeightPx, 0,
678 Integer.MAX_VALUE, availableHeightPx);
679 } else {
680 return new Rect(0, availableHeightPx - hotseatBarHeightPx,
681 availableWidthPx, Integer.MAX_VALUE);
682 }
683 }
684
685 int calculateCellWidth(int width, int countX) {
686 return width / countX;
687 }
688 int calculateCellHeight(int height, int countY) {
689 return height / countY;
690 }
691
692 boolean isPhone() {
693 return !isTablet && !isLargeTablet;
694 }
695 boolean isTablet() {
696 return isTablet;
697 }
698 boolean isLargeTablet() {
699 return isLargeTablet;
700 }
701
702 boolean isVerticalBarLayout() {
703 return isLandscape && transposeLayoutWithOrientation;
704 }
705
706 boolean shouldFadeAdjacentWorkspaceScreens() {
707 return isVerticalBarLayout() || isLargeTablet();
708 }
709
Jorim Jaggid017f882014-01-14 17:08:48 -0800710 int getVisibleChildCount(ViewGroup parent) {
711 int visibleChildren = 0;
712 for (int i = 0; i < parent.getChildCount(); i++) {
713 if (parent.getChildAt(i).getVisibility() != View.GONE) {
714 visibleChildren++;
715 }
716 }
717 return visibleChildren;
718 }
719
Winson Chungb3800242013-10-24 11:01:54 -0700720 public void layout(Launcher launcher) {
721 FrameLayout.LayoutParams lp;
722 Resources res = launcher.getResources();
723 boolean hasVerticalBarLayout = isVerticalBarLayout();
724
725 // Layout the search bar space
726 View searchBar = launcher.getSearchBar();
727 lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
728 if (hasVerticalBarLayout) {
Winson Chung69e04ea2013-12-02 14:43:44 -0800729 // Vertical search bar space
Winson Chungb3800242013-10-24 11:01:54 -0700730 lp.gravity = Gravity.TOP | Gravity.LEFT;
731 lp.width = searchBarSpaceHeightPx;
Adam Cohen24ce0b32014-01-14 16:18:14 -0800732 lp.height = LayoutParams.WRAP_CONTENT;
Adam Cohen24ce0b32014-01-14 16:18:14 -0800733
734 LinearLayout targets = (LinearLayout) searchBar.findViewById(R.id.drag_target_bar);
735 targets.setOrientation(LinearLayout.VERTICAL);
Winson Chungb3800242013-10-24 11:01:54 -0700736 } else {
Winson Chung69e04ea2013-12-02 14:43:44 -0800737 // Horizontal search bar space
Winson Chungb3800242013-10-24 11:01:54 -0700738 lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
739 lp.width = searchBarSpaceWidthPx;
740 lp.height = searchBarSpaceHeightPx;
Winson Chungb3800242013-10-24 11:01:54 -0700741 }
742 searchBar.setLayoutParams(lp);
743
Winson Chungb3800242013-10-24 11:01:54 -0700744 // Layout the workspace
745 PagedView workspace = (PagedView) launcher.findViewById(R.id.workspace);
746 lp = (FrameLayout.LayoutParams) workspace.getLayoutParams();
747 lp.gravity = Gravity.CENTER;
748 int orientation = isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT;
749 Rect padding = getWorkspacePadding(orientation);
750 workspace.setLayoutParams(lp);
751 workspace.setPadding(padding.left, padding.top, padding.right, padding.bottom);
752 workspace.setPageSpacing(getWorkspacePageSpacing(orientation));
753
754 // Layout the hotseat
755 View hotseat = launcher.findViewById(R.id.hotseat);
756 lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams();
757 if (hasVerticalBarLayout) {
758 // Vertical hotseat
Winson Chung42b3c062013-12-04 12:09:59 -0800759 lp.gravity = Gravity.END;
Winson Chungb3800242013-10-24 11:01:54 -0700760 lp.width = hotseatBarHeightPx;
761 lp.height = LayoutParams.MATCH_PARENT;
762 hotseat.findViewById(R.id.layout).setPadding(0, 2 * edgeMarginPx, 0, 2 * edgeMarginPx);
763 } else if (isTablet()) {
Winson Chung59a488a2013-12-10 12:32:14 -0800764 // Pad the hotseat with the workspace padding calculated above
Winson Chungb3800242013-10-24 11:01:54 -0700765 lp.gravity = Gravity.BOTTOM;
766 lp.width = LayoutParams.MATCH_PARENT;
767 lp.height = hotseatBarHeightPx;
Winson Chung59a488a2013-12-10 12:32:14 -0800768 hotseat.setPadding(edgeMarginPx + padding.left, 0,
769 edgeMarginPx + padding.right,
Winson Chungb3800242013-10-24 11:01:54 -0700770 2 * edgeMarginPx);
771 } else {
772 // For phones, layout the hotseat without any bottom margin
773 // to ensure that we have space for the folders
774 lp.gravity = Gravity.BOTTOM;
775 lp.width = LayoutParams.MATCH_PARENT;
776 lp.height = hotseatBarHeightPx;
Sunny Goyald81992b2015-03-05 14:00:18 -0800777 hotseat.setPadding(2 * edgeMarginPx, 0, 2 * edgeMarginPx, 0);
Winson Chungb3800242013-10-24 11:01:54 -0700778 }
779 hotseat.setLayoutParams(lp);
780
781 // Layout the page indicators
782 View pageIndicator = launcher.findViewById(R.id.page_indicator);
783 if (pageIndicator != null) {
784 if (hasVerticalBarLayout) {
785 // Hide the page indicators when we have vertical search/hotseat
786 pageIndicator.setVisibility(View.GONE);
787 } else {
788 // Put the page indicators above the hotseat
789 lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams();
790 lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
791 lp.width = LayoutParams.WRAP_CONTENT;
792 lp.height = LayoutParams.WRAP_CONTENT;
793 lp.bottomMargin = hotseatBarHeightPx;
794 pageIndicator.setLayoutParams(lp);
795 }
796 }
797
798 // Layout AllApps
799 AppsCustomizeTabHost host = (AppsCustomizeTabHost)
800 launcher.findViewById(R.id.apps_customize_pane);
801 if (host != null) {
802 // Center the all apps page indicator
803 int pageIndicatorHeight = (int) (pageIndicatorHeightPx * Math.min(1f,
804 (allAppsIconSizePx / DynamicGrid.DEFAULT_ICON_SIZE_PX)));
805 pageIndicator = host.findViewById(R.id.apps_customize_page_indicator);
806 if (pageIndicator != null) {
Adam Cohen96bb7982014-07-07 11:58:56 -0700807 LinearLayout.LayoutParams lllp = (LinearLayout.LayoutParams) pageIndicator.getLayoutParams();
808 lllp.width = LayoutParams.WRAP_CONTENT;
809 lllp.height = pageIndicatorHeight;
810 pageIndicator.setLayoutParams(lllp);
Winson Chungb3800242013-10-24 11:01:54 -0700811 }
812
813 AppsCustomizePagedView pagedView = (AppsCustomizePagedView)
814 host.findViewById(R.id.apps_customize_pane_content);
Adam Cohen9bfdb762014-07-21 17:44:06 -0700815
816 FrameLayout fakePageContainer = (FrameLayout)
817 host.findViewById(R.id.fake_page_container);
818 FrameLayout fakePage = (FrameLayout) host.findViewById(R.id.fake_page);
819
Winson Chungb3800242013-10-24 11:01:54 -0700820 padding = new Rect();
821 if (pagedView != null) {
822 // Constrain the dimensions of all apps so that it does not span the full width
823 int paddingLR = (availableWidthPx - (allAppsCellWidthPx * allAppsNumCols)) /
824 (2 * (allAppsNumCols + 1));
825 int paddingTB = (availableHeightPx - (allAppsCellHeightPx * allAppsNumRows)) /
826 (2 * (allAppsNumRows + 1));
827 paddingLR = Math.min(paddingLR, (int)((paddingLR + paddingTB) * 0.75f));
828 paddingTB = Math.min(paddingTB, (int)((paddingLR + paddingTB) * 0.75f));
829 int maxAllAppsWidth = (allAppsNumCols * (allAppsCellWidthPx + 2 * paddingLR));
830 int gridPaddingLR = (availableWidthPx - maxAllAppsWidth) / 2;
Winson Chung495f44d2013-12-04 12:51:53 -0800831 // Only adjust the side paddings on landscape phones, or tablets
832 if ((isTablet() || isLandscape) && gridPaddingLR > (allAppsCellWidthPx / 4)) {
Winson Chungb3800242013-10-24 11:01:54 -0700833 padding.left = padding.right = gridPaddingLR;
834 }
Adam Cohen9bfdb762014-07-21 17:44:06 -0700835
Winson Chungb3800242013-10-24 11:01:54 -0700836 // The icons are centered, so we can't just offset by the page indicator height
837 // because the empty space will actually be pageIndicatorHeight + paddingTB
838 padding.bottom = Math.max(0, pageIndicatorHeight - paddingTB);
Adam Cohen9bfdb762014-07-21 17:44:06 -0700839
Winson Chungb3800242013-10-24 11:01:54 -0700840 pagedView.setWidgetsPageIndicatorPadding(pageIndicatorHeight);
Adam Cohen9bfdb762014-07-21 17:44:06 -0700841 fakePage.setBackground(res.getDrawable(R.drawable.quantum_panel));
Adam Cohen96bb7982014-07-07 11:58:56 -0700842
843 // Horizontal padding for the whole paged view
Adam Cohen9bfdb762014-07-21 17:44:06 -0700844 int pagedFixedViewPadding =
Adam Cohen96bb7982014-07-07 11:58:56 -0700845 res.getDimensionPixelSize(R.dimen.apps_customize_horizontal_padding);
Adam Cohen9bfdb762014-07-21 17:44:06 -0700846
847 padding.left += pagedFixedViewPadding;
848 padding.right += pagedFixedViewPadding;
849
850 pagedView.setPadding(padding.left, padding.top, padding.right, padding.bottom);
851 fakePageContainer.setPadding(padding.left, padding.top, padding.right, padding.bottom);
852
Winson Chungb3800242013-10-24 11:01:54 -0700853 }
854 }
855
856 // Layout the Overview Mode
Jorim Jaggid017f882014-01-14 17:08:48 -0800857 ViewGroup overviewMode = launcher.getOverviewPanel();
Winson Chungb3800242013-10-24 11:01:54 -0700858 if (overviewMode != null) {
859 Rect r = getOverviewModeButtonBarRect();
860 lp = (FrameLayout.LayoutParams) overviewMode.getLayoutParams();
861 lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
Sunny Goyal8dfe2da2014-10-24 16:26:52 -0700862
863 int visibleChildCount = getVisibleChildCount(overviewMode);
864 int totalItemWidth = visibleChildCount * overviewModeBarItemWidthPx;
865 int maxWidth = totalItemWidth + (visibleChildCount-1) * overviewModeBarSpacerWidthPx;
866
867 lp.width = Math.min(availableWidthPx, maxWidth);
Winson Chungb3800242013-10-24 11:01:54 -0700868 lp.height = r.height();
869 overviewMode.setLayoutParams(lp);
Sunny Goyal8dfe2da2014-10-24 16:26:52 -0700870
871 if (lp.width > totalItemWidth && visibleChildCount > 1) {
872 // We have enough space. Lets add some margin too.
873 int margin = (lp.width - totalItemWidth) / (visibleChildCount-1);
874 View lastChild = null;
875
876 // Set margin of all visible children except the last visible child
877 for (int i = 0; i < visibleChildCount; i++) {
878 if (lastChild != null) {
879 MarginLayoutParams clp = (MarginLayoutParams) lastChild.getLayoutParams();
880 if (isLayoutRtl) {
881 clp.leftMargin = margin;
882 } else {
883 clp.rightMargin = margin;
884 }
885 lastChild.setLayoutParams(clp);
886 lastChild = null;
887 }
888 View thisChild = overviewMode.getChildAt(i);
889 if (thisChild.getVisibility() != View.GONE) {
890 lastChild = thisChild;
891 }
892 }
893 }
Winson Chungb3800242013-10-24 11:01:54 -0700894 }
895 }
896}