DynamicGrid.java revision 414300a79d140f8c2c8760d9adab750f69ffeafd
131ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov/*
231ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov * Copyright (C) 2008 The Android Open Source Project
331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov *
431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov * Licensed under the Apache License, Version 2.0 (the "License");
531ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov * you may not use this file except in compliance with the License.
631ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov * You may obtain a copy of the License at
731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov *
831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov *      http://www.apache.org/licenses/LICENSE-2.0
931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov *
1031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov * Unless required by applicable law or agreed to in writing, software
1131ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov * distributed under the License is distributed on an "AS IS" BASIS,
1231ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov * See the License for the specific language governing permissions and
1431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov * limitations under the License.
1531ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov */
1631ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
1731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikovpackage com.android.launcher3;
1831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
192b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoroimport android.content.res.Configuration;
2031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikovimport android.content.res.Resources;
2179bea93fe361aac5b4928ef090b5021bcba99428Dmitri Plotnikovimport android.graphics.Paint;
2231ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikovimport android.graphics.PointF;
2331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikovimport android.graphics.Paint.FontMetrics;
2431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikovimport android.graphics.Rect;
2531ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikovimport android.util.DisplayMetrics;
2631ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikovimport android.util.TypedValue;
2779bea93fe361aac5b4928ef090b5021bcba99428Dmitri Plotnikovimport android.view.Gravity;
2831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikovimport android.view.View;
2931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikovimport android.view.ViewGroup.LayoutParams;
3031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikovimport android.widget.FrameLayout;
3131ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
32a4aa90ce861ea56f403aa244905c9a4e326f2a10Flavio Lerdaimport java.util.ArrayList;
3331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikovimport java.util.Collections;
3431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikovimport java.util.Comparator;
3531ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
3679bea93fe361aac5b4928ef090b5021bcba99428Dmitri Plotnikov
3779bea93fe361aac5b4928ef090b5021bcba99428Dmitri Plotnikovclass DeviceProfileQuery {
3831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    float widthDps;
3931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    float heightDps;
4027834b2ae8bb5a65dfc20c212844d98d1c252caeDmitri Plotnikov    float value;
4179bea93fe361aac5b4928ef090b5021bcba99428Dmitri Plotnikov    PointF dimens;
4227834b2ae8bb5a65dfc20c212844d98d1c252caeDmitri Plotnikov
4331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    DeviceProfileQuery(float w, float h, float v) {
4431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        widthDps = w;
4527834b2ae8bb5a65dfc20c212844d98d1c252caeDmitri Plotnikov        heightDps = h;
4631ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        value = v;
4731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        dimens = new PointF(w, h);
4831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    }
4965c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann}
5031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
5131ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikovclass DeviceProfile {
5231ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    String name;
5331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    float minWidthDps;
5431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    float minHeightDps;
5531ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    float numRows;
562013058703aa110a1d7aac87ef408c96a3919472Daniel Lehmann    float numColumns;
572013058703aa110a1d7aac87ef408c96a3919472Daniel Lehmann    float iconSize;
5831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    float iconTextSize;
59a4aa90ce861ea56f403aa244905c9a4e326f2a10Flavio Lerda    float numHotseatIcons;
6031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    float hotseatIconSize;
6131ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
62b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov    boolean isLandscape;
635e684a424e7036ba3d379221d1889b4dd7265021Dave Santoro    boolean isTablet;
6431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    boolean isLargeTablet;
65fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    boolean transposeLayoutWithOrientation;
66fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov
67fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    int edgeMarginPx;
68fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov
69fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    int widthPx;
70fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    int heightPx;
71fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    int availableWidthPx;
72ea14605e56c98fcaa93f2f1e4186ce9db5e2f159Makoto Onuki    int availableHeightPx;
73f0c4681ee4191e47103f8699dd1bf9cf7fc8b669Daisuke Miyakawa    int iconSizePx;
74fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    int iconTextSizePx;
75fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    int cellWidthPx;
76fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    int cellHeightPx;
77fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    int folderBackgroundOffset;
78fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    int folderIconSizePx;
79fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    int folderCellWidthPx;
80c86ace78ca5c2e10dcb001a916386c8a9865e230Daniel Lehmann    int folderCellHeightPx;
81fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    int hotseatCellWidthPx;
82fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    int hotseatCellHeightPx;
83fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    int hotseatIconSizePx;
84fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    int hotseatBarHeightPx;
85fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    int searchBarSpaceWidthPx;
86fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    int searchBarSpaceMaxWidthPx;
87fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    int searchBarSpaceHeightPx;
88fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    int searchBarHeightPx;
89fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    int pageIndicatorHeightPx;
90fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov
91fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    DeviceProfile(String n, float w, float h, float r, float c,
92fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov                  float is, float its, float hs, float his) {
93fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        name = n;
94fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        minWidthDps = w;
95fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        minHeightDps = h;
96fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        numRows = r;
97fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        numColumns = c;
98fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        iconSize = is;
99fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        iconTextSize = its;
100fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        numHotseatIcons = hs;
101fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        hotseatIconSize = his;
102fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    }
103fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov
104fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov    DeviceProfile(ArrayList<DeviceProfile> profiles,
105fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov                  float minWidth, float minHeight,
106fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov                  int wPx, int hPx,
107fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov                  int awPx, int ahPx,
108fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov                  Resources resources) {
109fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        DisplayMetrics dm = resources.getDisplayMetrics();
110fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        ArrayList<DeviceProfileQuery> points =
111fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov                new ArrayList<DeviceProfileQuery>();
112fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        transposeLayoutWithOrientation =
113fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov                resources.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
114fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        minWidthDps = minWidth;
115fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        minHeightDps = minHeight;
116fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov
117fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        edgeMarginPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
118fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        pageIndicatorHeightPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height);
119fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov
120fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        // Interpolate the rows
121fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        for (DeviceProfile p : profiles) {
122fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov            points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numRows));
123a4aa90ce861ea56f403aa244905c9a4e326f2a10Flavio Lerda        }
124a4aa90ce861ea56f403aa244905c9a4e326f2a10Flavio Lerda        numRows = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));
125a4aa90ce861ea56f403aa244905c9a4e326f2a10Flavio Lerda        // Interpolate the columns
126a4aa90ce861ea56f403aa244905c9a4e326f2a10Flavio Lerda        points.clear();
127fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        for (DeviceProfile p : profiles) {
128fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov            points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numColumns));
129fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        }
130fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        numColumns = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));
131fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        // Interpolate the icon size
132e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        points.clear();
13331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        for (DeviceProfile p : profiles) {
13431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.iconSize));
13531ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        }
13631ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        iconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
13731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        iconSizePx = DynamicGrid.pxFromDp(iconSize, dm);
13831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
13931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        // Interpolate the icon text size
14031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        points.clear();
14131ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        for (DeviceProfile p : profiles) {
14231ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.iconTextSize));
14331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        }
144fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov        iconTextSize = invDistWeightedInterpolate(minWidth, minHeight, points);
14531ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        iconTextSizePx = DynamicGrid.pxFromSp(iconTextSize, dm);
146b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov
147ea14605e56c98fcaa93f2f1e4186ce9db5e2f159Makoto Onuki        // Interpolate the hotseat size
14831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        points.clear();
14931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        for (DeviceProfile p : profiles) {
15031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numHotseatIcons));
151b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov        }
15231ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        numHotseatIcons = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));
15331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        // Interpolate the hotseat icon size
15431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        points.clear();
15531ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        for (DeviceProfile p : profiles) {
156b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov            points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.hotseatIconSize));
15731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        }
15831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        // Hotseat
15931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        hotseatIconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
16031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        hotseatIconSizePx = DynamicGrid.pxFromDp(hotseatIconSize, dm);
161b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov
16231ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        // Calculate other vars based on Configuration
16331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        updateFromConfiguration(resources, wPx, hPx, awPx, ahPx);
16431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
16531ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        // Search Bar
166b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov        searchBarSpaceMaxWidthPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_max_width);
16731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        searchBarHeightPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_height);
16831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        searchBarSpaceWidthPx = Math.min(searchBarSpaceMaxWidthPx, widthPx);
16931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        searchBarSpaceHeightPx = searchBarHeightPx + 2 * edgeMarginPx;
17031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
171b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov        // Calculate the actual text height
17231ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        Paint textPaint = new Paint();
173ea14605e56c98fcaa93f2f1e4186ce9db5e2f159Makoto Onuki        textPaint.setTextSize(iconTextSizePx);
174ea14605e56c98fcaa93f2f1e4186ce9db5e2f159Makoto Onuki        FontMetrics fm = textPaint.getFontMetrics();
175ea14605e56c98fcaa93f2f1e4186ce9db5e2f159Makoto Onuki        cellWidthPx = iconSizePx;
176ea14605e56c98fcaa93f2f1e4186ce9db5e2f159Makoto Onuki        cellHeightPx = iconSizePx + (int) Math.ceil(fm.bottom - fm.top);
177ea14605e56c98fcaa93f2f1e4186ce9db5e2f159Makoto Onuki
17831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        // At this point, if the cells do not fit into the available height, then we need
17931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        // to shrink the icon size
18031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        /*
181b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov        Rect padding = getWorkspacePadding(isLandscape ?
18231ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
18331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        int h = (int) (numRows * cellHeightPx) + padding.top + padding.bottom;
18431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        if (h > availableHeightPx) {
18531ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            float delta = h - availableHeightPx;
186b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov            int deltaPx = (int) Math.ceil(delta / numRows);
18731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            iconSizePx -= deltaPx;
18831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            iconSize = DynamicGrid.dpiFromPx(iconSizePx, dm);
18931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            cellWidthPx = iconSizePx;
19031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            cellHeightPx = iconSizePx + (int) Math.ceil(fm.bottom - fm.top);
191b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov        }
19231ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        */
19331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
19431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        // Hotseat
19531ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        hotseatBarHeightPx = iconSizePx + 4 * edgeMarginPx;
196b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov        hotseatCellWidthPx = iconSizePx;
19731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        hotseatCellHeightPx = iconSizePx;
19831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
19931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        // Folder
20031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        folderCellWidthPx = cellWidthPx + 3 * edgeMarginPx;
201b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov        folderCellHeightPx = cellHeightPx + edgeMarginPx;
20231ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        folderBackgroundOffset = -edgeMarginPx;
20331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
20431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    }
20531ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
20631ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    void updateFromConfiguration(Resources resources, int wPx, int hPx,
20731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                                 int awPx, int ahPx) {
208b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov        isLandscape = (resources.getConfiguration().orientation ==
20931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                Configuration.ORIENTATION_LANDSCAPE);
21031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        isTablet = resources.getBoolean(R.bool.is_tablet);
211c86ace78ca5c2e10dcb001a916386c8a9865e230Daniel Lehmann        isLargeTablet = resources.getBoolean(R.bool.is_large_tablet);
212c86ace78ca5c2e10dcb001a916386c8a9865e230Daniel Lehmann        widthPx = wPx;
213c86ace78ca5c2e10dcb001a916386c8a9865e230Daniel Lehmann        heightPx = hPx;
214c86ace78ca5c2e10dcb001a916386c8a9865e230Daniel Lehmann        availableWidthPx = awPx;
215c86ace78ca5c2e10dcb001a916386c8a9865e230Daniel Lehmann        availableHeightPx = ahPx;
21631ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    }
21731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
218b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov    private float dist(PointF p0, PointF p1) {
21931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        return (float) Math.sqrt((p1.x - p0.x)*(p1.x-p0.x) +
22031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                (p1.y-p0.y)*(p1.y-p0.y));
22131ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    }
22231ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
223a4aa90ce861ea56f403aa244905c9a4e326f2a10Flavio Lerda    private float weight(PointF a, PointF b,
22431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                        float pow) {
22531ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        float d = dist(a, b);
22631ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        if (d == 0f) {
22731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            return Float.POSITIVE_INFINITY;
22831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        }
229a4aa90ce861ea56f403aa244905c9a4e326f2a10Flavio Lerda        return (float) (1f / Math.pow(d, pow));
23031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    }
23131ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
23231ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    private float invDistWeightedInterpolate(float width, float height,
23331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                ArrayList<DeviceProfileQuery> points) {
23431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        float sum = 0;
23531ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        float weights = 0;
236b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov        float pow = 5;
23731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        float kNearestNeighbors = 3;
23831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        final PointF xy = new PointF(width, height);
23931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
24031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        ArrayList<DeviceProfileQuery> pointsByNearness = points;
24131ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        Collections.sort(pointsByNearness, new Comparator<DeviceProfileQuery>() {
242b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov            public int compare(DeviceProfileQuery a, DeviceProfileQuery b) {
24331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                return (int) (dist(xy, a.dimens) - dist(xy, b.dimens));
24431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            }
24531ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        });
24631ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
24731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        for (int i = 0; i < pointsByNearness.size(); ++i) {
248b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov            DeviceProfileQuery p = pointsByNearness.get(i);
24931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            if (i < kNearestNeighbors) {
25031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                float w = weight(xy, p.dimens, pow);
25131ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                if (w == Float.POSITIVE_INFINITY) {
25231ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                    return p.value;
25331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                }
254b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov                weights += w;
25531ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            }
25631ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        }
25731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
25831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        for (int i = 0; i < pointsByNearness.size(); ++i) {
25931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            DeviceProfileQuery p = pointsByNearness.get(i);
260b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov            if (i < kNearestNeighbors) {
26131ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                float w = weight(xy, p.dimens, pow);
26231ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                sum += w * p.value / weights;
26331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            }
26431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        }
26531ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
266b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov        return sum;
26731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    }
26831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
26931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    Rect getWorkspacePadding(int orientation) {
27031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        Rect padding = new Rect();
27186092529f272b7ed2cee24fae397291696b29b80Dmitri Plotnikov        if (orientation == CellLayout.LANDSCAPE &&
27286092529f272b7ed2cee24fae397291696b29b80Dmitri Plotnikov                transposeLayoutWithOrientation) {
27386092529f272b7ed2cee24fae397291696b29b80Dmitri Plotnikov            // Pad the left and right of the workspace with search/hotseat bar sizes
27431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            padding.set(searchBarSpaceHeightPx, edgeMarginPx,
27531ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                    hotseatBarHeightPx, edgeMarginPx);
27631ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        } else {
27731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            if (isTablet()) {
27831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                // Pad the left and right of the workspace to ensure consistent spacing
27931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                // between all icons
28031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                int width = (orientation == CellLayout.LANDSCAPE)
28131ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                        ? Math.max(widthPx, heightPx)
28231ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                        : Math.min(widthPx, heightPx);
28331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                // XXX: If the icon size changes across orientations, we will have to take
28431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                //      that into account here too.
28531ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                int gap = (int) ((width - 2 * edgeMarginPx -
2867b8f74678640b571196557d64dd712dc4a109c49Dmitri Plotnikov                        (numColumns * cellWidthPx)) / (2 * (numColumns + 1)));
28731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                padding.set(edgeMarginPx + gap,
28831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                        searchBarSpaceHeightPx,
28931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                        edgeMarginPx + gap,
29031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                        hotseatBarHeightPx + pageIndicatorHeightPx);
2917b8f74678640b571196557d64dd712dc4a109c49Dmitri Plotnikov            } else {
29231ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                // Pad the top and bottom of the workspace with search/hotseat bar sizes
29331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                padding.set(edgeMarginPx,
29431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                        searchBarSpaceHeightPx,
2957b8f74678640b571196557d64dd712dc4a109c49Dmitri Plotnikov                        edgeMarginPx,
29631ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                        hotseatBarHeightPx + pageIndicatorHeightPx);
29731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            }
29831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        }
29931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        return padding;
30031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    }
30131ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
30231ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    int calculateCellWidth(int width, int countX) {
30331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        return width / countX;
30431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    }
305b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov    int calculateCellHeight(int height, int countY) {
30631ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        return height / countY;
30731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    }
30831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
30931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    boolean isTablet() {
31031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        return isTablet;
31131ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    }
31231ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
31331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    boolean isLargeTablet() {
31431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        return isLargeTablet;
315b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov    }
31631ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov
31731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov    public void layout(Launcher launcher) {
31831ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        FrameLayout.LayoutParams lp;
31931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        Resources res = launcher.getResources();
32031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        boolean hasVerticalBarLayout = isLandscape &&
32131ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
322b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov
32331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        // Layout the search bar space
32431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        View searchBarSpace = launcher.findViewById(R.id.qsb_bar);
32531ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        lp = (FrameLayout.LayoutParams) searchBarSpace.getLayoutParams();
32631ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        if (hasVerticalBarLayout) {
32731ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            // Vertical search bar
328b979b4294c14f2bc562a5279910bafb7ac1a92b4Dmitri Plotnikov            lp.gravity = Gravity.TOP | Gravity.LEFT;
32931ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            lp.width = searchBarSpaceHeightPx;
33031ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            lp.height = LayoutParams.MATCH_PARENT;
33131ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov            searchBarSpace.setPadding(
33231ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                    0, 2 * edgeMarginPx, 0,
33331ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov                    2 * edgeMarginPx);
33431ec0b51ac4d508f8727608b98d0bf916f2b20b9Dmitri Plotnikov        } else {
33565c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann            // Horizontal search bar
33665c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann            lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
33765c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann            lp.width = searchBarSpaceWidthPx;
33865c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann            lp.height = searchBarSpaceHeightPx;
33965c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann            searchBarSpace.setPadding(
34065c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann                    2 * edgeMarginPx,
34165c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann                    2 * edgeMarginPx,
34265c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann                    2 * edgeMarginPx, 0);
34365c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        }
34465c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        searchBarSpace.setLayoutParams(lp);
34565c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann
34665c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        // Layout the search bar
34765c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        View searchBar = launcher.getQsbBar();
34865c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
34965c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        lp.width = LayoutParams.MATCH_PARENT;
35065c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        lp.height = LayoutParams.MATCH_PARENT;
35165c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        searchBar.setLayoutParams(lp);
35265c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann
35365c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        // Layout the voice proxy
35465c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        View voiceButtonProxy = launcher.findViewById(R.id.voice_button_proxy);
35565c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        if (voiceButtonProxy != null) {
35665c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann            if (hasVerticalBarLayout) {
35765c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann                // TODO: MOVE THIS INTO SEARCH BAR MEASURE
35865c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann            } else {
35965c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann                lp = (FrameLayout.LayoutParams) voiceButtonProxy.getLayoutParams();
36065c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann                lp.gravity = Gravity.TOP | Gravity.END;
36165c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann                lp.width = (widthPx - searchBarSpaceWidthPx) / 2 +
36265c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann                        2 * iconSizePx;
36365c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann                lp.height = searchBarSpaceHeightPx;
36465c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann            }
36565c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        }
36665c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann
36765c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        // Layout the workspace
36865c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        View workspace = launcher.findViewById(R.id.workspace);
36965c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        lp = (FrameLayout.LayoutParams) workspace.getLayoutParams();
37065c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        lp.gravity = Gravity.CENTER;
37165c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        Rect padding = getWorkspacePadding(isLandscape
37265c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann                ? CellLayout.LANDSCAPE
37365c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann                : CellLayout.PORTRAIT);
37465c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        workspace.setPadding(padding.left, padding.top,
37565c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann                padding.right, padding.bottom);
37665c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        workspace.setLayoutParams(lp);
37765c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann
37865c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        // Layout the hotseat
37965c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        View hotseat = launcher.findViewById(R.id.hotseat);
38065c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams();
38165c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        if (hasVerticalBarLayout) {
38279bea93fe361aac5b4928ef090b5021bcba99428Dmitri Plotnikov            // Vertical hotseat
38379bea93fe361aac5b4928ef090b5021bcba99428Dmitri Plotnikov            lp.gravity = Gravity.RIGHT;
38479bea93fe361aac5b4928ef090b5021bcba99428Dmitri Plotnikov            lp.width = hotseatBarHeightPx;
38586092529f272b7ed2cee24fae397291696b29b80Dmitri Plotnikov            lp.height = LayoutParams.MATCH_PARENT;
38679bea93fe361aac5b4928ef090b5021bcba99428Dmitri Plotnikov            hotseat.setPadding(0, 2 * edgeMarginPx,
38779bea93fe361aac5b4928ef090b5021bcba99428Dmitri Plotnikov                    2 * edgeMarginPx, 2 * edgeMarginPx);
38879bea93fe361aac5b4928ef090b5021bcba99428Dmitri Plotnikov        } else if (isTablet()) {
38979bea93fe361aac5b4928ef090b5021bcba99428Dmitri Plotnikov            // Pad the hotseat with the grid gap calculated above
3902013058703aa110a1d7aac87ef408c96a3919472Daniel Lehmann            int gridGap = (int) ((widthPx - 2 * edgeMarginPx -
3912013058703aa110a1d7aac87ef408c96a3919472Daniel Lehmann                    (numColumns * cellWidthPx)) / (2 * (numColumns + 1)));
3922013058703aa110a1d7aac87ef408c96a3919472Daniel Lehmann            int gridWidth = (int) ((numColumns * cellWidthPx) +
3932013058703aa110a1d7aac87ef408c96a3919472Daniel Lehmann                    ((numColumns - 1) * gridGap));
3942013058703aa110a1d7aac87ef408c96a3919472Daniel Lehmann            int hotseatGap = (int) Math.max(0,
3952013058703aa110a1d7aac87ef408c96a3919472Daniel Lehmann                    (gridWidth - (numHotseatIcons * hotseatCellWidthPx))
3962013058703aa110a1d7aac87ef408c96a3919472Daniel Lehmann                            / (numHotseatIcons - 1));
39765c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann            lp.gravity = Gravity.BOTTOM;
39865c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann            lp.width = LayoutParams.MATCH_PARENT;
39965c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann            lp.height = hotseatBarHeightPx;
40065c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann            hotseat.setPadding(2 * edgeMarginPx + gridGap + hotseatGap, 0,
40165c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann                    2 * edgeMarginPx + gridGap + hotseatGap,
40265c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann                    2 * edgeMarginPx);
40365c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann        } else {
40465c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann            // For phones, layout the hotseat without any bottom margin
40565c90e23a0c141d390fc2177a1b461646f4f8a3aDaniel Lehmann            // to ensure that we have space for the folders
4063b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann            lp.gravity = Gravity.BOTTOM;
4073b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann            lp.width = LayoutParams.MATCH_PARENT;
4083b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann            lp.height = hotseatBarHeightPx;
4093b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann            hotseat.setPadding(2 * edgeMarginPx, 0,
4103b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann                    2 * edgeMarginPx, 0);
4113b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann        }
4123b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann        hotseat.setLayoutParams(lp);
413f0c4681ee4191e47103f8699dd1bf9cf7fc8b669Daisuke Miyakawa
414f0c4681ee4191e47103f8699dd1bf9cf7fc8b669Daisuke Miyakawa        // Layout the page indicators
415f0c4681ee4191e47103f8699dd1bf9cf7fc8b669Daisuke Miyakawa        View pageIndicator = launcher.findViewById(R.id.page_indicator);
416f0c4681ee4191e47103f8699dd1bf9cf7fc8b669Daisuke Miyakawa        if (pageIndicator != null) {
4173b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann            if (hasVerticalBarLayout) {
4183b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann                // Hide the page indicators when we have vertical search/hotseat
4193b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann                pageIndicator.setVisibility(View.GONE);
4203b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann            } else {
4213b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann                // Put the page indicators above the hotseat
4223b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann                lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams();
4233b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann                lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
4243b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann                lp.width = LayoutParams.WRAP_CONTENT;
4253b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann                lp.height = pageIndicatorHeightPx;
4263b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann                lp.bottomMargin = hotseatBarHeightPx;
4273b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann                pageIndicator.setLayoutParams(lp);
4283b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann            }
4293b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann        }
4303b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann    }
4313b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann}
4323b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann
4333b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmannpublic class DynamicGrid {
4343b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann    @SuppressWarnings("unused")
4353b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann    private static final String TAG = "DynamicGrid";
4363b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann
4373b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann    private DeviceProfile mProfile;
4383b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann    private float mMinWidth;
4393b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann    private float mMinHeight;
4403b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann
4413b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann    public static float dpiFromPx(int size, DisplayMetrics metrics){
4423b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann        float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
4433b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann        return (size / densityRatio);
4443b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann    }
4453b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann    public static int pxFromDp(float size, DisplayMetrics metrics) {
4463b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann        return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
4473b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann                size, metrics));
4483b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann    }
4493b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann    public static int pxFromSp(float size, DisplayMetrics metrics) {
4503b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann        return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
4513b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann                size, metrics));
4523b7d5949bfa524534e5d1aa29b8eaff8733ae00cDaniel Lehmann    }
453e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann
454e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann    public DynamicGrid(Resources resources, int minWidthPx, int minHeightPx,
455e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann                       int widthPx, int heightPx,
456e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann                       int awPx, int ahPx) {
457e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        DisplayMetrics dm = resources.getDisplayMetrics();
458e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        ArrayList<DeviceProfile> deviceProfiles =
459e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann                new ArrayList<DeviceProfile>();
460e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        // Our phone profiles include the bar sizes in each orientation
461e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        deviceProfiles.add(new DeviceProfile("Super Short Stubby",
462e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann                255, 300,  2, 3,  48, 12,  4, 48));
463e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        deviceProfiles.add(new DeviceProfile("Shorter Stubby",
464e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann                255, 400,  3, 3,  48, 12,  4, 48));
465e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        deviceProfiles.add(new DeviceProfile("Short Stubby",
466e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann                275, 420,  3, 4,  48, 12,  4, 48));
467e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        deviceProfiles.add(new DeviceProfile("Stubby",
468e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann                255, 450,  3, 4,  48, 12,  4, 48));
469e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        deviceProfiles.add(new DeviceProfile("Nexus S",
470e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann                296, 491.33f,  4, 4,  48, 12,  4, 48));
471e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        deviceProfiles.add(new DeviceProfile("Nexus 4",
472e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann                359, 518,  4, 4,  60, 12,  5, 56));
473e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        // The tablet profile is odd in that the landscape orientation
474e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        // also includes the nav bar on the side
475e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        deviceProfiles.add(new DeviceProfile("Nexus 7",
476e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann                575, 904,  6, 6,  72, 14.4f,  7, 60));
477e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        // Larger tablet profiles always have system bars on the top & bottom
478e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        deviceProfiles.add(new DeviceProfile("Nexus 10",
479e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann                727, 1207,  5, 8,  80, 14.4f,  9, 64));
480e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        /*
481e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        deviceProfiles.add(new DeviceProfile("Nexus 7",
482e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann                600, 960,  5, 5,  72, 14.4f,  5, 60));
483e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        deviceProfiles.add(new DeviceProfile("Nexus 10",
484e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann                800, 1280,  5, 5,  80, 14.4f,  6, 64));
485e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann         */
486e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        deviceProfiles.add(new DeviceProfile("20-inch Tablet",
487e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann                1527, 2527,  7, 7,  100, 20,  7, 72));
488e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        mMinWidth = dpiFromPx(minWidthPx, dm);
489e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        mMinHeight = dpiFromPx(minHeightPx, dm);
490e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann        mProfile = new DeviceProfile(deviceProfiles,
491e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann                mMinWidth, mMinHeight,
492e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann                widthPx, heightPx,
493e9b196e446cc34f690a043c5f5d9e5a7b8b92b3fDaniel Lehmann                awPx, ahPx,
494a126cddda7a8df442a1c4d63b4412d50c83725a6Daniel Lehmann                resources);
495a126cddda7a8df442a1c4d63b4412d50c83725a6Daniel Lehmann    }
496a126cddda7a8df442a1c4d63b4412d50c83725a6Daniel Lehmann
497a126cddda7a8df442a1c4d63b4412d50c83725a6Daniel Lehmann    DeviceProfile getDeviceProfile() {
498a126cddda7a8df442a1c4d63b4412d50c83725a6Daniel Lehmann        return mProfile;
499a126cddda7a8df442a1c4d63b4412d50c83725a6Daniel Lehmann    }
500a4aa90ce861ea56f403aa244905c9a4e326f2a10Flavio Lerda
501a4aa90ce861ea56f403aa244905c9a4e326f2a10Flavio Lerda    public String toString() {
502a4aa90ce861ea56f403aa244905c9a4e326f2a10Flavio Lerda        return "-------- DYNAMIC GRID ------- \n" +
503a4aa90ce861ea56f403aa244905c9a4e326f2a10Flavio Lerda                "Wd: " + mProfile.minWidthDps + ", Hd: " + mProfile.minHeightDps +
504a4aa90ce861ea56f403aa244905c9a4e326f2a10Flavio Lerda                ", W: " + mProfile.widthPx + ", H: " + mProfile.heightPx +
505a4aa90ce861ea56f403aa244905c9a4e326f2a10Flavio Lerda                " [r: " + mProfile.numRows + ", c: " + mProfile.numColumns +
506a4aa90ce861ea56f403aa244905c9a4e326f2a10Flavio Lerda                ", is: " + mProfile.iconSizePx + ", its: " + mProfile.iconTextSize +
507a4aa90ce861ea56f403aa244905c9a4e326f2a10Flavio Lerda                ", cw: " + mProfile.cellWidthPx + ", ch: " + mProfile.cellHeightPx +
508a4aa90ce861ea56f403aa244905c9a4e326f2a10Flavio Lerda                ", hc: " + mProfile.numHotseatIcons + ", his: " + mProfile.hotseatIconSizePx + "]";
509a4aa90ce861ea56f403aa244905c9a4e326f2a10Flavio Lerda    }
510a4aa90ce861ea56f403aa244905c9a4e326f2a10Flavio Lerda}
511a4aa90ce861ea56f403aa244905c9a4e326f2a10Flavio Lerda