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