• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 package com.android.launcher3.folder;
17 
18 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_CONVERTED_TO_ICON;
19 
20 import android.content.Context;
21 import android.view.MotionEvent;
22 import android.view.View;
23 
24 import androidx.annotation.Nullable;
25 
26 import com.android.launcher3.CellLayout;
27 import com.android.launcher3.DragSource;
28 import com.android.launcher3.DropTarget;
29 import com.android.launcher3.Launcher;
30 import com.android.launcher3.LauncherAppState;
31 import com.android.launcher3.celllayout.CellPosMapper;
32 import com.android.launcher3.dragndrop.DragOptions;
33 import com.android.launcher3.logging.InstanceId;
34 import com.android.launcher3.logging.StatsLogManager.StatsLogger;
35 import com.android.launcher3.model.ModelWriter;
36 import com.android.launcher3.model.data.FolderInfo;
37 import com.android.launcher3.model.data.WorkspaceItemInfo;
38 import com.android.launcher3.views.ActivityContext;
39 import com.android.launcher3.views.BaseDragLayer;
40 
41 import java.util.Optional;
42 import java.util.function.Consumer;
43 
44 /**
45  * Wrapper around Launcher methods to allow folders in non-launcher context
46  */
47 public class LauncherDelegate {
48 
49     private final Launcher mLauncher;
50 
LauncherDelegate(Launcher launcher)51     private LauncherDelegate(Launcher launcher) {
52         mLauncher = launcher;
53     }
54 
init(Folder folder, FolderIcon icon)55     void init(Folder folder, FolderIcon icon) {
56         folder.setDragController(mLauncher.getDragController());
57         icon.setOnFocusChangeListener(mLauncher.getFocusHandler());
58     }
59 
isDraggingEnabled()60     boolean isDraggingEnabled() {
61         return mLauncher.isDraggingEnabled();
62     }
63 
beginDragShared(View child, DragSource source, DragOptions options)64     void beginDragShared(View child, DragSource source, DragOptions options) {
65         mLauncher.getWorkspace().beginDragShared(child, source, options);
66     }
67 
getModelWriter()68     ModelWriter getModelWriter() {
69         return mLauncher.getModelWriter();
70     }
71 
forEachVisibleWorkspacePage(Consumer<View> callback)72     void forEachVisibleWorkspacePage(Consumer<View> callback) {
73         mLauncher.getWorkspace().forEachVisiblePage(callback);
74     }
75 
76     @Nullable
getLauncher()77     Launcher getLauncher() {
78         return mLauncher;
79     }
80 
replaceFolderWithFinalItem(Folder folder)81     boolean replaceFolderWithFinalItem(Folder folder) {
82         // Add the last remaining child to the workspace in place of the folder
83         Runnable onCompleteRunnable = new Runnable() {
84             @Override
85             public void run() {
86                 int itemCount = folder.getItemCount();
87                 FolderInfo info = folder.mInfo;
88                 if (itemCount <= 1) {
89                     View newIcon = null;
90                     WorkspaceItemInfo finalItem = null;
91 
92                     if (itemCount == 1) {
93                         // Move the item from the folder to the workspace, in the position of the
94                         // folder
95                         CellLayout cellLayout = mLauncher.getCellLayout(info.container,
96                                 mLauncher.getCellPosMapper().mapModelToPresenter(info).screenId);
97                         finalItem =  info.contents.remove(0);
98                         newIcon = mLauncher.createShortcut(cellLayout, finalItem);
99                         mLauncher.getModelWriter().addOrMoveItemInDatabase(finalItem,
100                                 info.container, info.screenId, info.cellX, info.cellY);
101                     }
102 
103                     // Remove the folder
104                     mLauncher.removeItem(folder.mFolderIcon, info, true /* deleteFromDb */,
105                             "folder removed because there's only 1 item in it");
106                     if (folder.mFolderIcon instanceof DropTarget) {
107                         folder.mDragController.removeDropTarget((DropTarget) folder.mFolderIcon);
108                     }
109 
110                     if (newIcon != null) {
111                         // We add the child after removing the folder to prevent both from existing
112                         // at the same time in the CellLayout.  We need to add the new item with
113                         // addInScreenFromBind() to ensure that hotseat items are placed correctly.
114                         mLauncher.getWorkspace().addInScreenFromBind(newIcon, info);
115 
116                         // Focus the newly created child
117                         newIcon.requestFocus();
118                     }
119                     if (finalItem != null) {
120                         StatsLogger logger = mLauncher.getStatsLogManager().logger()
121                                 .withItemInfo(finalItem);
122                         ((Optional<InstanceId>) folder.mDragController.getLogInstanceId())
123                                 .map(logger::withInstanceId)
124                                 .orElse(logger)
125                                 .log(LAUNCHER_FOLDER_CONVERTED_TO_ICON);
126                     }
127                 }
128             }
129         };
130         View finalChild = folder.mContent.getLastItem();
131         if (finalChild != null) {
132             folder.mFolderIcon.performDestroyAnimation(onCompleteRunnable);
133         } else {
134             onCompleteRunnable.run();
135         }
136         return true;
137     }
138 
139 
interceptOutsideTouch(MotionEvent ev, BaseDragLayer dl, Folder folder)140     boolean interceptOutsideTouch(MotionEvent ev, BaseDragLayer dl, Folder folder) {
141         if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
142             // Do not close the container if in drag and drop.
143             if (!dl.isEventOverView(mLauncher.getDropTargetBar(), ev)) {
144                 return true;
145             }
146         } else {
147             // TODO: add ww log if need to gather tap outside to close folder
148             folder.close(true);
149             return true;
150         }
151         return false;
152     }
153 
154     private static class FallbackDelegate extends LauncherDelegate {
155 
156         private final ActivityContext mContext;
157         private ModelWriter mWriter;
158 
FallbackDelegate(ActivityContext context)159         FallbackDelegate(ActivityContext context) {
160             super(null);
161             mContext = context;
162         }
163 
164         @Override
init(Folder folder, FolderIcon icon)165         void init(Folder folder, FolderIcon icon) {
166             folder.setDragController(mContext.getDragController());
167         }
168 
169         @Override
isDraggingEnabled()170         boolean isDraggingEnabled() {
171             return false;
172         }
173 
174         @Override
beginDragShared(View child, DragSource source, DragOptions options)175         void beginDragShared(View child, DragSource source, DragOptions options) { }
176 
177         @Override
getModelWriter()178         ModelWriter getModelWriter() {
179             if (mWriter == null) {
180                 mWriter = LauncherAppState.getInstance((Context) mContext).getModel()
181                         .getWriter(false, false, CellPosMapper.DEFAULT, null);
182             }
183             return mWriter;
184         }
185 
186         @Override
forEachVisibleWorkspacePage(Consumer<View> callback)187         void forEachVisibleWorkspacePage(Consumer<View> callback) { }
188 
189         @Override
getLauncher()190         Launcher getLauncher() {
191             return null;
192         }
193 
194         @Override
replaceFolderWithFinalItem(Folder folder)195         boolean replaceFolderWithFinalItem(Folder folder) {
196             return false;
197         }
198 
199         @Override
interceptOutsideTouch(MotionEvent ev, BaseDragLayer dl, Folder folder)200         boolean interceptOutsideTouch(MotionEvent ev, BaseDragLayer dl, Folder folder) {
201             folder.close(true);
202             return true;
203         }
204     }
205 
from(ActivityContext context)206     static LauncherDelegate from(ActivityContext context) {
207         return context instanceof Launcher
208                 ? new LauncherDelegate((Launcher) context)
209                 : new FallbackDelegate(context);
210     }
211 }
212