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