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