1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.launcher3;
18 
19 import static android.view.MotionEvent.ACTION_DOWN;
20 
21 import static com.android.launcher3.CellLayout.FOLDER;
22 import static com.android.launcher3.CellLayout.WORKSPACE;
23 
24 import android.app.WallpaperManager;
25 import android.content.Context;
26 import android.graphics.Point;
27 import android.graphics.Rect;
28 import android.view.MotionEvent;
29 import android.view.View;
30 import android.view.ViewGroup;
31 
32 import com.android.launcher3.CellLayout.ContainerType;
33 import com.android.launcher3.folder.FolderIcon;
34 import com.android.launcher3.views.ActivityContext;
35 import com.android.launcher3.widget.NavigableAppWidgetHostView;
36 
37 public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon.FolderIconParent {
38     static final String TAG = "ShortcutAndWidgetContainer";
39 
40     // These are temporary variables to prevent having to allocate a new object just to
41     // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
42     private final int[] mTmpCellXY = new int[2];
43 
44     private final Rect mTempRect = new Rect();
45 
46     @ContainerType
47     private final int mContainerType;
48     private final WallpaperManager mWallpaperManager;
49 
50     private int mCellWidth;
51     private int mCellHeight;
52     private Point mBorderSpace;
53 
54     private int mCountX;
55     private int mCountY;
56 
57     private final ActivityContext mActivity;
58     private boolean mInvertIfRtl = false;
59 
ShortcutAndWidgetContainer(Context context, @ContainerType int containerType)60     public ShortcutAndWidgetContainer(Context context, @ContainerType int containerType) {
61         super(context);
62         mActivity = ActivityContext.lookupContext(context);
63         mWallpaperManager = WallpaperManager.getInstance(context);
64         mContainerType = containerType;
65     }
66 
setCellDimensions(int cellWidth, int cellHeight, int countX, int countY, Point borderSpace)67     public void setCellDimensions(int cellWidth, int cellHeight, int countX, int countY,
68             Point borderSpace) {
69         mCellWidth = cellWidth;
70         mCellHeight = cellHeight;
71         mCountX = countX;
72         mCountY = countY;
73         mBorderSpace = borderSpace;
74     }
75 
getChildAt(int cellX, int cellY)76     public View getChildAt(int cellX, int cellY) {
77         final int count = getChildCount();
78         for (int i = 0; i < count; i++) {
79             View child = getChildAt(i);
80             CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
81 
82             if ((lp.cellX <= cellX) && (cellX < lp.cellX + lp.cellHSpan)
83                     && (lp.cellY <= cellY) && (cellY < lp.cellY + lp.cellVSpan)) {
84                 return child;
85             }
86         }
87         return null;
88     }
89 
90     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)91     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
92         int count = getChildCount();
93 
94         int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
95         int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
96         setMeasuredDimension(widthSpecSize, heightSpecSize);
97 
98         for (int i = 0; i < count; i++) {
99             View child = getChildAt(i);
100             if (child.getVisibility() != GONE) {
101                 measureChild(child);
102             }
103         }
104     }
105 
setupLp(View child)106     public void setupLp(View child) {
107         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
108         if (child instanceof NavigableAppWidgetHostView) {
109             DeviceProfile profile = mActivity.getDeviceProfile();
110             ((NavigableAppWidgetHostView) child).getWidgetInset(profile, mTempRect);
111             lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
112                     profile.appWidgetScale.x, profile.appWidgetScale.y, mBorderSpace, mTempRect);
113         } else {
114             lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
115                     mBorderSpace, null);
116         }
117     }
118 
119     // Set whether or not to invert the layout horizontally if the layout is in RTL mode.
setInvertIfRtl(boolean invert)120     public void setInvertIfRtl(boolean invert) {
121         mInvertIfRtl = invert;
122     }
123 
getCellContentHeight()124     public int getCellContentHeight() {
125         return Math.min(getMeasuredHeight(),
126                 mActivity.getDeviceProfile().getCellContentHeight(mContainerType));
127     }
128 
measureChild(View child)129     public void measureChild(View child) {
130         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
131         final DeviceProfile dp = mActivity.getDeviceProfile();
132 
133         if (child instanceof NavigableAppWidgetHostView) {
134             ((NavigableAppWidgetHostView) child).getWidgetInset(dp, mTempRect);
135             lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
136                     dp.appWidgetScale.x, dp.appWidgetScale.y, mBorderSpace, mTempRect);
137         } else {
138             lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
139                     mBorderSpace, null);
140             // Center the icon/folder
141             int cHeight = getCellContentHeight();
142             int cellPaddingY = dp.isScalableGrid && mContainerType == WORKSPACE
143                     ? dp.cellYPaddingPx
144                     : (int) Math.max(0, ((lp.height - cHeight) / 2f));
145 
146             // No need to add padding when cell layout border spacing is present.
147             boolean noPaddingX =
148                     (dp.cellLayoutBorderSpacePx.x > 0 && mContainerType == WORKSPACE)
149                             || (dp.folderCellLayoutBorderSpacePx.x > 0 && mContainerType == FOLDER);
150             int cellPaddingX = noPaddingX
151                     ? 0
152                     : mContainerType == WORKSPACE
153                             ? dp.workspaceCellPaddingXPx
154                             : (int) (dp.edgeMarginPx / 2f);
155             child.setPadding(cellPaddingX, cellPaddingY, cellPaddingX, 0);
156         }
157         int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
158         int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
159         child.measure(childWidthMeasureSpec, childheightMeasureSpec);
160     }
161 
invertLayoutHorizontally()162     public boolean invertLayoutHorizontally() {
163         return mInvertIfRtl && Utilities.isRtl(getResources());
164     }
165 
166     @Override
onLayout(boolean changed, int l, int t, int r, int b)167     protected void onLayout(boolean changed, int l, int t, int r, int b) {
168         int count = getChildCount();
169         for (int i = 0; i < count; i++) {
170             final View child = getChildAt(i);
171             if (child.getVisibility() != GONE) {
172                 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
173                 layoutChild(child);
174             }
175         }
176     }
177 
178     /**
179      * Core logic to layout a child for this ViewGroup.
180      */
layoutChild(View child)181     public void layoutChild(View child) {
182         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
183         if (child instanceof NavigableAppWidgetHostView) {
184             NavigableAppWidgetHostView nahv = (NavigableAppWidgetHostView) child;
185 
186             // Scale and center the widget to fit within its cells.
187             DeviceProfile profile = mActivity.getDeviceProfile();
188             float scaleX = profile.appWidgetScale.x;
189             float scaleY = profile.appWidgetScale.y;
190 
191             nahv.setScaleToFit(Math.min(scaleX, scaleY));
192             nahv.setTranslationForCentering(-(lp.width - (lp.width * scaleX)) / 2.0f,
193                     -(lp.height - (lp.height * scaleY)) / 2.0f);
194         }
195 
196         int childLeft = lp.x;
197         int childTop = lp.y;
198         child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
199 
200         if (lp.dropped) {
201             lp.dropped = false;
202 
203             final int[] cellXY = mTmpCellXY;
204             getLocationOnScreen(cellXY);
205             mWallpaperManager.sendWallpaperCommand(getWindowToken(),
206                     WallpaperManager.COMMAND_DROP,
207                     cellXY[0] + childLeft + lp.width / 2,
208                     cellXY[1] + childTop + lp.height / 2, 0, null);
209         }
210     }
211 
212 
213     @Override
onInterceptTouchEvent(MotionEvent ev)214     public boolean onInterceptTouchEvent(MotionEvent ev) {
215         if (ev.getAction() == ACTION_DOWN && getAlpha() == 0) {
216             // Dont let children handle touch, if we are not visible.
217             return true;
218         }
219         return super.onInterceptTouchEvent(ev);
220     }
221 
222     @Override
shouldDelayChildPressedState()223     public boolean shouldDelayChildPressedState() {
224         return false;
225     }
226 
227     @Override
requestChildFocus(View child, View focused)228     public void requestChildFocus(View child, View focused) {
229         super.requestChildFocus(child, focused);
230         if (child != null) {
231             Rect r = new Rect();
232             child.getDrawingRect(r);
233             requestRectangleOnScreen(r);
234         }
235     }
236 
237     @Override
cancelLongPress()238     public void cancelLongPress() {
239         super.cancelLongPress();
240 
241         // Cancel long press for all children
242         final int count = getChildCount();
243         for (int i = 0; i < count; i++) {
244             final View child = getChildAt(i);
245             child.cancelLongPress();
246         }
247     }
248 
249     @Override
drawFolderLeaveBehindForIcon(FolderIcon child)250     public void drawFolderLeaveBehindForIcon(FolderIcon child) {
251         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
252         // While the folder is open, the position of the icon cannot change.
253         lp.canReorder = false;
254         if (mContainerType == CellLayout.HOTSEAT) {
255             CellLayout cl = (CellLayout) getParent();
256             cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
257         }
258     }
259 
260     @Override
clearFolderLeaveBehind(FolderIcon child)261     public void clearFolderLeaveBehind(FolderIcon child) {
262         ((CellLayout.LayoutParams) child.getLayoutParams()).canReorder = true;
263         if (mContainerType == CellLayout.HOTSEAT) {
264             CellLayout cl = (CellLayout) getParent();
265             cl.clearFolderLeaveBehind();
266         }
267     }
268 }
269