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