blob: d90deca4fb3c1267e72194adc370c32742f2bba6 [file] [log] [blame]
Winson Chung5f8afe62013-08-12 16:19:28 -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
Winson Chungf7d45852013-10-10 18:57:15 -070019import android.appwidget.AppWidgetHostView;
20import android.content.ComponentName;
21import android.content.Context;
Winson Chung5f8afe62013-08-12 16:19:28 -070022import android.content.res.Configuration;
23import android.content.res.Resources;
24import android.graphics.Paint;
Winson Chung5f8afe62013-08-12 16:19:28 -070025import android.graphics.Paint.FontMetrics;
Winson Chung6e1c0d32013-10-25 15:24:24 -070026import android.graphics.Point;
Winson Chungf7d45852013-10-10 18:57:15 -070027import android.graphics.PointF;
Winson Chung5f8afe62013-08-12 16:19:28 -070028import android.graphics.Rect;
29import android.util.DisplayMetrics;
30import android.util.TypedValue;
Winson Chung6e1c0d32013-10-25 15:24:24 -070031import android.view.Display;
Winson Chung5f8afe62013-08-12 16:19:28 -070032import android.view.Gravity;
Winson Chung6e1c0d32013-10-25 15:24:24 -070033import android.view.Surface;
Winson Chung5f8afe62013-08-12 16:19:28 -070034import android.view.View;
35import android.view.ViewGroup.LayoutParams;
Winson Chung6e1c0d32013-10-25 15:24:24 -070036import android.view.WindowManager;
Winson Chung5f8afe62013-08-12 16:19:28 -070037import android.widget.FrameLayout;
38
39import java.util.ArrayList;
40import java.util.Collections;
41import java.util.Comparator;
42
43
44class DeviceProfileQuery {
45 float widthDps;
46 float heightDps;
47 float value;
48 PointF dimens;
49
50 DeviceProfileQuery(float w, float h, float v) {
51 widthDps = w;
52 heightDps = h;
53 value = v;
54 dimens = new PointF(w, h);
55 }
56}
57
58class DeviceProfile {
Winson Chung6e1c0d32013-10-25 15:24:24 -070059 public static interface DeviceProfileCallbacks {
60 public void onAvailableSizeChanged(DeviceProfile grid);
61 }
62
Winson Chung5f8afe62013-08-12 16:19:28 -070063 String name;
64 float minWidthDps;
65 float minHeightDps;
66 float numRows;
67 float numColumns;
Winson Chung5f8afe62013-08-12 16:19:28 -070068 float numHotseatIcons;
Winson Chung6e1c0d32013-10-25 15:24:24 -070069 private float iconSize;
70 private float iconTextSize;
71 private int iconDrawablePaddingOriginalPx;
72 private float hotseatIconSize;
Winson Chung5f8afe62013-08-12 16:19:28 -070073
74 boolean isLandscape;
75 boolean isTablet;
76 boolean isLargeTablet;
77 boolean transposeLayoutWithOrientation;
78
Winson Chungf7d45852013-10-10 18:57:15 -070079 int desiredWorkspaceLeftRightMarginPx;
Winson Chung5f8afe62013-08-12 16:19:28 -070080 int edgeMarginPx;
Winson Chungf7d45852013-10-10 18:57:15 -070081 Rect defaultWidgetPadding;
Winson Chung5f8afe62013-08-12 16:19:28 -070082
83 int widthPx;
84 int heightPx;
Winson Chung892c74d2013-08-22 16:15:50 -070085 int availableWidthPx;
86 int availableHeightPx;
Winson Chung6e1c0d32013-10-25 15:24:24 -070087
Winson Chung5f8afe62013-08-12 16:19:28 -070088 int iconSizePx;
89 int iconTextSizePx;
Winson Chung6e1c0d32013-10-25 15:24:24 -070090 int iconDrawablePaddingPx;
Winson Chung5f8afe62013-08-12 16:19:28 -070091 int cellWidthPx;
92 int cellHeightPx;
93 int folderBackgroundOffset;
94 int folderIconSizePx;
95 int folderCellWidthPx;
96 int folderCellHeightPx;
97 int hotseatCellWidthPx;
98 int hotseatCellHeightPx;
99 int hotseatIconSizePx;
100 int hotseatBarHeightPx;
Winson Chungc58497e2013-09-03 17:48:37 -0700101 int hotseatAllAppsRank;
102 int allAppsNumRows;
103 int allAppsNumCols;
Winson Chung5f8afe62013-08-12 16:19:28 -0700104 int searchBarSpaceWidthPx;
105 int searchBarSpaceMaxWidthPx;
106 int searchBarSpaceHeightPx;
107 int searchBarHeightPx;
108 int pageIndicatorHeightPx;
109
Winson Chung6e1c0d32013-10-25 15:24:24 -0700110 private ArrayList<DeviceProfileCallbacks> mCallbacks = new ArrayList<DeviceProfileCallbacks>();
111
Winson Chung5f8afe62013-08-12 16:19:28 -0700112 DeviceProfile(String n, float w, float h, float r, float c,
113 float is, float its, float hs, float his) {
Winson Chungc58497e2013-09-03 17:48:37 -0700114 // Ensure that we have an odd number of hotseat items (since we need to place all apps)
115 if (!AppsCustomizePagedView.DISABLE_ALL_APPS && hs % 2 == 0) {
116 throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces");
117 }
118
Winson Chung5f8afe62013-08-12 16:19:28 -0700119 name = n;
120 minWidthDps = w;
121 minHeightDps = h;
122 numRows = r;
123 numColumns = c;
124 iconSize = is;
125 iconTextSize = its;
126 numHotseatIcons = hs;
127 hotseatIconSize = his;
128 }
129
Winson Chungf7d45852013-10-10 18:57:15 -0700130 DeviceProfile(Context context,
131 ArrayList<DeviceProfile> profiles,
Winson Chung892c74d2013-08-22 16:15:50 -0700132 float minWidth, float minHeight,
Winson Chung5f8afe62013-08-12 16:19:28 -0700133 int wPx, int hPx,
Winson Chung892c74d2013-08-22 16:15:50 -0700134 int awPx, int ahPx,
Winson Chung5f8afe62013-08-12 16:19:28 -0700135 Resources resources) {
136 DisplayMetrics dm = resources.getDisplayMetrics();
137 ArrayList<DeviceProfileQuery> points =
138 new ArrayList<DeviceProfileQuery>();
139 transposeLayoutWithOrientation =
140 resources.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
Winson Chung5f8afe62013-08-12 16:19:28 -0700141 minWidthDps = minWidth;
142 minHeightDps = minHeight;
143
Winson Chungf7d45852013-10-10 18:57:15 -0700144 ComponentName cn = new ComponentName(context.getPackageName(),
145 this.getClass().getName());
146 defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
Winson Chung5f8afe62013-08-12 16:19:28 -0700147 edgeMarginPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
Winson Chungf7d45852013-10-10 18:57:15 -0700148 desiredWorkspaceLeftRightMarginPx = 2 * edgeMarginPx;
Winson Chung5f8afe62013-08-12 16:19:28 -0700149 pageIndicatorHeightPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height);
150
151 // Interpolate the rows
152 for (DeviceProfile p : profiles) {
153 points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numRows));
154 }
155 numRows = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));
156 // Interpolate the columns
157 points.clear();
158 for (DeviceProfile p : profiles) {
159 points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numColumns));
160 }
161 numColumns = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));
Winson Chung6e1c0d32013-10-25 15:24:24 -0700162 // Interpolate the hotseat length
163 points.clear();
164 for (DeviceProfile p : profiles) {
165 points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numHotseatIcons));
166 }
167 numHotseatIcons = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));
168 hotseatAllAppsRank = (int) (numHotseatIcons / 2);
169
Winson Chung5f8afe62013-08-12 16:19:28 -0700170 // Interpolate the icon size
171 points.clear();
172 for (DeviceProfile p : profiles) {
173 points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.iconSize));
174 }
175 iconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
Winson Chung892c74d2013-08-22 16:15:50 -0700176
Winson Chung5f8afe62013-08-12 16:19:28 -0700177 // Interpolate the icon text size
178 points.clear();
179 for (DeviceProfile p : profiles) {
180 points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.iconTextSize));
181 }
182 iconTextSize = invDistWeightedInterpolate(minWidth, minHeight, points);
Winson Chung6e1c0d32013-10-25 15:24:24 -0700183 iconDrawablePaddingOriginalPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);
Winson Chung892c74d2013-08-22 16:15:50 -0700184
Winson Chung5f8afe62013-08-12 16:19:28 -0700185 // Interpolate the hotseat icon size
186 points.clear();
187 for (DeviceProfile p : profiles) {
188 points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.hotseatIconSize));
189 }
Winson Chung5f8afe62013-08-12 16:19:28 -0700190 // Hotseat
191 hotseatIconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
Winson Chung892c74d2013-08-22 16:15:50 -0700192
Winson Chung6e1c0d32013-10-25 15:24:24 -0700193 // Calculate the remaining vars
194 updateFromConfiguration(context, resources, wPx, hPx, awPx, ahPx);
195 updateAvailableDimensions(context);
196 }
197
198 void addCallback(DeviceProfileCallbacks cb) {
199 mCallbacks.add(cb);
200 cb.onAvailableSizeChanged(this);
201 }
202 void removeCallback(DeviceProfileCallbacks cb) {
203 mCallbacks.remove(cb);
204 }
205
206 private int getDeviceOrientation(Context context) {
207 WindowManager windowManager = (WindowManager)
208 context.getSystemService(Context.WINDOW_SERVICE);
209 Resources resources = context.getResources();
210 DisplayMetrics dm = resources.getDisplayMetrics();
211 Configuration config = resources.getConfiguration();
212 int rotation = windowManager.getDefaultDisplay().getRotation();
213
214 boolean isLandscape = (config.orientation == Configuration.ORIENTATION_LANDSCAPE) &&
215 (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180);
216 boolean isRotatedPortrait = (config.orientation == Configuration.ORIENTATION_PORTRAIT) &&
217 (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
218 if (isLandscape || isRotatedPortrait) {
219 return CellLayout.LANDSCAPE;
220 } else {
221 return CellLayout.PORTRAIT;
222 }
223 }
224
225 private void updateAvailableDimensions(Context context) {
226 WindowManager windowManager = (WindowManager)
227 context.getSystemService(Context.WINDOW_SERVICE);
228 Display display = windowManager.getDefaultDisplay();
229 Resources resources = context.getResources();
230 DisplayMetrics dm = resources.getDisplayMetrics();
231 Configuration config = resources.getConfiguration();
232
233 // There are three possible configurations that the dynamic grid accounts for, portrait,
234 // landscape with the nav bar at the bottom, and landscape with the nav bar at the side.
235 // To prevent waiting for fitSystemWindows(), we make the observation that in landscape,
236 // the height is the smallest height (either with the nav bar at the bottom or to the
237 // side) and otherwise, the height is simply the largest possible height for a portrait
238 // device.
239 Point size = new Point();
240 Point smallestSize = new Point();
241 Point largestSize = new Point();
242 display.getSize(size);
243 display.getCurrentSizeRange(smallestSize, largestSize);
244 availableWidthPx = size.x;
245 if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
246 availableHeightPx = smallestSize.y;
247 } else {
248 availableHeightPx = largestSize.y;
249 }
250
251 // Check to see if the icons fit in the new available height. If not, then we need to
252 // shrink the icon size.
253 Rect workspacePadding = getWorkspacePadding();
254 float scale = 1f;
255 int drawablePadding = iconDrawablePaddingOriginalPx;
256 updateIconSize(1f, drawablePadding, resources, dm);
257 float usedHeight = (cellHeightPx * numRows);
258 int maxHeight = (availableHeightPx - workspacePadding.top - workspacePadding.bottom);
259 if (usedHeight > maxHeight) {
260 scale = maxHeight / usedHeight;
261 drawablePadding = 0;
262 }
263 updateIconSize(scale, drawablePadding, resources, dm);
264
265 // Make the callbacks
266 for (DeviceProfileCallbacks cb : mCallbacks) {
267 cb.onAvailableSizeChanged(this);
268 }
269 }
270
271 private void updateIconSize(float scale, int drawablePadding, Resources resources,
272 DisplayMetrics dm) {
273 iconSizePx = (int) (DynamicGrid.pxFromDp(iconSize, dm) * scale);
274 iconTextSizePx = (int) (DynamicGrid.pxFromSp(iconTextSize, dm) * scale);
275 iconDrawablePaddingPx = drawablePadding;
276 hotseatIconSizePx = (int) (DynamicGrid.pxFromDp(hotseatIconSize, dm) * scale);
Winson Chung5f8afe62013-08-12 16:19:28 -0700277
278 // Search Bar
279 searchBarSpaceMaxWidthPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_max_width);
280 searchBarHeightPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_height);
281 searchBarSpaceWidthPx = Math.min(searchBarSpaceMaxWidthPx, widthPx);
282 searchBarSpaceHeightPx = searchBarHeightPx + 2 * edgeMarginPx;
283
284 // Calculate the actual text height
285 Paint textPaint = new Paint();
286 textPaint.setTextSize(iconTextSizePx);
287 FontMetrics fm = textPaint.getFontMetrics();
288 cellWidthPx = iconSizePx;
Winson Chung6e1c0d32013-10-25 15:24:24 -0700289 cellHeightPx = iconSizePx + iconDrawablePaddingPx + (int) Math.ceil(fm.bottom - fm.top);
Winson Chung892c74d2013-08-22 16:15:50 -0700290
291 // Hotseat
292 hotseatBarHeightPx = iconSizePx + 4 * edgeMarginPx;
293 hotseatCellWidthPx = iconSizePx;
294 hotseatCellHeightPx = iconSizePx;
295
Winson Chung5f8afe62013-08-12 16:19:28 -0700296 // Folder
297 folderCellWidthPx = cellWidthPx + 3 * edgeMarginPx;
Winson Chung6e1c0d32013-10-25 15:24:24 -0700298 folderCellHeightPx = cellHeightPx + edgeMarginPx;
Winson Chung5f8afe62013-08-12 16:19:28 -0700299 folderBackgroundOffset = -edgeMarginPx;
300 folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
Winson Chung6e1c0d32013-10-25 15:24:24 -0700301
302 // All Apps
303 Rect padding = getWorkspacePadding(isLandscape ?
304 CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
305 int pageIndicatorOffset =
306 resources.getDimensionPixelSize(R.dimen.apps_customize_page_indicator_offset);
307 if (isLandscape) {
308 allAppsNumRows = (availableHeightPx - pageIndicatorOffset - 4 * edgeMarginPx) /
309 (iconSizePx + iconTextSizePx + 2 * edgeMarginPx);
310 } else {
311 allAppsNumRows = (int) numRows + 1;
312 }
313 allAppsNumCols = (availableWidthPx - padding.left - padding.right - 2 * edgeMarginPx) /
314 (iconSizePx + 2 * edgeMarginPx);
Winson Chung5f8afe62013-08-12 16:19:28 -0700315 }
316
Winson Chung6e1c0d32013-10-25 15:24:24 -0700317 void updateFromConfiguration(Context context, Resources resources, int wPx, int hPx,
Winson Chung892c74d2013-08-22 16:15:50 -0700318 int awPx, int ahPx) {
Winson Chung5f8afe62013-08-12 16:19:28 -0700319 isLandscape = (resources.getConfiguration().orientation ==
320 Configuration.ORIENTATION_LANDSCAPE);
321 isTablet = resources.getBoolean(R.bool.is_tablet);
322 isLargeTablet = resources.getBoolean(R.bool.is_large_tablet);
323 widthPx = wPx;
324 heightPx = hPx;
Winson Chung892c74d2013-08-22 16:15:50 -0700325 availableWidthPx = awPx;
326 availableHeightPx = ahPx;
Winson Chungc58497e2013-09-03 17:48:37 -0700327
Winson Chung6e1c0d32013-10-25 15:24:24 -0700328 updateAvailableDimensions(context);
Winson Chung5f8afe62013-08-12 16:19:28 -0700329 }
330
331 private float dist(PointF p0, PointF p1) {
332 return (float) Math.sqrt((p1.x - p0.x)*(p1.x-p0.x) +
333 (p1.y-p0.y)*(p1.y-p0.y));
334 }
335
336 private float weight(PointF a, PointF b,
337 float pow) {
338 float d = dist(a, b);
339 if (d == 0f) {
340 return Float.POSITIVE_INFINITY;
341 }
342 return (float) (1f / Math.pow(d, pow));
343 }
344
345 private float invDistWeightedInterpolate(float width, float height,
346 ArrayList<DeviceProfileQuery> points) {
347 float sum = 0;
348 float weights = 0;
349 float pow = 5;
350 float kNearestNeighbors = 3;
351 final PointF xy = new PointF(width, height);
352
353 ArrayList<DeviceProfileQuery> pointsByNearness = points;
354 Collections.sort(pointsByNearness, new Comparator<DeviceProfileQuery>() {
355 public int compare(DeviceProfileQuery a, DeviceProfileQuery b) {
356 return (int) (dist(xy, a.dimens) - dist(xy, b.dimens));
357 }
358 });
359
360 for (int i = 0; i < pointsByNearness.size(); ++i) {
361 DeviceProfileQuery p = pointsByNearness.get(i);
362 if (i < kNearestNeighbors) {
363 float w = weight(xy, p.dimens, pow);
364 if (w == Float.POSITIVE_INFINITY) {
365 return p.value;
366 }
367 weights += w;
368 }
369 }
370
371 for (int i = 0; i < pointsByNearness.size(); ++i) {
372 DeviceProfileQuery p = pointsByNearness.get(i);
373 if (i < kNearestNeighbors) {
374 float w = weight(xy, p.dimens, pow);
375 sum += w * p.value / weights;
376 }
377 }
378
379 return sum;
380 }
381
Winson Chung6e1c0d32013-10-25 15:24:24 -0700382 Rect getWorkspacePadding() {
383 return getWorkspacePadding(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
384 }
Winson Chung5f8afe62013-08-12 16:19:28 -0700385 Rect getWorkspacePadding(int orientation) {
386 Rect padding = new Rect();
Winson Chungfe411c82013-09-26 16:07:17 -0700387 if (orientation == CellLayout.LANDSCAPE &&
388 transposeLayoutWithOrientation) {
Winson Chung5f8afe62013-08-12 16:19:28 -0700389 // Pad the left and right of the workspace with search/hotseat bar sizes
390 padding.set(searchBarSpaceHeightPx, edgeMarginPx,
391 hotseatBarHeightPx, edgeMarginPx);
392 } else {
393 if (isTablet()) {
394 // Pad the left and right of the workspace to ensure consistent spacing
395 // between all icons
396 int width = (orientation == CellLayout.LANDSCAPE)
397 ? Math.max(widthPx, heightPx)
398 : Math.min(widthPx, heightPx);
399 // XXX: If the icon size changes across orientations, we will have to take
400 // that into account here too.
401 int gap = (int) ((width - 2 * edgeMarginPx -
402 (numColumns * cellWidthPx)) / (2 * (numColumns + 1)));
403 padding.set(edgeMarginPx + gap,
404 searchBarSpaceHeightPx,
405 edgeMarginPx + gap,
406 hotseatBarHeightPx + pageIndicatorHeightPx);
407 } else {
408 // Pad the top and bottom of the workspace with search/hotseat bar sizes
Winson Chungf7d45852013-10-10 18:57:15 -0700409 padding.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left,
Winson Chung5f8afe62013-08-12 16:19:28 -0700410 searchBarSpaceHeightPx,
Winson Chungf7d45852013-10-10 18:57:15 -0700411 desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.right,
Winson Chung5f8afe62013-08-12 16:19:28 -0700412 hotseatBarHeightPx + pageIndicatorHeightPx);
413 }
414 }
415 return padding;
416 }
417
Winson Chungabedd9f2013-09-24 15:41:09 -0700418 // The rect returned will be extended to below the system ui that covers the workspace
419 Rect getHotseatRect() {
420 if (isVerticalBarLayout()) {
421 return new Rect(availableWidthPx - hotseatBarHeightPx, 0,
422 Integer.MAX_VALUE, availableHeightPx);
423 } else {
424 return new Rect(0, availableHeightPx - hotseatBarHeightPx,
425 availableWidthPx, Integer.MAX_VALUE);
426 }
427 }
428
Winson Chung5f8afe62013-08-12 16:19:28 -0700429 int calculateCellWidth(int width, int countX) {
430 return width / countX;
431 }
432 int calculateCellHeight(int height, int countY) {
433 return height / countY;
434 }
435
Winson Chungaf40f202013-09-18 18:26:31 -0700436 boolean isPhone() {
437 return !isTablet && !isLargeTablet;
438 }
Winson Chung5f8afe62013-08-12 16:19:28 -0700439 boolean isTablet() {
440 return isTablet;
441 }
Winson Chung5f8afe62013-08-12 16:19:28 -0700442 boolean isLargeTablet() {
443 return isLargeTablet;
444 }
445
Winson Chungabedd9f2013-09-24 15:41:09 -0700446 boolean isVerticalBarLayout() {
447 return isLandscape && transposeLayoutWithOrientation;
448 }
449
Winson Chung5f8afe62013-08-12 16:19:28 -0700450 public void layout(Launcher launcher) {
451 FrameLayout.LayoutParams lp;
452 Resources res = launcher.getResources();
Winson Chungabedd9f2013-09-24 15:41:09 -0700453 boolean hasVerticalBarLayout = isVerticalBarLayout();
Winson Chung5f8afe62013-08-12 16:19:28 -0700454
455 // Layout the search bar space
Winson Chung54e65132013-09-27 11:44:58 -0700456 View searchBar = launcher.getSearchBar();
457 lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
Winson Chung5f8afe62013-08-12 16:19:28 -0700458 if (hasVerticalBarLayout) {
459 // Vertical search bar
460 lp.gravity = Gravity.TOP | Gravity.LEFT;
461 lp.width = searchBarSpaceHeightPx;
462 lp.height = LayoutParams.MATCH_PARENT;
Winson Chung54e65132013-09-27 11:44:58 -0700463 searchBar.setPadding(
Winson Chung5f8afe62013-08-12 16:19:28 -0700464 0, 2 * edgeMarginPx, 0,
465 2 * edgeMarginPx);
466 } else {
467 // Horizontal search bar
468 lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
469 lp.width = searchBarSpaceWidthPx;
470 lp.height = searchBarSpaceHeightPx;
Winson Chung54e65132013-09-27 11:44:58 -0700471 searchBar.setPadding(
Winson Chung5f8afe62013-08-12 16:19:28 -0700472 2 * edgeMarginPx,
473 2 * edgeMarginPx,
474 2 * edgeMarginPx, 0);
475 }
Winson Chung54e65132013-09-27 11:44:58 -0700476 searchBar.setLayoutParams(lp);
Winson Chung5f8afe62013-08-12 16:19:28 -0700477
478 // Layout the search bar
Winson Chung54e65132013-09-27 11:44:58 -0700479 View qsbBar = launcher.getQsbBar();
480 LayoutParams vglp = qsbBar.getLayoutParams();
481 vglp.width = LayoutParams.MATCH_PARENT;
482 vglp.height = LayoutParams.MATCH_PARENT;
483 qsbBar.setLayoutParams(vglp);
Winson Chung5f8afe62013-08-12 16:19:28 -0700484
485 // Layout the voice proxy
486 View voiceButtonProxy = launcher.findViewById(R.id.voice_button_proxy);
487 if (voiceButtonProxy != null) {
488 if (hasVerticalBarLayout) {
489 // TODO: MOVE THIS INTO SEARCH BAR MEASURE
490 } else {
491 lp = (FrameLayout.LayoutParams) voiceButtonProxy.getLayoutParams();
492 lp.gravity = Gravity.TOP | Gravity.END;
493 lp.width = (widthPx - searchBarSpaceWidthPx) / 2 +
494 2 * iconSizePx;
495 lp.height = searchBarSpaceHeightPx;
496 }
497 }
498
499 // Layout the workspace
500 View workspace = launcher.findViewById(R.id.workspace);
501 lp = (FrameLayout.LayoutParams) workspace.getLayoutParams();
502 lp.gravity = Gravity.CENTER;
503 Rect padding = getWorkspacePadding(isLandscape
504 ? CellLayout.LANDSCAPE
505 : CellLayout.PORTRAIT);
506 workspace.setPadding(padding.left, padding.top,
507 padding.right, padding.bottom);
508 workspace.setLayoutParams(lp);
509
510 // Layout the hotseat
511 View hotseat = launcher.findViewById(R.id.hotseat);
512 lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams();
513 if (hasVerticalBarLayout) {
514 // Vertical hotseat
515 lp.gravity = Gravity.RIGHT;
516 lp.width = hotseatBarHeightPx;
517 lp.height = LayoutParams.MATCH_PARENT;
Winson Chung6e1c0d32013-10-25 15:24:24 -0700518 hotseat.findViewById(R.id.layout).setPadding(0, 2 * edgeMarginPx, 0, 2 * edgeMarginPx);
Winson Chung5f8afe62013-08-12 16:19:28 -0700519 } else if (isTablet()) {
520 // Pad the hotseat with the grid gap calculated above
521 int gridGap = (int) ((widthPx - 2 * edgeMarginPx -
522 (numColumns * cellWidthPx)) / (2 * (numColumns + 1)));
523 int gridWidth = (int) ((numColumns * cellWidthPx) +
524 ((numColumns - 1) * gridGap));
525 int hotseatGap = (int) Math.max(0,
526 (gridWidth - (numHotseatIcons * hotseatCellWidthPx))
527 / (numHotseatIcons - 1));
528 lp.gravity = Gravity.BOTTOM;
529 lp.width = LayoutParams.MATCH_PARENT;
530 lp.height = hotseatBarHeightPx;
531 hotseat.setPadding(2 * edgeMarginPx + gridGap + hotseatGap, 0,
532 2 * edgeMarginPx + gridGap + hotseatGap,
533 2 * edgeMarginPx);
534 } else {
535 // For phones, layout the hotseat without any bottom margin
536 // to ensure that we have space for the folders
537 lp.gravity = Gravity.BOTTOM;
538 lp.width = LayoutParams.MATCH_PARENT;
539 lp.height = hotseatBarHeightPx;
Winson Chung2d75f122013-09-23 16:53:31 -0700540 hotseat.findViewById(R.id.layout).setPadding(2 * edgeMarginPx, 0,
Winson Chung5f8afe62013-08-12 16:19:28 -0700541 2 * edgeMarginPx, 0);
542 }
543 hotseat.setLayoutParams(lp);
544
545 // Layout the page indicators
546 View pageIndicator = launcher.findViewById(R.id.page_indicator);
547 if (pageIndicator != null) {
548 if (hasVerticalBarLayout) {
549 // Hide the page indicators when we have vertical search/hotseat
550 pageIndicator.setVisibility(View.GONE);
551 } else {
552 // Put the page indicators above the hotseat
553 lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams();
554 lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
555 lp.width = LayoutParams.WRAP_CONTENT;
Winson Chungf4bd2362013-10-07 17:11:27 -0700556 lp.height = LayoutParams.WRAP_CONTENT;
Winson Chung5f8afe62013-08-12 16:19:28 -0700557 lp.bottomMargin = hotseatBarHeightPx;
558 pageIndicator.setLayoutParams(lp);
559 }
560 }
561 }
562}
563
564public class DynamicGrid {
565 @SuppressWarnings("unused")
566 private static final String TAG = "DynamicGrid";
567
568 private DeviceProfile mProfile;
569 private float mMinWidth;
570 private float mMinHeight;
571
Winson Chung892c74d2013-08-22 16:15:50 -0700572 public static float dpiFromPx(int size, DisplayMetrics metrics){
Winson Chung5f8afe62013-08-12 16:19:28 -0700573 float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
Winson Chung892c74d2013-08-22 16:15:50 -0700574 return (size / densityRatio);
575 }
576 public static int pxFromDp(float size, DisplayMetrics metrics) {
577 return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
578 size, metrics));
579 }
580 public static int pxFromSp(float size, DisplayMetrics metrics) {
581 return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
582 size, metrics));
Winson Chung5f8afe62013-08-12 16:19:28 -0700583 }
584
Winson Chungf7d45852013-10-10 18:57:15 -0700585 public DynamicGrid(Context context, Resources resources,
586 int minWidthPx, int minHeightPx,
Winson Chung892c74d2013-08-22 16:15:50 -0700587 int widthPx, int heightPx,
588 int awPx, int ahPx) {
Winson Chung5f8afe62013-08-12 16:19:28 -0700589 DisplayMetrics dm = resources.getDisplayMetrics();
590 ArrayList<DeviceProfile> deviceProfiles =
591 new ArrayList<DeviceProfile>();
Winson Chungc58497e2013-09-03 17:48:37 -0700592 boolean hasAA = !AppsCustomizePagedView.DISABLE_ALL_APPS;
Winson Chung5f8afe62013-08-12 16:19:28 -0700593 // Our phone profiles include the bar sizes in each orientation
594 deviceProfiles.add(new DeviceProfile("Super Short Stubby",
Winson Chung2651d132013-10-01 16:28:15 -0700595 255, 300, 2, 3, 48, 13, (hasAA ? 5 : 4), 48));
Winson Chung5f8afe62013-08-12 16:19:28 -0700596 deviceProfiles.add(new DeviceProfile("Shorter Stubby",
Winson Chung2651d132013-10-01 16:28:15 -0700597 255, 400, 3, 3, 48, 13, (hasAA ? 5 : 4), 48));
Winson Chung5f8afe62013-08-12 16:19:28 -0700598 deviceProfiles.add(new DeviceProfile("Short Stubby",
Winson Chung2651d132013-10-01 16:28:15 -0700599 275, 420, 3, 4, 48, 13, (hasAA ? 5 : 4), 48));
Winson Chung5f8afe62013-08-12 16:19:28 -0700600 deviceProfiles.add(new DeviceProfile("Stubby",
Winson Chung2651d132013-10-01 16:28:15 -0700601 255, 450, 3, 4, 48, 13, (hasAA ? 5 : 4), 48));
Winson Chung5f8afe62013-08-12 16:19:28 -0700602 deviceProfiles.add(new DeviceProfile("Nexus S",
Winson Chung2651d132013-10-01 16:28:15 -0700603 296, 491.33f, 4, 4, 48, 13, (hasAA ? 5 : 4), 48));
Winson Chung5f8afe62013-08-12 16:19:28 -0700604 deviceProfiles.add(new DeviceProfile("Nexus 4",
Winson Chung2651d132013-10-01 16:28:15 -0700605 359, 518, 4, 4, 60, 13, (hasAA ? 5 : 4), 56));
Winson Chung5f8afe62013-08-12 16:19:28 -0700606 // The tablet profile is odd in that the landscape orientation
607 // also includes the nav bar on the side
608 deviceProfiles.add(new DeviceProfile("Nexus 7",
609 575, 904, 6, 6, 72, 14.4f, 7, 60));
610 // Larger tablet profiles always have system bars on the top & bottom
611 deviceProfiles.add(new DeviceProfile("Nexus 10",
612 727, 1207, 5, 8, 80, 14.4f, 9, 64));
613 /*
614 deviceProfiles.add(new DeviceProfile("Nexus 7",
615 600, 960, 5, 5, 72, 14.4f, 5, 60));
616 deviceProfiles.add(new DeviceProfile("Nexus 10",
Winson Chungc58497e2013-09-03 17:48:37 -0700617 800, 1280, 5, 5, 80, 14.4f, (hasAA ? 7 : 6), 64));
Winson Chung5f8afe62013-08-12 16:19:28 -0700618 */
619 deviceProfiles.add(new DeviceProfile("20-inch Tablet",
620 1527, 2527, 7, 7, 100, 20, 7, 72));
621 mMinWidth = dpiFromPx(minWidthPx, dm);
622 mMinHeight = dpiFromPx(minHeightPx, dm);
Winson Chungf7d45852013-10-10 18:57:15 -0700623 mProfile = new DeviceProfile(context, deviceProfiles,
Winson Chung892c74d2013-08-22 16:15:50 -0700624 mMinWidth, mMinHeight,
Winson Chung5f8afe62013-08-12 16:19:28 -0700625 widthPx, heightPx,
Winson Chung892c74d2013-08-22 16:15:50 -0700626 awPx, ahPx,
Winson Chung5f8afe62013-08-12 16:19:28 -0700627 resources);
628 }
629
630 DeviceProfile getDeviceProfile() {
631 return mProfile;
632 }
633
634 public String toString() {
635 return "-------- DYNAMIC GRID ------- \n" +
636 "Wd: " + mProfile.minWidthDps + ", Hd: " + mProfile.minHeightDps +
637 ", W: " + mProfile.widthPx + ", H: " + mProfile.heightPx +
638 " [r: " + mProfile.numRows + ", c: " + mProfile.numColumns +
Winson Chung6e1c0d32013-10-25 15:24:24 -0700639 ", is: " + mProfile.iconSizePx + ", its: " + mProfile.iconTextSizePx +
Winson Chung5f8afe62013-08-12 16:19:28 -0700640 ", cw: " + mProfile.cellWidthPx + ", ch: " + mProfile.cellHeightPx +
641 ", hc: " + mProfile.numHotseatIcons + ", his: " + mProfile.hotseatIconSizePx + "]";
642 }
643}