• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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 android.animation.AnimatorSet;
20 import android.animation.FloatArrayEvaluator;
21 import android.animation.ObjectAnimator;
22 import android.animation.ValueAnimator;
23 import android.animation.ValueAnimator.AnimatorUpdateListener;
24 import android.content.Context;
25 import android.content.res.ColorStateList;
26 import android.content.res.Resources;
27 import android.content.res.TypedArray;
28 import android.graphics.ColorMatrix;
29 import android.graphics.ColorMatrixColorFilter;
30 import android.graphics.Rect;
31 import android.graphics.drawable.Drawable;
32 import android.util.AttributeSet;
33 import android.view.View;
34 import android.view.View.OnClickListener;
35 import android.view.ViewGroup;
36 import android.view.accessibility.AccessibilityEvent;
37 import android.view.animation.DecelerateInterpolator;
38 import android.view.animation.LinearInterpolator;
39 import android.widget.TextView;
40 
41 import com.android.launcher3.dragndrop.DragController;
42 import com.android.launcher3.dragndrop.DragLayer;
43 import com.android.launcher3.dragndrop.DragOptions;
44 import com.android.launcher3.dragndrop.DragView;
45 import com.android.launcher3.util.Themes;
46 import com.android.launcher3.util.Thunk;
47 
48 /**
49  * Implements a DropTarget.
50  */
51 public abstract class ButtonDropTarget extends TextView
52         implements DropTarget, DragController.DragListener, OnClickListener {
53 
54     private static final int DRAG_VIEW_DROP_DURATION = 285;
55 
56     private final boolean mHideParentOnDisable;
57     protected final Launcher mLauncher;
58 
59     private int mBottomDragPadding;
60     protected DropTargetBar mDropTargetBar;
61 
62     /** Whether this drop target is active for the current drag */
63     protected boolean mActive;
64     /** Whether an accessible drag is in progress */
65     private boolean mAccessibleDrag;
66     /** An item must be dragged at least this many pixels before this drop target is enabled. */
67     private final int mDragDistanceThreshold;
68 
69     /** The paint applied to the drag view on hover */
70     protected int mHoverColor = 0;
71 
72     protected ColorStateList mOriginalTextColor;
73     protected Drawable mDrawable;
74 
75     private AnimatorSet mCurrentColorAnim;
76     @Thunk ColorMatrix mSrcFilter, mDstFilter, mCurrentFilter;
77 
ButtonDropTarget(Context context, AttributeSet attrs)78     public ButtonDropTarget(Context context, AttributeSet attrs) {
79         this(context, attrs, 0);
80     }
81 
ButtonDropTarget(Context context, AttributeSet attrs, int defStyle)82     public ButtonDropTarget(Context context, AttributeSet attrs, int defStyle) {
83         super(context, attrs, defStyle);
84         mLauncher = Launcher.getLauncher(context);
85 
86         Resources resources = getResources();
87         mBottomDragPadding = resources.getDimensionPixelSize(R.dimen.drop_target_drag_padding);
88 
89         TypedArray a = context.obtainStyledAttributes(attrs,
90                 R.styleable.ButtonDropTarget, defStyle, 0);
91         mHideParentOnDisable = a.getBoolean(R.styleable.ButtonDropTarget_hideParentOnDisable, false);
92         a.recycle();
93         mDragDistanceThreshold = resources.getDimensionPixelSize(R.dimen.drag_distanceThreshold);
94     }
95 
96     @Override
onFinishInflate()97     protected void onFinishInflate() {
98         super.onFinishInflate();
99         mOriginalTextColor = getTextColors();
100     }
101 
setDrawable(int resId)102     protected void setDrawable(int resId) {
103         // We do not set the drawable in the xml as that inflates two drawables corresponding to
104         // drawableLeft and drawableStart.
105         mDrawable = getResources().getDrawable(resId);
106         setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
107     }
108 
setDropTargetBar(DropTargetBar dropTargetBar)109     public void setDropTargetBar(DropTargetBar dropTargetBar) {
110         mDropTargetBar = dropTargetBar;
111     }
112 
113     @Override
onDragEnter(DragObject d)114     public final void onDragEnter(DragObject d) {
115         d.dragView.setColor(mHoverColor);
116         animateTextColor(mHoverColor);
117         if (d.stateAnnouncer != null) {
118             d.stateAnnouncer.cancel();
119         }
120         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
121     }
122 
123     @Override
onDragOver(DragObject d)124     public void onDragOver(DragObject d) {
125         // Do nothing
126     }
127 
resetHoverColor()128     protected void resetHoverColor() {
129         animateTextColor(mOriginalTextColor.getDefaultColor());
130     }
131 
animateTextColor(int targetColor)132     private void animateTextColor(int targetColor) {
133         if (mCurrentColorAnim != null) {
134             mCurrentColorAnim.cancel();
135         }
136 
137         mCurrentColorAnim = new AnimatorSet();
138         mCurrentColorAnim.setDuration(DragView.COLOR_CHANGE_DURATION);
139 
140         if (mSrcFilter == null) {
141             mSrcFilter = new ColorMatrix();
142             mDstFilter = new ColorMatrix();
143             mCurrentFilter = new ColorMatrix();
144         }
145 
146         Themes.setColorScaleOnMatrix(getTextColor(), mSrcFilter);
147         Themes.setColorScaleOnMatrix(targetColor, mDstFilter);
148         ValueAnimator anim1 = ValueAnimator.ofObject(
149                 new FloatArrayEvaluator(mCurrentFilter.getArray()),
150                 mSrcFilter.getArray(), mDstFilter.getArray());
151         anim1.addUpdateListener(new AnimatorUpdateListener() {
152 
153             @Override
154             public void onAnimationUpdate(ValueAnimator animation) {
155                 mDrawable.setColorFilter(new ColorMatrixColorFilter(mCurrentFilter));
156                 invalidate();
157             }
158         });
159 
160         mCurrentColorAnim.play(anim1);
161         mCurrentColorAnim.play(ObjectAnimator.ofArgb(this, "textColor", targetColor));
162         mCurrentColorAnim.start();
163     }
164 
165     @Override
onDragExit(DragObject d)166     public final void onDragExit(DragObject d) {
167         if (!d.dragComplete) {
168             d.dragView.setColor(0);
169             resetHoverColor();
170         } else {
171             // Restore the hover color
172             d.dragView.setColor(mHoverColor);
173         }
174     }
175 
176     @Override
onDragStart(DropTarget.DragObject dragObject, DragOptions options)177     public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
178         mActive = supportsDrop(dragObject.dragSource, dragObject.dragInfo);
179         mDrawable.setColorFilter(null);
180         if (mCurrentColorAnim != null) {
181             mCurrentColorAnim.cancel();
182             mCurrentColorAnim = null;
183         }
184         setTextColor(mOriginalTextColor);
185         (mHideParentOnDisable ? ((ViewGroup) getParent()) : this)
186                 .setVisibility(mActive ? View.VISIBLE : View.GONE);
187 
188         mAccessibleDrag = options.isAccessibleDrag;
189         setOnClickListener(mAccessibleDrag ? this : null);
190     }
191 
192     @Override
acceptDrop(DragObject dragObject)193     public final boolean acceptDrop(DragObject dragObject) {
194         return supportsDrop(dragObject.dragSource, dragObject.dragInfo);
195     }
196 
supportsDrop(DragSource source, ItemInfo info)197     protected abstract boolean supportsDrop(DragSource source, ItemInfo info);
198 
199     @Override
isDropEnabled()200     public boolean isDropEnabled() {
201         return mActive && (mAccessibleDrag ||
202                 mLauncher.getDragController().getDistanceDragged() >= mDragDistanceThreshold);
203     }
204 
205     @Override
onDragEnd()206     public void onDragEnd() {
207         mActive = false;
208         setOnClickListener(null);
209     }
210 
211     /**
212      * On drop animate the dropView to the icon.
213      */
214     @Override
onDrop(final DragObject d)215     public void onDrop(final DragObject d) {
216         final DragLayer dragLayer = mLauncher.getDragLayer();
217         final Rect from = new Rect();
218         dragLayer.getViewRectRelativeToSelf(d.dragView, from);
219 
220         final Rect to = getIconRect(d);
221         final float scale = (float) to.width() / from.width();
222         mDropTargetBar.deferOnDragEnd();
223 
224         Runnable onAnimationEndRunnable = new Runnable() {
225             @Override
226             public void run() {
227                 completeDrop(d);
228                 mDropTargetBar.onDragEnd();
229                 mLauncher.exitSpringLoadedDragModeDelayed(true, 0, null);
230             }
231         };
232         dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f,
233                 DRAG_VIEW_DROP_DURATION,
234                 new DecelerateInterpolator(2),
235                 new LinearInterpolator(), onAnimationEndRunnable,
236                 DragLayer.ANIMATION_END_DISAPPEAR, null);
237     }
238 
239     @Override
prepareAccessibilityDrop()240     public void prepareAccessibilityDrop() { }
241 
completeDrop(DragObject d)242     public abstract void completeDrop(DragObject d);
243 
244     @Override
getHitRectRelativeToDragLayer(android.graphics.Rect outRect)245     public void getHitRectRelativeToDragLayer(android.graphics.Rect outRect) {
246         super.getHitRect(outRect);
247         outRect.bottom += mBottomDragPadding;
248 
249         int[] coords = new int[2];
250         mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, coords);
251         outRect.offsetTo(coords[0], coords[1]);
252     }
253 
getIconRect(DragObject dragObject)254     public Rect getIconRect(DragObject dragObject) {
255         int viewWidth = dragObject.dragView.getMeasuredWidth();
256         int viewHeight = dragObject.dragView.getMeasuredHeight();
257         int drawableWidth = mDrawable.getIntrinsicWidth();
258         int drawableHeight = mDrawable.getIntrinsicHeight();
259         DragLayer dragLayer = mLauncher.getDragLayer();
260 
261         // Find the rect to animate to (the view is center aligned)
262         Rect to = new Rect();
263         dragLayer.getViewRectRelativeToSelf(this, to);
264 
265         final int width = drawableWidth;
266         final int height = drawableHeight;
267 
268         final int left;
269         final int right;
270 
271         if (Utilities.isRtl(getResources())) {
272             right = to.right - getPaddingRight();
273             left = right - width;
274         } else {
275             left = to.left + getPaddingLeft();
276             right = left + width;
277         }
278 
279         final int top = to.top + (getMeasuredHeight() - height) / 2;
280         final int bottom = top +  height;
281 
282         to.set(left, top, right, bottom);
283 
284         // Center the destination rect about the trash icon
285         final int xOffset = (int) -(viewWidth - width) / 2;
286         final int yOffset = (int) -(viewHeight - height) / 2;
287         to.offset(xOffset, yOffset);
288 
289         return to;
290     }
291 
292     @Override
onClick(View v)293     public void onClick(View v) {
294         mLauncher.getAccessibilityDelegate().handleAccessibleDrop(this, null, null);
295     }
296 
getTextColor()297     public int getTextColor() {
298         return getTextColors().getDefaultColor();
299     }
300 }
301