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.getWallpaperDeviceProfile();
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.getWallpaperDeviceProfile().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.getWallpaperDeviceProfile();
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 
149                 if (child instanceof LauncherAppWidgetHostView) {
150                     LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) child;
151 
152                     // Scale and center the widget to fit within its cells.
153                     DeviceProfile profile = mActivity.getDeviceProfile();
154                     float scaleX = profile.appWidgetScale.x;
155                     float scaleY = profile.appWidgetScale.y;
156 
157                     lahv.setScaleToFit(Math.min(scaleX, scaleY));
158                     lahv.setTranslationForCentering(-(lp.width - (lp.width * scaleX)) / 2.0f,
159                             -(lp.height - (lp.height * scaleY)) / 2.0f);
160                 }
161 
162                 int childLeft = lp.x;
163                 int childTop = lp.y;
164                 child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
165 
166                 if (lp.dropped) {
167                     lp.dropped = false;
168 
169                     final int[] cellXY = mTmpCellXY;
170                     getLocationOnScreen(cellXY);
171                     mWallpaperManager.sendWallpaperCommand(getWindowToken(),
172                             WallpaperManager.COMMAND_DROP,
173                             cellXY[0] + childLeft + lp.width / 2,
174                             cellXY[1] + childTop + lp.height / 2, 0, null);
175                 }
176             }
177         }
178     }
179 
180     @Override
onInterceptTouchEvent(MotionEvent ev)181     public boolean onInterceptTouchEvent(MotionEvent ev) {
182         if (ev.getAction() == ACTION_DOWN && getAlpha() == 0) {
183             // Dont let children handle touch, if we are not visible.
184             return true;
185         }
186         return super.onInterceptTouchEvent(ev);
187     }
188 
189     @Override
shouldDelayChildPressedState()190     public boolean shouldDelayChildPressedState() {
191         return false;
192     }
193 
194     @Override
requestChildFocus(View child, View focused)195     public void requestChildFocus(View child, View focused) {
196         super.requestChildFocus(child, focused);
197         if (child != null) {
198             Rect r = new Rect();
199             child.getDrawingRect(r);
200             requestRectangleOnScreen(r);
201         }
202     }
203 
204     @Override
cancelLongPress()205     public void cancelLongPress() {
206         super.cancelLongPress();
207 
208         // Cancel long press for all children
209         final int count = getChildCount();
210         for (int i = 0; i < count; i++) {
211             final View child = getChildAt(i);
212             child.cancelLongPress();
213         }
214     }
215 }
216