Home | History | Annotate | Download | only in launcher3
      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 android.animation.Animator;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.animation.AnimatorSet;
     22 import android.animation.ObjectAnimator;
     23 import android.animation.TimeInterpolator;
     24 import android.content.Context;
     25 import android.graphics.Rect;
     26 import android.util.AttributeSet;
     27 import android.util.Log;
     28 import android.view.View;
     29 import android.view.accessibility.AccessibilityManager;
     30 import android.view.animation.AccelerateDecelerateInterpolator;
     31 import android.view.animation.AccelerateInterpolator;
     32 import android.view.animation.DecelerateInterpolator;
     33 import android.widget.FrameLayout;
     34 
     35 import com.android.launcher3.util.Thunk;
     36 
     37 /*
     38  * Ths bar will manage the transition between the QSB search bar and the delete drop
     39  * targets so that each of the individual IconDropTargets don't have to.
     40  */
     41 public class SearchDropTargetBar extends FrameLayout implements DragController.DragListener {
     42 
     43     private static final TimeInterpolator MOVE_DOWN_INTERPOLATOR = new DecelerateInterpolator(0.6f);
     44     private static final TimeInterpolator MOVE_UP_INTERPOLATOR = new DecelerateInterpolator(1.5f);
     45     private static final TimeInterpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator();
     46 
     47     /** The different states that the search bar space can be in. */
     48     public enum State {
     49         INVISIBLE             (0f, 0f, 0f),
     50         INVISIBLE_TRANSLATED  (0f, 0f, -1f),
     51         SEARCH_BAR            (1f, 0f, 0f),
     52         DROP_TARGET           (0f, 1f, 0f);
     53 
     54         private final float mSearchBarAlpha;
     55         private final float mDropTargetBarAlpha;
     56         private final float mTranslation;
     57 
     58         State(float sbAlpha, float dtbAlpha, float translation) {
     59             mSearchBarAlpha = sbAlpha;
     60             mDropTargetBarAlpha = dtbAlpha;
     61             mTranslation = translation;
     62         }
     63 
     64     }
     65 
     66     private static int DEFAULT_DRAG_FADE_DURATION = 175;
     67 
     68     private AnimatorSet mCurrentAnimation;
     69 
     70     private State mState = State.SEARCH_BAR;
     71     @Thunk View mQSB;
     72     @Thunk View mDropTargetBar;
     73     private boolean mDeferOnDragEnd = false;
     74     @Thunk boolean mAccessibilityEnabled = false;
     75 
     76     // Drop targets
     77     private ButtonDropTarget mInfoDropTarget;
     78     private ButtonDropTarget mDeleteDropTarget;
     79     private ButtonDropTarget mUninstallDropTarget;
     80 
     81     public SearchDropTargetBar(Context context, AttributeSet attrs) {
     82         this(context, attrs, 0);
     83     }
     84 
     85     public SearchDropTargetBar(Context context, AttributeSet attrs, int defStyle) {
     86         super(context, attrs, defStyle);
     87     }
     88 
     89     public void setup(Launcher launcher, DragController dragController) {
     90         dragController.addDragListener(this);
     91         dragController.setFlingToDeleteDropTarget(mDeleteDropTarget);
     92 
     93         dragController.addDragListener(mInfoDropTarget);
     94         dragController.addDragListener(mDeleteDropTarget);
     95         dragController.addDragListener(mUninstallDropTarget);
     96 
     97         dragController.addDropTarget(mInfoDropTarget);
     98         dragController.addDropTarget(mDeleteDropTarget);
     99         dragController.addDropTarget(mUninstallDropTarget);
    100 
    101         mInfoDropTarget.setLauncher(launcher);
    102         mDeleteDropTarget.setLauncher(launcher);
    103         mUninstallDropTarget.setLauncher(launcher);
    104     }
    105 
    106     @Override
    107     protected void onFinishInflate() {
    108         super.onFinishInflate();
    109 
    110         // Get the individual components
    111         mDropTargetBar = findViewById(R.id.drag_target_bar);
    112         mInfoDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.info_target_text);
    113         mDeleteDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.delete_target_text);
    114         mUninstallDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.uninstall_target_text);
    115 
    116         mInfoDropTarget.setSearchDropTargetBar(this);
    117         mDeleteDropTarget.setSearchDropTargetBar(this);
    118         mUninstallDropTarget.setSearchDropTargetBar(this);
    119 
    120         // Create the various fade animations
    121         mDropTargetBar.setAlpha(0f);
    122         AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
    123     }
    124 
    125     public void setQsbSearchBar(View qsb) {
    126         mQSB = qsb;
    127     }
    128 
    129     /**
    130      * Animates the current search bar state to a new state.  If the {@param duration} is 0, then
    131      * the state is applied immediately.
    132      */
    133     public void animateToState(State newState, int duration) {
    134         animateToState(newState, duration, null);
    135     }
    136 
    137     public void animateToState(State newState, int duration, AnimatorSet animation) {
    138         if (mState != newState) {
    139             mState = newState;
    140 
    141             // Update the accessibility state
    142             AccessibilityManager am = (AccessibilityManager)
    143                     getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
    144             mAccessibilityEnabled = am.isEnabled();
    145 
    146             if (mCurrentAnimation != null) {
    147                 mCurrentAnimation.cancel();
    148                 mCurrentAnimation = null;
    149             }
    150             mCurrentAnimation = null;
    151 
    152             if (duration > 0) {
    153                 mCurrentAnimation = new AnimatorSet();
    154                 mCurrentAnimation.setDuration(duration);
    155 
    156                 animateAlpha(mDropTargetBar, mState.mDropTargetBarAlpha, DEFAULT_INTERPOLATOR);
    157             } else {
    158                 mDropTargetBar.setAlpha(mState.mDropTargetBarAlpha);
    159                 AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
    160             }
    161 
    162             if (mQSB != null) {
    163                 boolean isVertical = ((Launcher) getContext()).getDeviceProfile()
    164                         .isVerticalBarLayout();
    165                 float translation = isVertical ? 0 : mState.mTranslation * getMeasuredHeight();
    166 
    167                 if (duration > 0) {
    168                     int translationChange = Float.compare(mQSB.getTranslationY(), translation);
    169 
    170                     animateAlpha(mQSB, mState.mSearchBarAlpha,
    171                             translationChange == 0 ? DEFAULT_INTERPOLATOR
    172                                     : (translationChange < 0 ? MOVE_DOWN_INTERPOLATOR
    173                                     : MOVE_UP_INTERPOLATOR));
    174 
    175                     if (translationChange != 0) {
    176                         mCurrentAnimation.play(
    177                                 ObjectAnimator.ofFloat(mQSB, View.TRANSLATION_Y, translation));
    178                     }
    179                 } else {
    180                     mQSB.setTranslationY(translation);
    181                     mQSB.setAlpha(mState.mSearchBarAlpha);
    182                     AlphaUpdateListener.updateVisibility(mQSB, mAccessibilityEnabled);
    183                 }
    184             }
    185 
    186             // Start the final animation
    187             if (duration > 0) {
    188                 if (animation != null) {
    189                     animation.play(mCurrentAnimation);
    190                 } else {
    191                     mCurrentAnimation.start();
    192                 }
    193             }
    194         }
    195     }
    196 
    197     private void animateAlpha(View v, float alpha, TimeInterpolator interpolator) {
    198         if (Float.compare(v.getAlpha(), alpha) != 0) {
    199             ObjectAnimator anim = ObjectAnimator.ofFloat(v, View.ALPHA, alpha);
    200             anim.setInterpolator(interpolator);
    201             anim.addListener(new ViewVisiblilyUpdateHandler(v));
    202             mCurrentAnimation.play(anim);
    203         }
    204     }
    205 
    206     /*
    207      * DragController.DragListener implementation
    208      */
    209     @Override
    210     public void onDragStart(DragSource source, Object info, int dragAction) {
    211         animateToState(State.DROP_TARGET, DEFAULT_DRAG_FADE_DURATION);
    212     }
    213 
    214     /**
    215      * This is called to defer hiding the delete drop target until the drop animation has completed,
    216      * instead of hiding immediately when the drag has ended.
    217      */
    218     public void deferOnDragEnd() {
    219         mDeferOnDragEnd = true;
    220     }
    221 
    222     @Override
    223     public void onDragEnd() {
    224         if (!mDeferOnDragEnd) {
    225             animateToState(State.SEARCH_BAR, DEFAULT_DRAG_FADE_DURATION);
    226         } else {
    227             mDeferOnDragEnd = false;
    228         }
    229     }
    230 
    231     /**
    232      * @return the bounds of the QSB search bar.
    233      */
    234     public Rect getSearchBarBounds() {
    235         if (mQSB != null) {
    236             final int[] pos = new int[2];
    237             mQSB.getLocationOnScreen(pos);
    238 
    239             final Rect rect = new Rect();
    240             rect.left = pos[0];
    241             rect.top = pos[1];
    242             rect.right = pos[0] + mQSB.getWidth();
    243             rect.bottom = pos[1] + mQSB.getHeight();
    244             return rect;
    245         } else {
    246             return null;
    247         }
    248     }
    249 
    250     public void enableAccessibleDrag(boolean enable) {
    251         if (mQSB != null) {
    252             mQSB.setVisibility(enable ? View.GONE : View.VISIBLE);
    253         }
    254         mInfoDropTarget.enableAccessibleDrag(enable);
    255         mDeleteDropTarget.enableAccessibleDrag(enable);
    256         mUninstallDropTarget.enableAccessibleDrag(enable);
    257     }
    258 
    259     private class ViewVisiblilyUpdateHandler extends AnimatorListenerAdapter {
    260         private final View mView;
    261 
    262         ViewVisiblilyUpdateHandler(View v) {
    263             mView = v;
    264         }
    265 
    266         @Override
    267         public void onAnimationStart(Animator animation) {
    268             // Ensure that the view is visible for the animation
    269             mView.setVisibility(View.VISIBLE);
    270         }
    271 
    272         @Override
    273         public void onAnimationEnd(Animator animation){
    274             AlphaUpdateListener.updateVisibility(mView, mAccessibilityEnabled);
    275         }
    276 
    277     }
    278 }
    279