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 android.app.WallpaperManager;
22 import android.content.Context;
23 import android.graphics.Rect;
24 import android.view.MotionEvent;
25 import android.view.View;
26 import android.view.ViewGroup;
27 
28 import com.android.launcher3.CellLayout.ContainerType;
29 import com.android.launcher3.views.ActivityContext;
30 import com.android.launcher3.widget.LauncherAppWidgetHostView;
31 
32 public class ShortcutAndWidgetContainer extends ViewGroup {
33     static final String TAG = "ShortcutAndWidgetContainer";
34 
35     // These are temporary variables to prevent having to allocate a new object just to
36     // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
37     private final int[] mTmpCellXY = new int[2];
38 
39     @ContainerType private final int mContainerType;
40     private final WallpaperManager mWallpaperManager;
41 
42     private int mCellWidth;
43     private int mCellHeight;
44 
45     private int mCountX;
46 
47     private ActivityContext mActivity;
48     private boolean mInvertIfRtl = false;
49 
ShortcutAndWidgetContainer(Context context, @ContainerType int containerType)50     public ShortcutAndWidgetContainer(Context context, @ContainerType int containerType) {
51         super(context);
52         mActivity = ActivityContext.lookupContext(context);
53         mWallpaperManager = WallpaperManager.getInstance(context);
54         mContainerType = containerType;
55     }
56 
setCellDimensions(int cellWidth, int cellHeight, int countX, int countY)57     public void setCellDimensions(int cellWidth, int cellHeight, int countX, int countY) {
58         mCellWidth = cellWidth;
59         mCellHeight = cellHeight;
60         mCountX = countX;
61     }
62 
getChildAt(int x, int y)63     public View getChildAt(int x, int y) {
64         final int count = getChildCount();
65         for (int i = 0; i < count; i++) {
66             View child = getChildAt(i);
67             CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
68 
69             if ((lp.cellX <= x) && (x < lp.cellX + lp.cellHSpan) &&
70                     (lp.cellY <= y) && (y < lp.cellY + lp.cellVSpan)) {
71                 return child;
72             }
73         }
74         return null;
75     }
76 
77     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)78     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
79         int count = getChildCount();
80 
81         int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
82         int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
83         setMeasuredDimension(widthSpecSize, heightSpecSize);
84 
85         for (int i = 0; i < count; i++) {
86             View child = getChildAt(i);
87             if (child.getVisibility() != GONE) {
88                 measureChild(child);
89             }
90         }
91     }
92 
setupLp(View child)93     public void setupLp(View child) {
94         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
95         if (child instanceof LauncherAppWidgetHostView) {
96             DeviceProfile profile = mActivity.getDeviceProfile();
97             lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX,
98                     profile.appWidgetScale.x, profile.appWidgetScale.y);
99         } else {
100             lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX);
101         }
102     }
103 
104     // Set whether or not to invert the layout horizontally if the layout is in RTL mode.
setInvertIfRtl(boolean invert)105     public void setInvertIfRtl(boolean invert) {
106         mInvertIfRtl = invert;
107     }
108 
getCellContentHeight()109     public int getCellContentHeight() {
110         return Math.min(getMeasuredHeight(),
111                 mActivity.getDeviceProfile().getCellHeight(mContainerType));
112     }
113 
measureChild(View child)114     public void measureChild(View child) {
115         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
116         final DeviceProfile profile = mActivity.getDeviceProfile();
117 
118         if (child instanceof LauncherAppWidgetHostView) {
119             lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX,
120                     profile.appWidgetScale.x, profile.appWidgetScale.y);
121             // Widgets have their own padding
122         } else {
123             lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX);
124             // Center the icon/folder
125             int cHeight = getCellContentHeight();
126             int cellPaddingY = (int) Math.max(0, ((lp.height - cHeight) / 2f));
127             int cellPaddingX = mContainerType == CellLayout.WORKSPACE
128                     ? profile.workspaceCellPaddingXPx
129                     : (int) (profile.edgeMarginPx / 2f);
130             child.setPadding(cellPaddingX, cellPaddingY, cellPaddingX, 0);
131         }
132         int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
133         int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
134         child.measure(childWidthMeasureSpec, childheightMeasureSpec);
135     }
136 
invertLayoutHorizontally()137     public boolean invertLayoutHorizontally() {
138         return mInvertIfRtl && Utilities.isRtl(getResources());
139     }
140 
141     @Override
onLayout(boolean changed, int l, int t, int r, int b)142     protected void onLayout(boolean changed, int l, int t, int r, int b) {
143         int count = getChildCount();
144         for (int i = 0; i < count; i++) {
145             final View child = getChildAt(i);
146             if (child.getVisibility() != GONE) {
147                 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
148                 layoutChild(child);
149             }
150         }
151     }
152 
153     /**
154      * Core logic to layout a child for this ViewGroup.
155      */
layoutChild(View child)156     public void layoutChild(View child) {
157         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
158         if (child instanceof LauncherAppWidgetHostView) {
159             LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) child;
160 
161             // Scale and center the widget to fit within its cells.
162             DeviceProfile profile = mActivity.getDeviceProfile();
163             float scaleX = profile.appWidgetScale.x;
164             float scaleY = profile.appWidgetScale.y;
165 
166             lahv.setScaleToFit(Math.min(scaleX, scaleY));
167             lahv.setTranslationForCentering(-(lp.width - (lp.width * scaleX)) / 2.0f,
168                     -(lp.height - (lp.height * scaleY)) / 2.0f);
169         }
170 
171         int childLeft = lp.x;
172         int childTop = lp.y;
173         child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
174 
175         if (lp.dropped) {
176             lp.dropped = false;
177 
178             final int[] cellXY = mTmpCellXY;
179             getLocationOnScreen(cellXY);
180             mWallpaperManager.sendWallpaperCommand(getWindowToken(),
181                     WallpaperManager.COMMAND_DROP,
182                     cellXY[0] + childLeft + lp.width / 2,
183                     cellXY[1] + childTop + lp.height / 2, 0, null);
184         }
185     }
186 
187 
188     @Override
onInterceptTouchEvent(MotionEvent ev)189     public boolean onInterceptTouchEvent(MotionEvent ev) {
190         if (ev.getAction() == ACTION_DOWN && getAlpha() == 0) {
191             // Dont let children handle touch, if we are not visible.
192             return true;
193         }
194         return super.onInterceptTouchEvent(ev);
195     }
196 
197     @Override
shouldDelayChildPressedState()198     public boolean shouldDelayChildPressedState() {
199         return false;
200     }
201 
202     @Override
requestChildFocus(View child, View focused)203     public void requestChildFocus(View child, View focused) {
204         super.requestChildFocus(child, focused);
205         if (child != null) {
206             Rect r = new Rect();
207             child.getDrawingRect(r);
208             requestRectangleOnScreen(r);
209         }
210     }
211 
212     @Override
cancelLongPress()213     public void cancelLongPress() {
214         super.cancelLongPress();
215 
216         // Cancel long press for all children
217         final int count = getChildCount();
218         for (int i = 0; i < count; i++) {
219             final View child = getChildAt(i);
220             child.cancelLongPress();
221         }
222     }
223 }
224