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