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