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