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
17package com.android.launcher3;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ObjectAnimator;
22import android.content.Context;
23import android.graphics.Rect;
24import android.graphics.drawable.Drawable;
25import android.util.AttributeSet;
26import android.view.View;
27import android.view.animation.AccelerateInterpolator;
28import android.widget.FrameLayout;
29
30/*
31 * Ths bar will manage the transition between the QSB search bar and the delete drop
32 * targets so that each of the individual IconDropTargets don't have to.
33 */
34public class SearchDropTargetBar extends FrameLayout implements DragController.DragListener {
35
36    private static final int sTransitionInDuration = 200;
37    private static final int sTransitionOutDuration = 175;
38
39    private ObjectAnimator mDropTargetBarAnim;
40    private ObjectAnimator mQSBSearchBarAnim;
41    private static final AccelerateInterpolator sAccelerateInterpolator =
42            new AccelerateInterpolator();
43
44    private boolean mIsSearchBarHidden;
45    private View mQSBSearchBar;
46    private View mDropTargetBar;
47    private ButtonDropTarget mInfoDropTarget;
48    private ButtonDropTarget mDeleteDropTarget;
49    private int mBarHeight;
50    private boolean mDeferOnDragEnd = false;
51
52    private Drawable mPreviousBackground;
53    private boolean mEnableDropDownDropTargets;
54
55    public SearchDropTargetBar(Context context, AttributeSet attrs) {
56        this(context, attrs, 0);
57    }
58
59    public SearchDropTargetBar(Context context, AttributeSet attrs, int defStyle) {
60        super(context, attrs, defStyle);
61    }
62
63    public void setup(Launcher launcher, DragController dragController) {
64        dragController.addDragListener(this);
65        dragController.addDragListener(mInfoDropTarget);
66        dragController.addDragListener(mDeleteDropTarget);
67        dragController.addDropTarget(mInfoDropTarget);
68        dragController.addDropTarget(mDeleteDropTarget);
69        dragController.setFlingToDeleteDropTarget(mDeleteDropTarget);
70        mInfoDropTarget.setLauncher(launcher);
71        mDeleteDropTarget.setLauncher(launcher);
72        mQSBSearchBar = launcher.getQsbBar();
73        if (mEnableDropDownDropTargets) {
74            mQSBSearchBarAnim = LauncherAnimUtils.ofFloat(mQSBSearchBar, "translationY", 0,
75                    -mBarHeight);
76        } else {
77            mQSBSearchBarAnim = LauncherAnimUtils.ofFloat(mQSBSearchBar, "alpha", 1f, 0f);
78        }
79        setupAnimation(mQSBSearchBarAnim, mQSBSearchBar);
80    }
81
82    private void prepareStartAnimation(View v) {
83        // Enable the hw layers before the animation starts (will be disabled in the onAnimationEnd
84        // callback below)
85        v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
86    }
87
88    private void setupAnimation(ObjectAnimator anim, final View v) {
89        anim.setInterpolator(sAccelerateInterpolator);
90        anim.setDuration(sTransitionInDuration);
91        anim.addListener(new AnimatorListenerAdapter() {
92            @Override
93            public void onAnimationEnd(Animator animation) {
94                v.setLayerType(View.LAYER_TYPE_NONE, null);
95            }
96        });
97    }
98
99    @Override
100    protected void onFinishInflate() {
101        super.onFinishInflate();
102
103        // Get the individual components
104        mDropTargetBar = findViewById(R.id.drag_target_bar);
105        mInfoDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.info_target_text);
106        mDeleteDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.delete_target_text);
107
108        mInfoDropTarget.setSearchDropTargetBar(this);
109        mDeleteDropTarget.setSearchDropTargetBar(this);
110
111        mEnableDropDownDropTargets =
112            getResources().getBoolean(R.bool.config_useDropTargetDownTransition);
113
114        // Create the various fade animations
115        if (mEnableDropDownDropTargets) {
116            LauncherAppState app = LauncherAppState.getInstance();
117            DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
118            mBarHeight = grid.searchBarSpaceHeightPx;
119            mDropTargetBar.setTranslationY(-mBarHeight);
120            mDropTargetBarAnim = LauncherAnimUtils.ofFloat(mDropTargetBar, "translationY",
121                    -mBarHeight, 0f);
122
123        } else {
124            mDropTargetBar.setAlpha(0f);
125            mDropTargetBarAnim = LauncherAnimUtils.ofFloat(mDropTargetBar, "alpha", 0f, 1f);
126        }
127        setupAnimation(mDropTargetBarAnim, mDropTargetBar);
128    }
129
130    public void finishAnimations() {
131        prepareStartAnimation(mDropTargetBar);
132        mDropTargetBarAnim.reverse();
133        prepareStartAnimation(mQSBSearchBar);
134        mQSBSearchBarAnim.reverse();
135    }
136
137    /*
138     * Shows and hides the search bar.
139     */
140    public void showSearchBar(boolean animated) {
141        boolean needToCancelOngoingAnimation = mQSBSearchBarAnim.isRunning() && !animated;
142        if (!mIsSearchBarHidden && !needToCancelOngoingAnimation) return;
143        if (animated) {
144            prepareStartAnimation(mQSBSearchBar);
145            mQSBSearchBarAnim.reverse();
146        } else {
147            mQSBSearchBarAnim.cancel();
148            if (mEnableDropDownDropTargets) {
149                mQSBSearchBar.setTranslationY(0);
150            } else {
151                mQSBSearchBar.setAlpha(1f);
152            }
153        }
154        mIsSearchBarHidden = false;
155    }
156    public void hideSearchBar(boolean animated) {
157        boolean needToCancelOngoingAnimation = mQSBSearchBarAnim.isRunning() && !animated;
158        if (mIsSearchBarHidden && !needToCancelOngoingAnimation) return;
159        if (animated) {
160            prepareStartAnimation(mQSBSearchBar);
161            mQSBSearchBarAnim.start();
162        } else {
163            mQSBSearchBarAnim.cancel();
164            if (mEnableDropDownDropTargets) {
165                mQSBSearchBar.setTranslationY(-mBarHeight);
166            } else {
167                mQSBSearchBar.setAlpha(0f);
168            }
169        }
170        mIsSearchBarHidden = true;
171    }
172
173    /*
174     * Gets various transition durations.
175     */
176    public int getTransitionInDuration() {
177        return sTransitionInDuration;
178    }
179    public int getTransitionOutDuration() {
180        return sTransitionOutDuration;
181    }
182
183    /*
184     * DragController.DragListener implementation
185     */
186    @Override
187    public void onDragStart(DragSource source, Object info, int dragAction) {
188        // Animate out the QSB search bar, and animate in the drop target bar
189        prepareStartAnimation(mDropTargetBar);
190        mDropTargetBarAnim.start();
191        if (!mIsSearchBarHidden) {
192            prepareStartAnimation(mQSBSearchBar);
193            mQSBSearchBarAnim.start();
194        }
195    }
196
197    public void deferOnDragEnd() {
198        mDeferOnDragEnd = true;
199    }
200
201    @Override
202    public void onDragEnd() {
203        if (!mDeferOnDragEnd) {
204            // Restore the QSB search bar, and animate out the drop target bar
205            prepareStartAnimation(mDropTargetBar);
206            mDropTargetBarAnim.reverse();
207            if (!mIsSearchBarHidden) {
208                prepareStartAnimation(mQSBSearchBar);
209                mQSBSearchBarAnim.reverse();
210            }
211        } else {
212            mDeferOnDragEnd = false;
213        }
214    }
215
216    public void onSearchPackagesChanged(boolean searchVisible, boolean voiceVisible) {
217        if (mQSBSearchBar != null) {
218            Drawable bg = mQSBSearchBar.getBackground();
219            if (bg != null && (!searchVisible && !voiceVisible)) {
220                // Save the background and disable it
221                mPreviousBackground = bg;
222                mQSBSearchBar.setBackgroundResource(0);
223            } else if (mPreviousBackground != null && (searchVisible || voiceVisible)) {
224                // Restore the background
225                mQSBSearchBar.setBackground(mPreviousBackground);
226            }
227        }
228    }
229
230    public Rect getSearchBarBounds() {
231        if (mQSBSearchBar != null) {
232            final int[] pos = new int[2];
233            mQSBSearchBar.getLocationOnScreen(pos);
234
235            final Rect rect = new Rect();
236            rect.left = pos[0];
237            rect.top = pos[1];
238            rect.right = pos[0] + mQSBSearchBar.getWidth();
239            rect.bottom = pos[1] + mQSBSearchBar.getHeight();
240            return rect;
241        } else {
242            return null;
243        }
244    }
245}
246