1 /* 2 * Copyright (C) 2011 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 com.android.launcher3.ButtonDropTarget.TOOLTIP_DEFAULT; 20 import static com.android.launcher3.ButtonDropTarget.TOOLTIP_LEFT; 21 import static com.android.launcher3.ButtonDropTarget.TOOLTIP_RIGHT; 22 import static com.android.launcher3.anim.AlphaUpdateListener.updateVisibility; 23 24 import android.animation.TimeInterpolator; 25 import android.content.Context; 26 import android.graphics.Rect; 27 import android.util.AttributeSet; 28 import android.view.Gravity; 29 import android.view.View; 30 import android.view.ViewDebug; 31 import android.view.ViewPropertyAnimator; 32 import android.widget.FrameLayout; 33 34 import com.android.launcher3.anim.Interpolators; 35 import com.android.launcher3.dragndrop.DragController; 36 import com.android.launcher3.dragndrop.DragController.DragListener; 37 import com.android.launcher3.dragndrop.DragOptions; 38 39 /* 40 * The top bar containing various drop targets: Delete/App Info/Uninstall. 41 */ 42 public class DropTargetBar extends FrameLayout 43 implements DragListener, Insettable { 44 45 protected static final int DEFAULT_DRAG_FADE_DURATION = 175; 46 protected static final TimeInterpolator DEFAULT_INTERPOLATOR = Interpolators.ACCEL; 47 48 private final Runnable mFadeAnimationEndRunnable = 49 () -> updateVisibility(DropTargetBar.this); 50 51 @ViewDebug.ExportedProperty(category = "launcher") 52 protected boolean mDeferOnDragEnd; 53 54 @ViewDebug.ExportedProperty(category = "launcher") 55 protected boolean mVisible = false; 56 57 private ButtonDropTarget[] mDropTargets; 58 private ViewPropertyAnimator mCurrentAnimation; 59 60 private boolean mIsVertical = true; 61 62 public DropTargetBar(Context context, AttributeSet attrs) { 63 super(context, attrs); 64 } 65 66 public DropTargetBar(Context context, AttributeSet attrs, int defStyle) { 67 super(context, attrs, defStyle); 68 } 69 70 @Override 71 protected void onFinishInflate() { 72 super.onFinishInflate(); 73 mDropTargets = new ButtonDropTarget[getChildCount()]; 74 for (int i = 0; i < mDropTargets.length; i++) { 75 mDropTargets[i] = (ButtonDropTarget) getChildAt(i); 76 mDropTargets[i].setDropTargetBar(this); 77 } 78 } 79 80 @Override 81 public void setInsets(Rect insets) { 82 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); 83 DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile(); 84 mIsVertical = grid.isVerticalBarLayout(); 85 86 lp.leftMargin = insets.left; 87 lp.topMargin = insets.top; 88 lp.bottomMargin = insets.bottom; 89 lp.rightMargin = insets.right; 90 int tooltipLocation = TOOLTIP_DEFAULT; 91 92 if (grid.isVerticalBarLayout()) { 93 lp.width = grid.dropTargetBarSizePx; 94 lp.height = grid.availableHeightPx - 2 * grid.edgeMarginPx; 95 lp.gravity = grid.isSeascape() ? Gravity.RIGHT : Gravity.LEFT; 96 tooltipLocation = grid.isSeascape() ? TOOLTIP_LEFT : TOOLTIP_RIGHT; 97 } else { 98 int gap; 99 if (grid.isTablet) { 100 // XXX: If the icon size changes across orientations, we will have to take 101 // that into account here too. 102 gap = ((grid.widthPx - 2 * grid.edgeMarginPx 103 - (grid.inv.numColumns * grid.cellWidthPx)) 104 / (2 * (grid.inv.numColumns + 1))) 105 + grid.edgeMarginPx; 106 } else { 107 gap = grid.desiredWorkspaceLeftRightMarginPx - grid.defaultWidgetPadding.right; 108 } 109 lp.width = grid.availableWidthPx - 2 * gap; 110 111 lp.topMargin += grid.edgeMarginPx; 112 lp.height = grid.dropTargetBarSizePx; 113 lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP; 114 } 115 setLayoutParams(lp); 116 for (ButtonDropTarget button : mDropTargets) { 117 button.setToolTipLocation(tooltipLocation); 118 } 119 } 120 121 public void setup(DragController dragController) { 122 dragController.addDragListener(this); 123 for (int i = 0; i < mDropTargets.length; i++) { 124 dragController.addDragListener(mDropTargets[i]); 125 dragController.addDropTarget(mDropTargets[i]); 126 } 127 } 128 129 @Override 130 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 131 int width = MeasureSpec.getSize(widthMeasureSpec); 132 int height = MeasureSpec.getSize(heightMeasureSpec); 133 134 if (mIsVertical) { 135 int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); 136 int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); 137 138 for (ButtonDropTarget button : mDropTargets) { 139 if (button.getVisibility() != GONE) { 140 button.setTextVisible(false); 141 button.measure(widthSpec, heightSpec); 142 } 143 } 144 } else { 145 int visibleCount = getVisibleButtonsCount(); 146 int availableWidth = width / visibleCount; 147 boolean textVisible = true; 148 for (ButtonDropTarget buttons : mDropTargets) { 149 if (buttons.getVisibility() != GONE) { 150 textVisible = textVisible && !buttons.isTextTruncated(availableWidth); 151 } 152 } 153 154 int widthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST); 155 int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); 156 for (ButtonDropTarget button : mDropTargets) { 157 if (button.getVisibility() != GONE) { 158 button.setTextVisible(textVisible); 159 button.measure(widthSpec, heightSpec); 160 } 161 } 162 } 163 setMeasuredDimension(width, height); 164 } 165 166 @Override 167 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 168 if (mIsVertical) { 169 int gap = getResources().getDimensionPixelSize(R.dimen.drop_target_vertical_gap); 170 int start = gap; 171 int end; 172 173 for (ButtonDropTarget button : mDropTargets) { 174 if (button.getVisibility() != GONE) { 175 end = start + button.getMeasuredHeight(); 176 button.layout(0, start, button.getMeasuredWidth(), end); 177 start = end + gap; 178 } 179 } 180 } else { 181 int visibleCount = getVisibleButtonsCount(); 182 int frameSize = (right - left) / visibleCount; 183 184 int start = frameSize / 2; 185 int halfWidth; 186 for (ButtonDropTarget button : mDropTargets) { 187 if (button.getVisibility() != GONE) { 188 halfWidth = button.getMeasuredWidth() / 2; 189 button.layout(start - halfWidth, 0, 190 start + halfWidth, button.getMeasuredHeight()); 191 start = start + frameSize; 192 } 193 } 194 } 195 } 196 197 private int getVisibleButtonsCount() { 198 int visibleCount = 0; 199 for (ButtonDropTarget buttons : mDropTargets) { 200 if (buttons.getVisibility() != GONE) { 201 visibleCount++; 202 } 203 } 204 return visibleCount; 205 } 206 207 private void animateToVisibility(boolean isVisible) { 208 if (mVisible != isVisible) { 209 mVisible = isVisible; 210 211 // Cancel any existing animation 212 if (mCurrentAnimation != null) { 213 mCurrentAnimation.cancel(); 214 mCurrentAnimation = null; 215 } 216 217 float finalAlpha = mVisible ? 1 : 0; 218 if (Float.compare(getAlpha(), finalAlpha) != 0) { 219 setVisibility(View.VISIBLE); 220 mCurrentAnimation = animate().alpha(finalAlpha) 221 .setInterpolator(DEFAULT_INTERPOLATOR) 222 .setDuration(DEFAULT_DRAG_FADE_DURATION) 223 .withEndAction(mFadeAnimationEndRunnable); 224 } 225 226 } 227 } 228 229 /* 230 * DragController.DragListener implementation 231 */ 232 @Override 233 public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { 234 animateToVisibility(true); 235 } 236 237 /** 238 * This is called to defer hiding the delete drop target until the drop animation has completed, 239 * instead of hiding immediately when the drag has ended. 240 */ 241 protected void deferOnDragEnd() { 242 mDeferOnDragEnd = true; 243 } 244 245 @Override 246 public void onDragEnd() { 247 if (!mDeferOnDragEnd) { 248 animateToVisibility(false); 249 } else { 250 mDeferOnDragEnd = false; 251 } 252 } 253 254 public ButtonDropTarget[] getDropTargets() { 255 return mDropTargets; 256 } 257 } 258