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.HOTSEAT; 23 import static com.android.launcher3.CellLayout.WORKSPACE; 24 import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_WIDGET_CENTERING; 25 26 import android.app.WallpaperManager; 27 import android.content.Context; 28 import android.graphics.Point; 29 import android.graphics.PointF; 30 import android.graphics.Rect; 31 import android.os.Trace; 32 import android.view.MotionEvent; 33 import android.view.View; 34 import android.view.ViewGroup; 35 36 import com.android.launcher3.CellLayout.ContainerType; 37 import com.android.launcher3.celllayout.CellLayoutLayoutParams; 38 import com.android.launcher3.folder.FolderIcon; 39 import com.android.launcher3.model.data.ItemInfo; 40 import com.android.launcher3.views.ActivityContext; 41 import com.android.launcher3.widget.NavigableAppWidgetHostView; 42 43 public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon.FolderIconParent { 44 static final String TAG = "ShortcutAndWidgetContainer"; 45 46 // These are temporary variables to prevent having to allocate a new object just to 47 // return an (x, y) value from helper functions. Do NOT use them to maintain other state. 48 private final int[] mTmpCellXY = new int[2]; 49 50 @ContainerType 51 private final int mContainerType; 52 private final WallpaperManager mWallpaperManager; 53 54 private int mCellWidth; 55 private int mCellHeight; 56 private Point mBorderSpace; 57 58 private int mCountX; 59 private int mCountY; 60 61 private final ActivityContext mActivity; 62 private boolean mInvertIfRtl = false; 63 ShortcutAndWidgetContainer(Context context, @ContainerType int containerType)64 public ShortcutAndWidgetContainer(Context context, @ContainerType int containerType) { 65 super(context); 66 mActivity = ActivityContext.lookupContext(context); 67 mWallpaperManager = WallpaperManager.getInstance(context); 68 mContainerType = containerType; 69 setClipChildren(false); 70 } 71 setCellDimensions(int cellWidth, int cellHeight, int countX, int countY, Point borderSpace)72 public void setCellDimensions(int cellWidth, int cellHeight, int countX, int countY, 73 Point borderSpace) { 74 mCellWidth = cellWidth; 75 mCellHeight = cellHeight; 76 mCountX = countX; 77 mCountY = countY; 78 mBorderSpace = borderSpace; 79 } 80 getChildAt(int cellX, int cellY)81 public View getChildAt(int cellX, int cellY) { 82 final int count = getChildCount(); 83 for (int i = 0; i < count; i++) { 84 View child = getChildAt(i); 85 CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams(); 86 87 if ((lp.getCellX() <= cellX) && (cellX < lp.getCellX() + lp.cellHSpan) 88 && (lp.getCellY() <= cellY) && (cellY < lp.getCellY() + lp.cellVSpan)) { 89 return child; 90 } 91 } 92 return null; 93 } 94 95 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)96 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 97 int count = getChildCount(); 98 99 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); 100 int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); 101 setMeasuredDimension(widthSpecSize, heightSpecSize); 102 103 for (int i = 0; i < count; i++) { 104 View child = getChildAt(i); 105 if (child.getVisibility() != GONE) { 106 measureChild(child); 107 } 108 } 109 } 110 111 /** 112 * Adds view to Layout a new position and it does not trigger a layout request. 113 * For more information check documentation for 114 * {@code ViewGroup#addViewInLayout(View, int, LayoutParams, boolean)} 115 * 116 * @param child view to be added 117 * @return true if the child was added, false otherwise 118 */ addViewInLayout(View child, LayoutParams layoutParams)119 public boolean addViewInLayout(View child, LayoutParams layoutParams) { 120 return super.addViewInLayout(child, -1, layoutParams, true); 121 } 122 setupLp(View child)123 public void setupLp(View child) { 124 CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams(); 125 if (child instanceof NavigableAppWidgetHostView) { 126 DeviceProfile profile = mActivity.getDeviceProfile(); 127 final PointF appWidgetScale = profile.getAppWidgetScale((ItemInfo) child.getTag()); 128 lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY, 129 appWidgetScale.x, appWidgetScale.y, mBorderSpace, profile.widgetPadding); 130 } else { 131 lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY, 132 mBorderSpace); 133 } 134 } 135 136 // Set whether or not to invert the layout horizontally if the layout is in RTL mode. setInvertIfRtl(boolean invert)137 public void setInvertIfRtl(boolean invert) { 138 mInvertIfRtl = invert; 139 } 140 getCellContentHeight()141 public int getCellContentHeight() { 142 return Math.min(getMeasuredHeight(), 143 mActivity.getDeviceProfile().getCellContentHeight(mContainerType)); 144 } 145 measureChild(View child)146 public void measureChild(View child) { 147 CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams(); 148 final DeviceProfile dp = mActivity.getDeviceProfile(); 149 150 if (child instanceof NavigableAppWidgetHostView) { 151 final PointF appWidgetScale = dp.getAppWidgetScale((ItemInfo) child.getTag()); 152 lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY, 153 appWidgetScale.x, appWidgetScale.y, mBorderSpace, dp.widgetPadding); 154 } else { 155 lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY, 156 mBorderSpace); 157 // Center the icon/folder 158 int cHeight = getCellContentHeight(); 159 int cellPaddingY = 160 dp.cellYPaddingPx >= 0 && mContainerType == WORKSPACE 161 ? dp.cellYPaddingPx 162 : (int) Math.max(0, ((lp.height - cHeight) / 2f)); 163 164 // No need to add padding when cell layout border spacing is present. 165 boolean noPaddingX = 166 (dp.cellLayoutBorderSpacePx.x > 0 && mContainerType == WORKSPACE) 167 || (dp.folderCellLayoutBorderSpacePx.x > 0 && mContainerType == FOLDER) 168 || (dp.hotseatBorderSpace > 0 && mContainerType == HOTSEAT); 169 int cellPaddingX = noPaddingX 170 ? 0 171 : mContainerType == WORKSPACE 172 ? dp.workspaceCellPaddingXPx 173 : (int) (dp.edgeMarginPx / 2f); 174 child.setPadding(cellPaddingX, cellPaddingY, cellPaddingX, 0); 175 } 176 int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY); 177 int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY); 178 child.measure(childWidthMeasureSpec, childheightMeasureSpec); 179 } 180 invertLayoutHorizontally()181 public boolean invertLayoutHorizontally() { 182 return mInvertIfRtl && Utilities.isRtl(getResources()); 183 } 184 185 @Override onLayout(boolean changed, int l, int t, int r, int b)186 protected void onLayout(boolean changed, int l, int t, int r, int b) { 187 Trace.beginSection("ShortcutAndWidgetConteiner#onLayout"); 188 int count = getChildCount(); 189 for (int i = 0; i < count; i++) { 190 final View child = getChildAt(i); 191 if (child.getVisibility() != GONE) { 192 layoutChild(child); 193 } 194 } 195 Trace.endSection(); 196 } 197 198 /** 199 * Core logic to layout a child for this ViewGroup. 200 */ layoutChild(View child)201 public void layoutChild(View child) { 202 CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams(); 203 if (child instanceof NavigableAppWidgetHostView) { 204 NavigableAppWidgetHostView nahv = (NavigableAppWidgetHostView) child; 205 206 // Scale and center the widget to fit within its cells. 207 DeviceProfile profile = mActivity.getDeviceProfile(); 208 final PointF appWidgetScale = profile.getAppWidgetScale((ItemInfo) child.getTag()); 209 float scaleX = appWidgetScale.x; 210 float scaleY = appWidgetScale.y; 211 212 nahv.setScaleToFit(Math.min(scaleX, scaleY)); 213 nahv.getTranslateDelegate().setTranslation(INDEX_WIDGET_CENTERING, 214 -(lp.width - (lp.width * scaleX)) / 2.0f, 215 -(lp.height - (lp.height * scaleY)) / 2.0f); 216 } 217 218 int childLeft = lp.x; 219 int childTop = lp.y; 220 child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height); 221 222 if (lp.dropped) { 223 lp.dropped = false; 224 225 final int[] cellXY = mTmpCellXY; 226 getLocationOnScreen(cellXY); 227 mWallpaperManager.sendWallpaperCommand(getWindowToken(), 228 WallpaperManager.COMMAND_DROP, 229 cellXY[0] + childLeft + lp.width / 2, 230 cellXY[1] + childTop + lp.height / 2, 0, null); 231 } 232 } 233 234 235 @Override onInterceptTouchEvent(MotionEvent ev)236 public boolean onInterceptTouchEvent(MotionEvent ev) { 237 if (ev.getAction() == ACTION_DOWN && getAlpha() == 0) { 238 // Dont let children handle touch, if we are not visible. 239 return true; 240 } 241 return super.onInterceptTouchEvent(ev); 242 } 243 244 @Override shouldDelayChildPressedState()245 public boolean shouldDelayChildPressedState() { 246 return false; 247 } 248 249 @Override requestChildFocus(View child, View focused)250 public void requestChildFocus(View child, View focused) { 251 super.requestChildFocus(child, focused); 252 if (child != null) { 253 Rect r = new Rect(); 254 child.getDrawingRect(r); 255 requestRectangleOnScreen(r); 256 } 257 } 258 259 @Override cancelLongPress()260 public void cancelLongPress() { 261 super.cancelLongPress(); 262 263 // Cancel long press for all children 264 final int count = getChildCount(); 265 for (int i = 0; i < count; i++) { 266 final View child = getChildAt(i); 267 child.cancelLongPress(); 268 } 269 } 270 271 @Override drawFolderLeaveBehindForIcon(FolderIcon child)272 public void drawFolderLeaveBehindForIcon(FolderIcon child) { 273 CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams(); 274 // While the folder is open, the position of the icon cannot change. 275 lp.canReorder = false; 276 if (mContainerType == HOTSEAT) { 277 CellLayout cl = (CellLayout) getParent(); 278 cl.setFolderLeaveBehindCell(lp.getCellX(), lp.getCellY()); 279 } 280 } 281 282 @Override clearFolderLeaveBehind(FolderIcon child)283 public void clearFolderLeaveBehind(FolderIcon child) { 284 ((CellLayoutLayoutParams) child.getLayoutParams()).canReorder = true; 285 if (mContainerType == HOTSEAT) { 286 CellLayout cl = (CellLayout) getParent(); 287 cl.clearFolderLeaveBehind(); 288 } 289 } 290 } 291