ShortcutsItemView.java revision ee82b035dbaf0cba73ffd856ddc97d092728dc34
1/*
2 * Copyright (C) 2017 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.shortcuts;
18
19import android.animation.Animator;
20import android.animation.AnimatorSet;
21import android.content.Context;
22import android.graphics.Point;
23import android.util.AttributeSet;
24import android.view.MotionEvent;
25import android.view.View;
26import android.widget.LinearLayout;
27
28import com.android.launcher3.AbstractFloatingView;
29import com.android.launcher3.ItemInfo;
30import com.android.launcher3.Launcher;
31import com.android.launcher3.LauncherAnimUtils;
32import com.android.launcher3.R;
33import com.android.launcher3.anim.PropertyListBuilder;
34import com.android.launcher3.dragndrop.DragOptions;
35import com.android.launcher3.dragndrop.DragView;
36import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
37import com.android.launcher3.popup.PopupContainerWithArrow;
38import com.android.launcher3.popup.PopupItemView;
39import com.android.launcher3.popup.PopupPopulator;
40import com.android.launcher3.popup.SystemShortcut;
41import com.android.launcher3.userevent.nano.LauncherLogProto;
42
43import java.util.ArrayList;
44import java.util.Collections;
45import java.util.List;
46
47/**
48 * A {@link PopupItemView} that contains all of the {@link DeepShortcutView}s for an app,
49 * as well as the system shortcuts such as Widgets and App Info.
50 */
51public class ShortcutsItemView extends PopupItemView implements View.OnLongClickListener,
52        View.OnTouchListener, LogContainerProvider {
53
54    private Launcher mLauncher;
55    private LinearLayout mShortcutsLayout;
56    private LinearLayout mSystemShortcutIcons;
57    private final Point mIconShift = new Point();
58    private final Point mIconLastTouchPos = new Point();
59    private final List<DeepShortcutView> mDeepShortcutViews = new ArrayList<>();
60    private final List<View> mSystemShortcutViews = new ArrayList<>();
61
62    public ShortcutsItemView(Context context) {
63        this(context, null, 0);
64    }
65
66    public ShortcutsItemView(Context context, AttributeSet attrs) {
67        this(context, attrs, 0);
68    }
69
70    public ShortcutsItemView(Context context, AttributeSet attrs, int defStyle) {
71        super(context, attrs, defStyle);
72
73        mLauncher = Launcher.getLauncher(context);
74    }
75
76    @Override
77    protected void onFinishInflate() {
78        super.onFinishInflate();
79        mShortcutsLayout = findViewById(R.id.deep_shortcuts);
80    }
81
82    @Override
83    public boolean onTouch(View v, MotionEvent ev) {
84        // Touched a shortcut, update where it was touched so we can drag from there on long click.
85        switch (ev.getAction()) {
86            case MotionEvent.ACTION_DOWN:
87            case MotionEvent.ACTION_MOVE:
88                mIconLastTouchPos.set((int) ev.getX(), (int) ev.getY());
89                break;
90        }
91        return false;
92    }
93
94    @Override
95    public boolean onLongClick(View v) {
96        // Return early if this is not initiated from a touch or not the correct view
97        if (!v.isInTouchMode() || !(v.getParent() instanceof DeepShortcutView)) return false;
98        // Return early if global dragging is not enabled
99        if (!mLauncher.isDraggingEnabled()) return false;
100        // Return early if an item is already being dragged (e.g. when long-pressing two shortcuts)
101        if (mLauncher.getDragController().isDragging()) return false;
102
103        // Long clicked on a shortcut.
104        DeepShortcutView sv = (DeepShortcutView) v.getParent();
105        sv.setWillDrawIcon(false);
106
107        // Move the icon to align with the center-top of the touch point
108        mIconShift.x = mIconLastTouchPos.x - sv.getIconCenter().x;
109        mIconShift.y = mIconLastTouchPos.y - mLauncher.getDeviceProfile().iconSizePx;
110
111        DragView dv = mLauncher.getWorkspace().beginDragShared(sv.getBubbleText(),
112                (PopupContainerWithArrow) getParent(), sv.getFinalInfo(),
113                new ShortcutDragPreviewProvider(sv.getIconView(), mIconShift), new DragOptions());
114        dv.animateShift(-mIconShift.x, -mIconShift.y);
115
116        // TODO: support dragging from within folder without having to close it
117        AbstractFloatingView.closeOpenContainer(mLauncher, AbstractFloatingView.TYPE_FOLDER);
118        return false;
119    }
120
121    public void addShortcutView(View shortcutView, PopupPopulator.Item shortcutType) {
122        if (shortcutType == PopupPopulator.Item.SHORTCUT) {
123            mDeepShortcutViews.add((DeepShortcutView) shortcutView);
124        } else {
125            mSystemShortcutViews.add(shortcutView);
126        }
127        if (shortcutType == PopupPopulator.Item.SYSTEM_SHORTCUT_ICON) {
128            // System shortcut icons are added to a header that is separate from the full shortcuts.
129            if (mSystemShortcutIcons == null) {
130                mSystemShortcutIcons = (LinearLayout) mLauncher.getLayoutInflater().inflate(
131                        R.layout.system_shortcut_icons, mShortcutsLayout, false);
132                mShortcutsLayout.addView(mSystemShortcutIcons, 0);
133            }
134            mSystemShortcutIcons.addView(shortcutView);
135        } else {
136            if (mShortcutsLayout.getChildCount() > 0) {
137                View prevChild = mShortcutsLayout.getChildAt(mShortcutsLayout.getChildCount() - 1);
138                if (prevChild instanceof DeepShortcutView) {
139                    prevChild.findViewById(R.id.divider).setVisibility(VISIBLE);
140                }
141            }
142            mShortcutsLayout.addView(shortcutView);
143        }
144    }
145
146    public List<DeepShortcutView> getDeepShortcutViews(boolean reverseOrder) {
147        if (reverseOrder) {
148            Collections.reverse(mDeepShortcutViews);
149        }
150        return mDeepShortcutViews;
151    }
152
153    public List<View> getSystemShortcutViews(boolean reverseOrder) {
154        // Always reverse system shortcut icons (in the header)
155        // so they are in priority order from right to left.
156        if (reverseOrder || mSystemShortcutIcons != null) {
157            Collections.reverse(mSystemShortcutViews);
158        }
159        return mSystemShortcutViews;
160    }
161
162    /**
163     * Sets the onClickListener on widgets system shortcut child, and updates alpha to 1.
164     * @return whether widgets is enabled, i.e. the onClickListener is not null.
165     */
166    public boolean enableWidgets(ItemInfo itemInfo) {
167        for (View systemShortcut : mSystemShortcutViews) {
168            if (systemShortcut.getTag() instanceof SystemShortcut.Widgets) {
169                View.OnClickListener onClickListener =
170                        ((SystemShortcut.Widgets) systemShortcut.getTag()).getOnClickListener(
171                                mLauncher, itemInfo);
172                if (onClickListener != null) {
173                    systemShortcut.setAlpha(1f);
174                    systemShortcut.setOnClickListener(onClickListener);
175                    return true;
176                }
177                return false;
178            }
179        }
180        return false;
181    }
182
183    @Override
184    public Animator createOpenAnimation(boolean isContainerAboveIcon, boolean pivotLeft) {
185        AnimatorSet openAnimation = LauncherAnimUtils.createAnimatorSet();
186        openAnimation.play(super.createOpenAnimation(isContainerAboveIcon, pivotLeft));
187        for (int i = 0; i < mShortcutsLayout.getChildCount(); i++) {
188            if (!(mShortcutsLayout.getChildAt(i) instanceof DeepShortcutView)) {
189                continue;
190            }
191            DeepShortcutView shortcutView = ((DeepShortcutView) mShortcutsLayout.getChildAt(i));
192            View deepShortcutIcon = shortcutView.getIconView();
193            deepShortcutIcon.setScaleX(0);
194            deepShortcutIcon.setScaleY(0);
195            openAnimation.play(LauncherAnimUtils.ofPropertyValuesHolder(
196                    deepShortcutIcon, new PropertyListBuilder().scale(1).build()));
197        }
198        return openAnimation;
199    }
200
201    @Override
202    public Animator createCloseAnimation(boolean isContainerAboveIcon, boolean pivotLeft,
203            long duration) {
204        AnimatorSet closeAnimation = LauncherAnimUtils.createAnimatorSet();
205        closeAnimation.play(super.createCloseAnimation(isContainerAboveIcon, pivotLeft, duration));
206        for (int i = 0; i < mShortcutsLayout.getChildCount(); i++) {
207            if (!(mShortcutsLayout.getChildAt(i) instanceof DeepShortcutView)) {
208                continue;
209            }
210            DeepShortcutView shortcutView = ((DeepShortcutView) mShortcutsLayout.getChildAt(i));
211            View deepShortcutIcon = shortcutView.getIconView();
212            deepShortcutIcon.setScaleX(1);
213            deepShortcutIcon.setScaleY(1);
214            closeAnimation.play(LauncherAnimUtils.ofPropertyValuesHolder(
215                    deepShortcutIcon, new PropertyListBuilder().scale(0).build()));
216        }
217        return closeAnimation;
218    }
219
220    @Override
221    public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
222            LauncherLogProto.Target targetParent) {
223        target.itemType = LauncherLogProto.ItemType.DEEPSHORTCUT;
224        target.rank = info.rank;
225        targetParent.containerType = LauncherLogProto.ContainerType.DEEPSHORTCUTS;
226    }
227}
228