DropTargetBar.java revision bfaa4a4edc8999bf4f86fcb70610339e53d53b49
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.TimeInterpolator;
20import android.content.Context;
21import android.util.AttributeSet;
22import android.view.View;
23import android.view.ViewDebug;
24import android.view.ViewGroup;
25import android.view.ViewPropertyAnimator;
26import android.view.accessibility.AccessibilityManager;
27import android.view.animation.AccelerateInterpolator;
28import android.widget.LinearLayout;
29
30import com.android.launcher3.dragndrop.DragController;
31import com.android.launcher3.dragndrop.DragOptions;
32
33/*
34 * The top bar containing various drop targets: Delete/App Info/Uninstall.
35 */
36public class DropTargetBar extends LinearLayout implements DragController.DragListener {
37
38    protected static final int DEFAULT_DRAG_FADE_DURATION = 175;
39    protected static final TimeInterpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator();
40
41    private final Runnable mFadeAnimationEndRunnable = new Runnable() {
42
43        @Override
44        public void run() {
45            AccessibilityManager am = (AccessibilityManager)
46                    getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
47            boolean accessibilityEnabled = am.isEnabled();
48            AlphaUpdateListener.updateVisibility(DropTargetBar.this, accessibilityEnabled);
49        }
50    };
51
52    @ViewDebug.ExportedProperty(category = "launcher")
53    protected boolean mDeferOnDragEnd;
54
55    @ViewDebug.ExportedProperty(category = "launcher")
56    protected boolean mVisible = false;
57
58    private ViewPropertyAnimator mCurrentAnimation;
59
60    public DropTargetBar(Context context, AttributeSet attrs) {
61        super(context, attrs);
62    }
63
64    public DropTargetBar(Context context, AttributeSet attrs, int defStyle) {
65        super(context, attrs, defStyle);
66    }
67
68    @Override
69    protected void onFinishInflate() {
70        super.onFinishInflate();
71
72        // Initialize with hidden state
73        setAlpha(0f);
74    }
75
76    public void setup(DragController dragController) {
77        dragController.addDragListener(this);
78        setupButtonDropTarget(this, dragController);
79    }
80
81    @Override
82    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
83        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
84
85        boolean hideText = hideTextHelper(false /* shouldUpdateText */, false /* no-op */);
86        if (hideTextHelper(true /* shouldUpdateText */, hideText)) {
87            // Text has changed, so we need to re-measure.
88            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
89        }
90    }
91
92    /**
93     * Helper method that iterates through the children and returns whether any of the visible
94     * {@link ButtonDropTarget} has truncated text.
95     *
96     * @param shouldUpdateText If True, updates the text of all children.
97     * @param hideText If True and {@param shouldUpdateText} is True, clears the text of all
98     *                 children; otherwise it sets the original text value.
99     *
100     *
101     * @return If shouldUpdateText is True, returns whether any of the children updated their text.
102     *         Else, returns whether any of the children have truncated their text.
103     */
104    private boolean hideTextHelper(boolean shouldUpdateText, boolean hideText) {
105        boolean result = false;
106        View visibleView;
107        ButtonDropTarget dropTarget;
108        for (int i = getChildCount() - 1; i >= 0; --i) {
109            if (getChildAt(i) instanceof ButtonDropTarget) {
110                visibleView = dropTarget = (ButtonDropTarget) getChildAt(i);
111            } else if (getChildAt(i) instanceof ViewGroup) {
112                // The Drop Target is wrapped in a FrameLayout.
113                visibleView = getChildAt(i);
114                dropTarget = (ButtonDropTarget) ((ViewGroup) visibleView).getChildAt(0);
115            } else {
116                // Ignore other views.
117                continue;
118            }
119
120            if (visibleView.getVisibility() == View.VISIBLE) {
121                if (shouldUpdateText) {
122                    result |= dropTarget.updateText(hideText);
123                } else if (dropTarget.isTextTruncated()) {
124                    result = true;
125                    break;
126                }
127            }
128        }
129
130        return result;
131    }
132
133    private void setupButtonDropTarget(View view, DragController dragController) {
134        if (view instanceof ButtonDropTarget) {
135            ButtonDropTarget bdt = (ButtonDropTarget) view;
136            bdt.setDropTargetBar(this);
137            dragController.addDragListener(bdt);
138            dragController.addDropTarget(bdt);
139        } else if (view instanceof ViewGroup) {
140            ViewGroup vg = (ViewGroup) view;
141            for (int i = vg.getChildCount() - 1; i >= 0; i--) {
142                setupButtonDropTarget(vg.getChildAt(i), dragController);
143            }
144        }
145    }
146
147    private void animateToVisibility(boolean isVisible) {
148        if (mVisible != isVisible) {
149            mVisible = isVisible;
150
151            // Cancel any existing animation
152            if (mCurrentAnimation != null) {
153                mCurrentAnimation.cancel();
154                mCurrentAnimation = null;
155            }
156
157            float finalAlpha = mVisible ? 1 : 0;
158            if (Float.compare(getAlpha(), finalAlpha) != 0) {
159                setVisibility(View.VISIBLE);
160                mCurrentAnimation = animate().alpha(finalAlpha)
161                        .setInterpolator(DEFAULT_INTERPOLATOR)
162                        .setDuration(DEFAULT_DRAG_FADE_DURATION)
163                        .withEndAction(mFadeAnimationEndRunnable);
164            }
165
166        }
167    }
168
169    /*
170     * DragController.DragListener implementation
171     */
172    @Override
173    public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
174        animateToVisibility(true);
175    }
176
177    /**
178     * This is called to defer hiding the delete drop target until the drop animation has completed,
179     * instead of hiding immediately when the drag has ended.
180     */
181    protected void deferOnDragEnd() {
182        mDeferOnDragEnd = true;
183    }
184
185    @Override
186    public void onDragEnd() {
187        if (!mDeferOnDragEnd) {
188            animateToVisibility(false);
189        } else {
190            mDeferOnDragEnd = false;
191        }
192    }
193}
194