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.content.res.Configuration;
     28 import android.graphics.ColorMatrix;
     29 import android.graphics.ColorMatrixColorFilter;
     30 import android.graphics.PointF;
     31 import android.graphics.Rect;
     32 import android.graphics.drawable.Drawable;
     33 import android.os.Build;
     34 import android.util.AttributeSet;
     35 import android.view.View;
     36 import android.view.View.OnClickListener;
     37 import android.view.ViewGroup;
     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 (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_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.isLmpOrAbove()) {
    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     }
    128 
    129     @Override
    130     public void onDragOver(DragObject d) {
    131         // Do nothing
    132     }
    133 
    134 	protected void resetHoverColor() {
    135         if (Utilities.isLmpOrAbove()) {
    136             animateTextColor(mOriginalTextColor.getDefaultColor());
    137         } else {
    138             mDrawable.setColorFilter(null);
    139             setTextColor(mOriginalTextColor);
    140         }
    141     }
    142 
    143     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    144     private void animateTextColor(int targetColor) {
    145         if (mCurrentColorAnim != null) {
    146             mCurrentColorAnim.cancel();
    147         }
    148 
    149         mCurrentColorAnim = new AnimatorSet();
    150         mCurrentColorAnim.setDuration(DragView.COLOR_CHANGE_DURATION);
    151 
    152         if (mSrcFilter == null) {
    153             mSrcFilter = new ColorMatrix();
    154             mDstFilter = new ColorMatrix();
    155             mCurrentFilter = new ColorMatrix();
    156         }
    157 
    158         DragView.setColorScale(getTextColor(), mSrcFilter);
    159         DragView.setColorScale(targetColor, mDstFilter);
    160         ValueAnimator anim1 = ValueAnimator.ofObject(
    161                 new FloatArrayEvaluator(mCurrentFilter.getArray()),
    162                 mSrcFilter.getArray(), mDstFilter.getArray());
    163         anim1.addUpdateListener(new AnimatorUpdateListener() {
    164 
    165             @Override
    166             public void onAnimationUpdate(ValueAnimator animation) {
    167                 mDrawable.setColorFilter(new ColorMatrixColorFilter(mCurrentFilter));
    168                 invalidate();
    169             }
    170         });
    171 
    172         mCurrentColorAnim.play(anim1);
    173         mCurrentColorAnim.play(ObjectAnimator.ofArgb(this, "textColor", targetColor));
    174         mCurrentColorAnim.start();
    175     }
    176 
    177     @Override
    178     public final void onDragExit(DragObject d) {
    179         if (!d.dragComplete) {
    180             d.dragView.setColor(0);
    181             resetHoverColor();
    182         } else {
    183             // Restore the hover color
    184             d.dragView.setColor(mHoverColor);
    185         }
    186     }
    187 
    188 	@Override
    189     public final void onDragStart(DragSource source, Object info, int dragAction) {
    190         mActive = supportsDrop(source, info);
    191         mDrawable.setColorFilter(null);
    192         if (mCurrentColorAnim != null) {
    193             mCurrentColorAnim.cancel();
    194             mCurrentColorAnim = null;
    195         }
    196         setTextColor(mOriginalTextColor);
    197         ((ViewGroup) getParent()).setVisibility(mActive ? View.VISIBLE : View.GONE);
    198     }
    199 
    200     @Override
    201     public final boolean acceptDrop(DragObject dragObject) {
    202         return supportsDrop(dragObject.dragSource, dragObject.dragInfo);
    203     }
    204 
    205     protected abstract boolean supportsDrop(DragSource source, Object info);
    206 
    207     @Override
    208     public boolean isDropEnabled() {
    209         return mActive;
    210     }
    211 
    212     @Override
    213     public void onDragEnd() {
    214         mActive = false;
    215     }
    216 
    217     /**
    218      * On drop animate the dropView to the icon.
    219      */
    220     @Override
    221     public void onDrop(final DragObject d) {
    222         final DragLayer dragLayer = mLauncher.getDragLayer();
    223         final Rect from = new Rect();
    224         dragLayer.getViewRectRelativeToSelf(d.dragView, from);
    225 
    226         int width = mDrawable.getIntrinsicWidth();
    227         int height = mDrawable.getIntrinsicHeight();
    228         final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
    229                 width, height);
    230         final float scale = (float) to.width() / from.width();
    231         mSearchDropTargetBar.deferOnDragEnd();
    232 
    233         Runnable onAnimationEndRunnable = new Runnable() {
    234             @Override
    235             public void run() {
    236                 completeDrop(d);
    237                 mSearchDropTargetBar.onDragEnd();
    238                 mLauncher.exitSpringLoadedDragModeDelayed(true, 0, null);
    239             }
    240         };
    241         dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f,
    242                 DRAG_VIEW_DROP_DURATION, new DecelerateInterpolator(2),
    243                 new LinearInterpolator(), onAnimationEndRunnable,
    244                 DragLayer.ANIMATION_END_DISAPPEAR, null);
    245     }
    246 
    247     @Override
    248     public void prepareAccessibilityDrop() { }
    249 
    250     @Thunk abstract void completeDrop(DragObject d);
    251 
    252     @Override
    253     public void getHitRectRelativeToDragLayer(android.graphics.Rect outRect) {
    254         super.getHitRect(outRect);
    255         outRect.bottom += mBottomDragPadding;
    256 
    257         int[] coords = new int[2];
    258         mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, coords);
    259         outRect.offsetTo(coords[0], coords[1]);
    260     }
    261 
    262     protected Rect getIconRect(int viewWidth, int viewHeight, int drawableWidth, int drawableHeight) {
    263         DragLayer dragLayer = mLauncher.getDragLayer();
    264 
    265         // Find the rect to animate to (the view is center aligned)
    266         Rect to = new Rect();
    267         dragLayer.getViewRectRelativeToSelf(this, to);
    268 
    269         final int width = drawableWidth;
    270         final int height = drawableHeight;
    271 
    272         final int left;
    273         final int right;
    274 
    275         if (Utilities.isRtl(getResources())) {
    276             right = to.right - getPaddingRight();
    277             left = right - width;
    278         } else {
    279             left = to.left + getPaddingLeft();
    280             right = left + width;
    281         }
    282 
    283         final int top = to.top + (getMeasuredHeight() - height) / 2;
    284         final int bottom = top +  height;
    285 
    286         to.set(left, top, right, bottom);
    287 
    288         // Center the destination rect about the trash icon
    289         final int xOffset = (int) -(viewWidth - width) / 2;
    290         final int yOffset = (int) -(viewHeight - height) / 2;
    291         to.offset(xOffset, yOffset);
    292 
    293         return to;
    294     }
    295 
    296     @Override
    297     public void getLocationInDragLayer(int[] loc) {
    298         mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
    299     }
    300 
    301     public void enableAccessibleDrag(boolean enable) {
    302         setOnClickListener(enable ? this : null);
    303     }
    304 
    305     protected String getAccessibilityDropConfirmation() {
    306         return null;
    307     }
    308 
    309     @Override
    310     public void onClick(View v) {
    311         LauncherAppState.getInstance().getAccessibilityDelegate()
    312             .handleAccessibleDrop(this, null, getAccessibilityDropConfirmation());
    313     }
    314 
    315     public int getTextColor() {
    316         return getTextColors().getDefaultColor();
    317     }
    318 }
    319