• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.model;
17 
18 import static com.android.launcher3.Utilities.SHOULD_SHOW_FIRST_PAGE_WIDGET;
19 import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
20 
21 import android.util.LongSparseArray;
22 
23 import com.android.launcher3.InvariantDeviceProfile;
24 import com.android.launcher3.LauncherModel;
25 import com.android.launcher3.LauncherSettings;
26 import com.android.launcher3.config.FeatureFlags;
27 import com.android.launcher3.model.data.ItemInfo;
28 import com.android.launcher3.util.GridOccupancy;
29 import com.android.launcher3.util.IntArray;
30 import com.android.launcher3.util.IntSet;
31 
32 import java.util.ArrayList;
33 
34 import javax.inject.Inject;
35 
36 /**
37  * Utility class to help find space for new workspace items
38  */
39 public class WorkspaceItemSpaceFinder {
40 
41     private BgDataModel mDataModel;
42     private InvariantDeviceProfile mIDP;
43     private LauncherModel mModel;
44 
45     @Inject
WorkspaceItemSpaceFinder( BgDataModel dataModel, InvariantDeviceProfile idp, LauncherModel model)46     WorkspaceItemSpaceFinder(
47             BgDataModel dataModel, InvariantDeviceProfile idp, LauncherModel model) {
48         mDataModel = dataModel;
49         mIDP = idp;
50         mModel = model;
51     }
52 
53     /**
54      * Find a position on the screen for the given size or adds a new screen.
55      *
56      * @return screenId and the coordinates for the item in an int array of size 3.
57      */
findSpaceForItem( IntArray workspaceScreens, IntArray addedWorkspaceScreensFinal, int spanX, int spanY)58     public int[] findSpaceForItem(
59             IntArray workspaceScreens, IntArray addedWorkspaceScreensFinal, int spanX, int spanY) {
60         LongSparseArray<ArrayList<ItemInfo>> screenItems = new LongSparseArray<>();
61 
62         // Use sBgItemsIdMap as all the items are already loaded.
63         synchronized (mDataModel) {
64             for (ItemInfo info : mDataModel.itemsIdMap) {
65                 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
66                     ArrayList<ItemInfo> items = screenItems.get(info.screenId);
67                     if (items == null) {
68                         items = new ArrayList<>();
69                         screenItems.put(info.screenId, items);
70                     }
71                     items.add(info);
72                 }
73             }
74         }
75 
76         // Find appropriate space for the item.
77         int screenId = 0;
78         int[] coordinates = new int[2];
79         boolean found = false;
80 
81         int screenCount = workspaceScreens.size();
82         // First check the preferred screen.
83         IntSet screensToExclude = new IntSet();
84         if (FeatureFlags.QSB_ON_FIRST_SCREEN
85                 && !SHOULD_SHOW_FIRST_PAGE_WIDGET) {
86             screensToExclude.add(FIRST_SCREEN_ID);
87         }
88 
89         for (int screen = 0; screen < screenCount; screen++) {
90             screenId = workspaceScreens.get(screen);
91             if (!screensToExclude.contains(screenId) && findNextAvailableIconSpaceInScreen(
92                     screenItems.get(screenId), coordinates, spanX, spanY)) {
93                 // We found a space for it
94                 found = true;
95                 break;
96             }
97         }
98 
99         if (!found) {
100             // Still no position found. Add a new screen to the end.
101             screenId = mModel.getModelDbController().getNewScreenId();
102 
103             // Save the screen id for binding in the workspace
104             workspaceScreens.add(screenId);
105             addedWorkspaceScreensFinal.add(screenId);
106 
107             // If we still can't find an empty space, then God help us all!!!
108             if (!findNextAvailableIconSpaceInScreen(
109                     screenItems.get(screenId), coordinates, spanX, spanY)) {
110                 throw new RuntimeException("Can't find space to add the item");
111             }
112         }
113         return new int[]{screenId, coordinates[0], coordinates[1]};
114     }
115 
findNextAvailableIconSpaceInScreen( ArrayList<ItemInfo> occupiedPos, int[] xy, int spanX, int spanY)116     private boolean findNextAvailableIconSpaceInScreen(
117             ArrayList<ItemInfo> occupiedPos, int[] xy, int spanX, int spanY) {
118         GridOccupancy occupied = new GridOccupancy(mIDP.numColumns, mIDP.numRows);
119         if (occupiedPos != null) {
120             for (ItemInfo r : occupiedPos) {
121                 occupied.markCells(r, true);
122             }
123         }
124         return occupied.findVacantCell(xy, spanX, spanY);
125     }
126 }
127