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 static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_CANCEL;
20 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_REMOVE;
21 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNDO;
22 
23 import android.content.Context;
24 import android.text.TextUtils;
25 import android.util.AttributeSet;
26 import android.view.View;
27 
28 import com.android.launcher3.LauncherSettings.Favorites;
29 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
30 import com.android.launcher3.dragndrop.DragOptions;
31 import com.android.launcher3.logging.StatsLogManager;
32 import com.android.launcher3.model.ModelWriter;
33 import com.android.launcher3.model.data.FolderInfo;
34 import com.android.launcher3.model.data.ItemInfo;
35 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
36 import com.android.launcher3.model.data.WorkspaceItemInfo;
37 import com.android.launcher3.util.IntSet;
38 import com.android.launcher3.views.Snackbar;
39 
40 public class DeleteDropTarget extends ButtonDropTarget {
41 
42     private final StatsLogManager mStatsLogManager;
43 
44     private StatsLogManager.LauncherEvent mLauncherEvent;
45 
DeleteDropTarget(Context context, AttributeSet attrs)46     public DeleteDropTarget(Context context, AttributeSet attrs) {
47         this(context, attrs, 0);
48     }
49 
DeleteDropTarget(Context context, AttributeSet attrs, int defStyle)50     public DeleteDropTarget(Context context, AttributeSet attrs, int defStyle) {
51         super(context, attrs, defStyle);
52         this.mStatsLogManager = StatsLogManager.newInstance(context);
53     }
54 
55     @Override
onFinishInflate()56     protected void onFinishInflate() {
57         super.onFinishInflate();
58         setDrawable(R.drawable.ic_remove_no_shadow);
59     }
60 
61     @Override
onDragStart(DropTarget.DragObject dragObject, DragOptions options)62     public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
63         super.onDragStart(dragObject, options);
64         setTextBasedOnDragSource(dragObject.dragInfo);
65         setControlTypeBasedOnDragSource(dragObject.dragInfo);
66     }
67 
68     /**
69      * @return true for items that should have a "Remove" action in accessibility.
70      */
71     @Override
supportsAccessibilityDrop(ItemInfo info, View view)72     public boolean supportsAccessibilityDrop(ItemInfo info, View view) {
73         if (info instanceof WorkspaceItemInfo) {
74             // Support the action unless the item is in a context menu.
75             return canRemove(info);
76         }
77 
78         return (info instanceof LauncherAppWidgetInfo)
79                 || (info instanceof FolderInfo);
80     }
81 
82     @Override
getAccessibilityAction()83     public int getAccessibilityAction() {
84         return LauncherAccessibilityDelegate.REMOVE;
85     }
86 
87     @Override
supportsDrop(ItemInfo info)88     protected boolean supportsDrop(ItemInfo info) {
89         return true;
90     }
91 
92     /**
93      * Set the drop target's text to either "Remove" or "Cancel" depending on the drag item.
94      */
setTextBasedOnDragSource(ItemInfo item)95     private void setTextBasedOnDragSource(ItemInfo item) {
96         if (!TextUtils.isEmpty(mText)) {
97             mText = getResources().getString(canRemove(item)
98                     ? R.string.remove_drop_target_label
99                     : android.R.string.cancel);
100             setContentDescription(mText);
101             requestLayout();
102         }
103     }
104 
canRemove(ItemInfo item)105     private boolean canRemove(ItemInfo item) {
106         return item.id != ItemInfo.NO_ID;
107     }
108 
109     /**
110      * Set mControlType depending on the drag item.
111      */
setControlTypeBasedOnDragSource(ItemInfo item)112     private void setControlTypeBasedOnDragSource(ItemInfo item) {
113         mLauncherEvent = item.id != ItemInfo.NO_ID ? LAUNCHER_ITEM_DROPPED_ON_REMOVE
114                 : LAUNCHER_ITEM_DROPPED_ON_CANCEL;
115     }
116 
117     @Override
onDrop(DragObject d, DragOptions options)118     public void onDrop(DragObject d, DragOptions options) {
119         if (canRemove(d.dragInfo)) {
120             mLauncher.getModelWriter().prepareToUndoDelete();
121             d.dragInfo.container = NO_ID;
122         }
123         super.onDrop(d, options);
124         mStatsLogManager.logger().withInstanceId(d.logInstanceId)
125                 .log(mLauncherEvent);
126     }
127 
128     @Override
completeDrop(DragObject d)129     public void completeDrop(DragObject d) {
130         ItemInfo item = d.dragInfo;
131         if (canRemove(item)) {
132             ItemInfo pageItem = item;
133             if (item.container <= 0) {
134                 View v = mLauncher.getWorkspace().getHomescreenIconByItemId(item.container);
135                 if (v != null) {
136                     pageItem = (ItemInfo) v.getTag();
137                 }
138             }
139             IntSet pageIds = pageItem.container == Favorites.CONTAINER_DESKTOP
140                     ? IntSet.wrap(pageItem.screenId)
141                     : mLauncher.getWorkspace().getCurrentPageScreenIds();
142 
143             onAccessibilityDrop(null, item);
144             ModelWriter modelWriter = mLauncher.getModelWriter();
145             Runnable onUndoClicked = () -> {
146                 mLauncher.setPagesToBindSynchronously(pageIds);
147                 modelWriter.abortDelete();
148                 mLauncher.getStatsLogManager().logger().log(LAUNCHER_UNDO);
149             };
150             Snackbar.show(mLauncher, R.string.item_removed, R.string.undo,
151                     modelWriter::commitDelete, onUndoClicked);
152         }
153     }
154 
155     /**
156      * Removes the item from the workspace. If the view is not null, it also removes the view.
157      */
158     @Override
onAccessibilityDrop(View view, ItemInfo item)159     public void onAccessibilityDrop(View view, ItemInfo item) {
160         // Remove the item from launcher and the db, we can ignore the containerInfo in this call
161         // because we already remove the drag view from the folder (if the drag originated from
162         // a folder) in Folder.beginDrag()
163         mLauncher.removeItem(view, item, true /* deleteFromDb */);
164         mLauncher.getWorkspace().stripEmptyScreens();
165         mLauncher.getDragLayer()
166                 .announceForAccessibility(getContext().getString(R.string.item_removed));
167     }
168 }
169