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.view.MotionEvent; 32 import android.view.View; 33 import android.view.ViewGroup; 34 35 import com.android.launcher3.CellLayout.ContainerType; 36 import com.android.launcher3.celllayout.CellLayoutLayoutParams; 37 import com.android.launcher3.folder.FolderIcon; 38 import com.android.launcher3.model.data.ItemInfo; 39 import com.android.launcher3.views.ActivityContext; 40 import com.android.launcher3.widget.NavigableAppWidgetHostView; 41 42 public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon.FolderIconParent { 43 static final String TAG = "ShortcutAndWidgetContainer"; 44 45 // These are temporary variables to prevent having to allocate a new object just to 46 // return an (x, y) value from helper functions. Do NOT use them to maintain other state. 47 private final int[] mTmpCellXY = new int[2]; 48 49 private final Rect mTempRect = new Rect(); 50 51 @ContainerType 52 private final int mContainerType; 53 private final WallpaperManager mWallpaperManager; 54 55 private int mCellWidth; 56 private int mCellHeight; 57 private Point mBorderSpace; 58 59 private int mCountX; 60 private int mCountY; 61 62 private final ActivityContext mActivity; 63 private boolean mInvertIfRtl = false; 64 ShortcutAndWidgetContainer(Context context, @ContainerType int containerType)65 public ShortcutAndWidgetContainer(Context context, @ContainerType int containerType) { 66 super(context); 67 mActivity = ActivityContext.lookupContext(context); 68 mWallpaperManager = WallpaperManager.getInstance(context); 69 mContainerType = containerType; 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 ((NavigableAppWidgetHostView) child).getWidgetInset(profile, mTempRect); 128 final PointF appWidgetScale = profile.getAppWidgetScale((ItemInfo) child.getTag()); 129 lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY, 130 appWidgetScale.x, appWidgetScale.y, mBorderSpace, mTempRect); 131 } else { 132 lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY, 133 mBorderSpace, null); 134 } 135 } 136 137 // Set whether or not to invert the layout horizontally if the layout is in RTL mode. setInvertIfRtl(boolean invert)138 public void setInvertIfRtl(boolean invert) { 139 mInvertIfRtl = invert; 140 } 141 getCellContentHeight()142 public int getCellContentHeight() { 143 return Math.min(getMeasuredHeight(), 144 mActivity.getDeviceProfile().getCellContentHeight(mContainerType)); 145 } 146 measureChild(View child)147 public void measureChild(View child) { 148 CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams(); 149 final DeviceProfile dp = mActivity.getDeviceProfile(); 150 151 if (child instanceof NavigableAppWidgetHostView) { 152 ((NavigableAppWidgetHostView) child).getWidgetInset(dp, mTempRect); 153 final PointF appWidgetScale = dp.getAppWidgetScale((ItemInfo) child.getTag()); 154 lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY, 155 appWidgetScale.x, appWidgetScale.y, mBorderSpace, mTempRect); 156 } else { 157 lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY, 158 mBorderSpace, null); 159 // Center the icon/folder 160 int cHeight = getCellContentHeight(); 161 int cellPaddingY = dp.isScalableGrid && mContainerType == WORKSPACE 162 ? dp.cellYPaddingPx 163 : (int) Math.max(0, ((lp.height - cHeight) / 2f)); 164 165 // No need to add padding when cell layout border spacing is present. 166 boolean noPaddingX = 167 (dp.cellLayoutBorderSpacePx.x > 0 && mContainerType == WORKSPACE) 168 || (dp.folderCellLayoutBorderSpacePx > 0 && mContainerType == FOLDER) 169 || (dp.hotseatBorderSpace > 0 && mContainerType == HOTSEAT); 170 int cellPaddingX = noPaddingX 171 ? 0 172 : mContainerType == WORKSPACE 173 ? dp.workspaceCellPaddingXPx 174 : (int) (dp.edgeMarginPx / 2f); 175 child.setPadding(cellPaddingX, cellPaddingY, cellPaddingX, 0); 176 } 177 int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY); 178 int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY); 179 child.measure(childWidthMeasureSpec, childheightMeasureSpec); 180 } 181 invertLayoutHorizontally()182 public boolean invertLayoutHorizontally() { 183 return mInvertIfRtl && Utilities.isRtl(getResources()); 184 } 185 186 @Override onLayout(boolean changed, int l, int t, int r, int b)187 protected void onLayout(boolean changed, int l, int t, int r, int b) { 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 } 196 197 /** 198 * Core logic to layout a child for this ViewGroup. 199 */ layoutChild(View child)200 public void layoutChild(View child) { 201 CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams(); 202 if (child instanceof NavigableAppWidgetHostView) { 203 NavigableAppWidgetHostView nahv = (NavigableAppWidgetHostView) child; 204 205 // Scale and center the widget to fit within its cells. 206 DeviceProfile profile = mActivity.getDeviceProfile(); 207 final PointF appWidgetScale = profile.getAppWidgetScale((ItemInfo) child.getTag()); 208 float scaleX = appWidgetScale.x; 209 float scaleY = appWidgetScale.y; 210 211 nahv.setScaleToFit(Math.min(scaleX, scaleY)); 212 nahv.getTranslateDelegate().setTranslation(INDEX_WIDGET_CENTERING, 213 -(lp.width - (lp.width * scaleX)) / 2.0f, 214 -(lp.height - (lp.height * scaleY)) / 2.0f); 215 } 216 217 int childLeft = lp.x; 218 int childTop = lp.y; 219 child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height); 220 221 if (lp.dropped) { 222 lp.dropped = false; 223 224 final int[] cellXY = mTmpCellXY; 225 getLocationOnScreen(cellXY); 226 mWallpaperManager.sendWallpaperCommand(getWindowToken(), 227 WallpaperManager.COMMAND_DROP, 228 cellXY[0] + childLeft + lp.width / 2, 229 cellXY[1] + childTop + lp.height / 2, 0, null); 230 } 231 } 232 233 234 @Override onInterceptTouchEvent(MotionEvent ev)235 public boolean onInterceptTouchEvent(MotionEvent ev) { 236 if (ev.getAction() == ACTION_DOWN && getAlpha() == 0) { 237 // Dont let children handle touch, if we are not visible. 238 return true; 239 } 240 return super.onInterceptTouchEvent(ev); 241 } 242 243 @Override shouldDelayChildPressedState()244 public boolean shouldDelayChildPressedState() { 245 return false; 246 } 247 248 @Override requestChildFocus(View child, View focused)249 public void requestChildFocus(View child, View focused) { 250 super.requestChildFocus(child, focused); 251 if (child != null) { 252 Rect r = new Rect(); 253 child.getDrawingRect(r); 254 requestRectangleOnScreen(r); 255 } 256 } 257 258 @Override cancelLongPress()259 public void cancelLongPress() { 260 super.cancelLongPress(); 261 262 // Cancel long press for all children 263 final int count = getChildCount(); 264 for (int i = 0; i < count; i++) { 265 final View child = getChildAt(i); 266 child.cancelLongPress(); 267 } 268 } 269 270 @Override drawFolderLeaveBehindForIcon(FolderIcon child)271 public void drawFolderLeaveBehindForIcon(FolderIcon child) { 272 CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams(); 273 // While the folder is open, the position of the icon cannot change. 274 lp.canReorder = false; 275 if (mContainerType == HOTSEAT) { 276 CellLayout cl = (CellLayout) getParent(); 277 cl.setFolderLeaveBehindCell(lp.getCellX(), lp.getCellY()); 278 } 279 } 280 281 @Override clearFolderLeaveBehind(FolderIcon child)282 public void clearFolderLeaveBehind(FolderIcon child) { 283 ((CellLayoutLayoutParams) child.getLayoutParams()).canReorder = true; 284 if (mContainerType == HOTSEAT) { 285 CellLayout cl = (CellLayout) getParent(); 286 cl.clearFolderLeaveBehind(); 287 } 288 } 289 } 290