Loading src/com/android/launcher3/Launcher.java +6 −16 Original line number Original line Diff line number Diff line Loading @@ -877,11 +877,11 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche if (dropLayout == null) { if (dropLayout == null) { // it's possible that the add screen was removed because it was // it's possible that the add screen was removed because it was // empty and a re-bind occurred // empty and a re-bind occurred mWorkspace.addExtraEmptyScreen(); mWorkspace.addExtraEmptyScreens(); return mWorkspace.commitExtraEmptyScreen(); IntSet emptyPagesAdded = mWorkspace.commitExtraEmptyScreens(); } else { return emptyPagesAdded.isEmpty() ? -1 : emptyPagesAdded.getArray().get(0); return screenId; } } return screenId; } } @Thunk @Thunk Loading Loading @@ -2181,7 +2181,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche orderedScreenIds.add(firstScreenPosition, Workspace.FIRST_SCREEN_ID); orderedScreenIds.add(firstScreenPosition, Workspace.FIRST_SCREEN_ID); } else if (!FeatureFlags.QSB_ON_FIRST_SCREEN && orderedScreenIds.isEmpty()) { } else if (!FeatureFlags.QSB_ON_FIRST_SCREEN && orderedScreenIds.isEmpty()) { // If there are no screens, we need to have an empty screen // If there are no screens, we need to have an empty screen mWorkspace.addExtraEmptyScreen(); mWorkspace.addExtraEmptyScreens(); } } bindAddScreens(orderedScreenIds); bindAddScreens(orderedScreenIds); Loading @@ -2196,17 +2196,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche // Some empty pages might have been removed while the phone was in a single panel // Some empty pages might have been removed while the phone was in a single panel // mode, so we want to add those empty pages back. // mode, so we want to add those empty pages back. IntSet screenIds = IntSet.wrap(orderedScreenIds); IntSet screenIds = IntSet.wrap(orderedScreenIds); for (int i = 0; i < orderedScreenIds.size(); i++) { orderedScreenIds.forEach(screenId -> screenIds.add(mWorkspace.getPagePair(screenId))); int screenId = orderedScreenIds.get(i); // Don't add the page pair if the page is the last one and if the pair is on the // right, because that would cause a bug when adding new pages. // TODO: (b/196376162) remove this when the new screen id logic is fixed for two // panel in Workspace::commitExtraEmptyScreen if (i == orderedScreenIds.size() - 1 && screenId % 2 == 0) { continue; } screenIds.add(mWorkspace.getPagePair(screenId)); } orderedScreenIds = screenIds.getArray(); orderedScreenIds = screenIds.getArray(); } } Loading src/com/android/launcher3/Workspace.java +171 −48 Original line number Original line Diff line number Diff line Loading @@ -125,6 +125,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Collections; import java.util.Iterator; import java.util.Iterator; import java.util.List; import java.util.List; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Collectors; Loading Loading @@ -644,18 +645,35 @@ public class Workspace extends PagedView<WorkspacePageIndicator> boolean childOnFinalScreen = false; boolean childOnFinalScreen = false; if (mDragSourceInternal != null) { if (mDragSourceInternal != null) { int dragSourceChildCount = mDragSourceInternal.getChildCount(); if (isTwoPanelEnabled()) { int pagePairScreenId = getPagePair(dragObject.dragInfo.screenId); CellLayout pagePair = mWorkspaceScreens.get(pagePairScreenId); if (pagePair == null) { // TODO: after http://b/198820019 is fixed, remove this throw new IllegalStateException("Page pair is null, " + "dragScreenId: " + dragObject.dragInfo.screenId + ", pagePairScreenId: " + pagePairScreenId + ", mScreenOrder: " + mScreenOrder.toConcatString() ); } dragSourceChildCount += pagePair.getShortcutsAndWidgets().getChildCount(); } // When the drag view content is a LauncherAppWidgetHostView, we should increment the // When the drag view content is a LauncherAppWidgetHostView, we should increment the // drag source child count by 1 because the widget in drag has been detached from its // drag source child count by 1 because the widget in drag has been detached from its // original parent, ShortcutAndWidgetContainer, and reattached to the DragView. // original parent, ShortcutAndWidgetContainer, and reattached to the DragView. int dragSourceChildCount = if (dragObject.dragView.getContentView() instanceof LauncherAppWidgetHostView) { dragObject.dragView.getContentView() instanceof LauncherAppWidgetHostView dragSourceChildCount++; ? mDragSourceInternal.getChildCount() + 1 } : mDragSourceInternal.getChildCount(); if (dragSourceChildCount == 1) { if (dragSourceChildCount == 1) { lastChildOnScreen = true; lastChildOnScreen = true; } } CellLayout cl = (CellLayout) mDragSourceInternal.getParent(); CellLayout cl = (CellLayout) mDragSourceInternal.getParent(); if (indexOfChild(cl) == getChildCount() - 1) { if (getLeftmostVisiblePageForIndex(indexOfChild(cl)) == getLeftmostVisiblePageForIndex(getPageCount() - 1)) { childOnFinalScreen = true; childOnFinalScreen = true; } } } } Loading @@ -664,40 +682,83 @@ public class Workspace extends PagedView<WorkspacePageIndicator> if (lastChildOnScreen && childOnFinalScreen) { if (lastChildOnScreen && childOnFinalScreen) { return; return; } } if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) { insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID); forEachExtraEmptyPageId(extraEmptyPageId -> { if (!mWorkspaceScreens.containsKey(extraEmptyPageId)) { insertNewWorkspaceScreen(extraEmptyPageId); } } }); } } public boolean addExtraEmptyScreen() { /** if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) { * Inserts extra empty pages to the end of the existing workspaces. insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID); * Usually we add one extra empty screen, but when two panel home is enabled we add return true; * two extra screens. **/ public void addExtraEmptyScreens() { forEachExtraEmptyPageId(extraEmptyPageId -> { if (!mWorkspaceScreens.containsKey(extraEmptyPageId)) { insertNewWorkspaceScreen(extraEmptyPageId); } }); } /** * Calls the consumer with all the necessary extra empty page IDs. * On a normal one panel Workspace that means only EXTRA_EMPTY_SCREEN_ID, * but in a two panel scenario this also includes EXTRA_EMPTY_SCREEN_SECOND_ID. */ private void forEachExtraEmptyPageId(Consumer<Integer> callback) { callback.accept(EXTRA_EMPTY_SCREEN_ID); if (isTwoPanelEnabled()) { callback.accept(EXTRA_EMPTY_SCREEN_SECOND_ID); } } return false; } } /** * If two panel home is enabled we convert the last two screens that are visible at the same * time. In other cases we only convert the last page. */ private void convertFinalScreenToEmptyScreenIfNecessary() { private void convertFinalScreenToEmptyScreenIfNecessary() { if (mLauncher.isWorkspaceLoading()) { if (mLauncher.isWorkspaceLoading()) { // Invalid and dangerous operation if workspace is loading // Invalid and dangerous operation if workspace is loading return; return; } } if (hasExtraEmptyScreen() || mScreenOrder.size() == 0) return; int panelCount = getPanelCount(); int finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1); if (hasExtraEmptyScreens() || mScreenOrder.size() < panelCount) { return; } CellLayout finalScreen = mWorkspaceScreens.get(finalScreenId); SparseArray<CellLayout> finalScreens = new SparseArray<>(); // If the final screen is empty, convert it to the extra empty screen int pageCount = mScreenOrder.size(); if (finalScreen != null // First we add the last page(s) to the finalScreens collection. The number of final pages && finalScreen.getShortcutsAndWidgets().getChildCount() == 0 // depends on the panel count. && !finalScreen.isDropPending()) { for (int pageIndex = pageCount - panelCount; pageIndex < pageCount; pageIndex++) { mWorkspaceScreens.remove(finalScreenId); int screenId = mScreenOrder.get(pageIndex); mScreenOrder.removeValue(finalScreenId); CellLayout screen = mWorkspaceScreens.get(screenId); if (screen == null || screen.getShortcutsAndWidgets().getChildCount() != 0 || screen.isDropPending()) { // Final screen doesn't exist or it isn't empty or there's a pending drop return; } finalScreens.append(screenId, screen); } // if this is the last screen, convert it to the empty screen // Then we remove the final screens from the collections (but not from the view hierarchy) mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, finalScreen); // and we store them as extra empty screens. mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID); for (int i = 0; i < finalScreens.size(); i++) { int screenId = finalScreens.keyAt(i); CellLayout screen = finalScreens.get(screenId); mWorkspaceScreens.remove(screenId); mScreenOrder.removeValue(screenId); int newScreenId = mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) ? EXTRA_EMPTY_SCREEN_SECOND_ID : EXTRA_EMPTY_SCREEN_ID; mWorkspaceScreens.put(newScreenId, screen); mScreenOrder.add(newScreenId); } } } } Loading @@ -705,6 +766,23 @@ public class Workspace extends PagedView<WorkspacePageIndicator> removeExtraEmptyScreenDelayed(0, stripEmptyScreens, null); removeExtraEmptyScreenDelayed(0, stripEmptyScreens, null); } } /** * The purpose of this method is to remove empty pages from Workspace. * Empty page(s) from the end of mWorkspaceScreens will always be removed. The pages with * ID = Workspace.EXTRA_EMPTY_SCREEN_IDS will be removed if there are other non-empty pages. * If there are no more non-empty pages left, extra empty page(s) will either stay or get added. * * If stripEmptyScreens is true, all empty pages (not just the ones on the end) will be removed * from the Workspace, and if there are no more pages left then extra empty page(s) will be * added. * * The number of extra empty pages is equal to what getPanelCount() returns. * * After the method returns the possible pages are: * stripEmptyScreens = true : [non-empty pages, extra empty page(s) alone] * stripEmptyScreens = false : [non-empty pages, empty pages (not in the end), * extra empty page(s) alone] */ public void removeExtraEmptyScreenDelayed( public void removeExtraEmptyScreenDelayed( int delay, boolean stripEmptyScreens, Runnable onComplete) { int delay, boolean stripEmptyScreens, Runnable onComplete) { if (mLauncher.isWorkspaceLoading()) { if (mLauncher.isWorkspaceLoading()) { Loading @@ -718,18 +796,26 @@ public class Workspace extends PagedView<WorkspacePageIndicator> return; return; } } // First we convert the last page to an extra page if the last page is empty // and we don't already have an extra page. convertFinalScreenToEmptyScreenIfNecessary(); convertFinalScreenToEmptyScreenIfNecessary(); if (hasExtraEmptyScreen()) { // Then we remove the extra page(s) if they are not the only pages left in Workspace. removeView(mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID)); if (hasExtraEmptyScreens()) { forEachExtraEmptyPageId(extraEmptyPageId -> { removeView(mWorkspaceScreens.get(extraEmptyPageId)); mWorkspaceScreens.remove(extraEmptyPageId); mScreenOrder.removeValue(extraEmptyPageId); }); setCurrentPage(getNextPage()); setCurrentPage(getNextPage()); mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID); mScreenOrder.removeValue(EXTRA_EMPTY_SCREEN_ID); // Update the page indicator to reflect the removed page. // Update the page indicator to reflect the removed page. showPageIndicatorAtCurrentScroll(); showPageIndicatorAtCurrentScroll(); } } if (stripEmptyScreens) { if (stripEmptyScreens) { // This will remove all empty pages from the Workspace. If there are no more pages left, // it will add extra page(s) so that users can put items on at least one page. stripEmptyScreens(); stripEmptyScreens(); } } Loading @@ -738,27 +824,56 @@ public class Workspace extends PagedView<WorkspacePageIndicator> } } } } public boolean hasExtraEmptyScreen() { public boolean hasExtraEmptyScreens() { return mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) && getChildCount() > 1; return mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) && getChildCount() > getPanelCount() && (!isTwoPanelEnabled() || mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_SECOND_ID)); } } public int commitExtraEmptyScreen() { /** * Commits the extra empty pages then returns the screen ids of those new screens. * Usually there's only one extra empty screen, but when two panel home is enabled we commit * two extra screens. * * Returns an empty IntSet in case we cannot commit any new screens. */ public IntSet commitExtraEmptyScreens() { if (mLauncher.isWorkspaceLoading()) { if (mLauncher.isWorkspaceLoading()) { // Invalid and dangerous operation if workspace is loading // Invalid and dangerous operation if workspace is loading return -1; return new IntSet(); } } CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID); IntSet extraEmptyPageIds = new IntSet(); mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID); forEachExtraEmptyPageId(extraEmptyPageId -> mScreenOrder.removeValue(EXTRA_EMPTY_SCREEN_ID); extraEmptyPageIds.add(commitExtraEmptyScreen(extraEmptyPageId))); return extraEmptyPageIds; } int newId = LauncherSettings.Settings.call(getContext().getContentResolver(), private int commitExtraEmptyScreen(int emptyScreenId) { CellLayout cl = mWorkspaceScreens.get(emptyScreenId); mWorkspaceScreens.remove(emptyScreenId); mScreenOrder.removeValue(emptyScreenId); int newScreenId = LauncherSettings.Settings.call(getContext().getContentResolver(), LauncherSettings.Settings.METHOD_NEW_SCREEN_ID) LauncherSettings.Settings.METHOD_NEW_SCREEN_ID) .getInt(LauncherSettings.Settings.EXTRA_VALUE); .getInt(LauncherSettings.Settings.EXTRA_VALUE); mWorkspaceScreens.put(newId, cl); mScreenOrder.add(newId); return newId; // When two panel home is enabled and the last page (the page on the right) doesn't // have any items, then Launcher database doesn't know about this page because it was added // by Launcher::bindAddScreens but wasn't inserted into the database. LauncherSettings's // generate new screen ID method will return the ID for the left page, // so we need to increment it. if (isTwoPanelEnabled() && emptyScreenId == EXTRA_EMPTY_SCREEN_ID && newScreenId % 2 == 1) { newScreenId++; } mWorkspaceScreens.put(newScreenId, cl); mScreenOrder.add(newScreenId); return newScreenId; } } @Override @Override Loading Loading @@ -859,9 +974,10 @@ public class Workspace extends PagedView<WorkspacePageIndicator> } } } } // We enforce at least one page to add new items to. In the case that we remove the last // We enforce at least one page (two pages on two panel home) to add new items to. // such screen, we convert the last screen to the empty screen // In the case that we remove the last such screen(s), we convert the last screen(s) int minScreens = 1; // to the empty screen(s) int minScreens = getPanelCount(); int pageShift = 0; int pageShift = 0; for (int i = 0; i < removeScreens.size(); i++) { for (int i = 0; i < removeScreens.size(); i++) { Loading @@ -871,14 +987,21 @@ public class Workspace extends PagedView<WorkspacePageIndicator> mScreenOrder.removeValue(id); mScreenOrder.removeValue(id); if (getChildCount() > minScreens) { if (getChildCount() > minScreens) { // If this isn't the last page, just remove it if (indexOfChild(cl) < currentPage) { if (indexOfChild(cl) < currentPage) { pageShift++; pageShift++; } } removeView(cl); removeView(cl); } else { } else { // if this is the last screen, convert it to the empty screen // The last page(s) should be converted into extra empty page(s) mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, cl); int extraScreenId = isTwoPanelEnabled() && id % 2 == 1 mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID); // This is the right panel in a two panel scenario ? EXTRA_EMPTY_SCREEN_SECOND_ID // This is either the last screen in a one panel scenario, or the left panel // in a two panel scenario when there are only two empty pages left : EXTRA_EMPTY_SCREEN_ID; mWorkspaceScreens.put(extraScreenId, cl); mScreenOrder.add(extraScreenId); } } } } Loading Loading @@ -1656,8 +1779,8 @@ public class Workspace extends PagedView<WorkspacePageIndicator> } } int screenId = getIdForScreen(dropTargetLayout); int screenId = getIdForScreen(dropTargetLayout); if (screenId == EXTRA_EMPTY_SCREEN_ID) { if (Workspace.EXTRA_EMPTY_SCREEN_IDS.contains(screenId)) { commitExtraEmptyScreen(); commitExtraEmptyScreens(); } } return true; return true; Loading src/com/android/launcher3/WorkspaceLayoutManager.java +9 −2 Original line number Original line Diff line number Diff line Loading @@ -23,6 +23,7 @@ import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.util.IntSet; public interface WorkspaceLayoutManager { public interface WorkspaceLayoutManager { Loading @@ -30,6 +31,12 @@ public interface WorkspaceLayoutManager { // The screen id used for the empty screen always present at the end. // The screen id used for the empty screen always present at the end. int EXTRA_EMPTY_SCREEN_ID = -201; int EXTRA_EMPTY_SCREEN_ID = -201; // The screen id used for the second empty screen always present at the end for two panel home. int EXTRA_EMPTY_SCREEN_SECOND_ID = -200; // The screen ids used for the empty screens at the end of the workspaces. IntSet EXTRA_EMPTY_SCREEN_IDS = IntSet.wrap(EXTRA_EMPTY_SCREEN_ID, EXTRA_EMPTY_SCREEN_SECOND_ID); // The is the first screen. It is always present, even if its empty. // The is the first screen. It is always present, even if its empty. int FIRST_SCREEN_ID = 0; int FIRST_SCREEN_ID = 0; // This is the second page. On two panel home it is always present, even if its empty. // This is the second page. On two panel home it is always present, even if its empty. Loading Loading @@ -81,9 +88,9 @@ public interface WorkspaceLayoutManager { return; return; } } } } if (screenId == EXTRA_EMPTY_SCREEN_ID) { if (EXTRA_EMPTY_SCREEN_IDS.contains(screenId)) { // This should never happen // This should never happen throw new RuntimeException("Screen id should not be EXTRA_EMPTY_SCREEN_ID"); throw new RuntimeException("Screen id should not be extra empty screen: " + screenId); } } final CellLayout layout; final CellLayout layout; Loading src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +15 −2 Original line number Original line Diff line number Diff line Loading @@ -45,6 +45,7 @@ import com.android.launcher3.popup.ArrowPopup; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.ShortcutUtil; import com.android.launcher3.util.ShortcutUtil; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.Thunk; import com.android.launcher3.views.OptionsPopupView; import com.android.launcher3.views.OptionsPopupView; Loading Loading @@ -221,6 +222,9 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme } else if (action == ADD_TO_WORKSPACE) { } else if (action == ADD_TO_WORKSPACE) { final int[] coordinates = new int[2]; final int[] coordinates = new int[2]; final int screenId = findSpaceOnWorkspace(item, coordinates); final int screenId = findSpaceOnWorkspace(item, coordinates); if (screenId == -1) { return false; } mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> { mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> { if (item instanceof AppInfo) { if (item instanceof AppInfo) { WorkspaceItemInfo info = ((AppInfo) item).makeWorkspaceItem(); WorkspaceItemInfo info = ((AppInfo) item).makeWorkspaceItem(); Loading Loading @@ -250,6 +254,9 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme final int[] coordinates = new int[2]; final int[] coordinates = new int[2]; final int screenId = findSpaceOnWorkspace(item, coordinates); final int screenId = findSpaceOnWorkspace(item, coordinates); if (screenId == -1) { return false; } mLauncher.getModelWriter().moveItemInDatabase(info, mLauncher.getModelWriter().moveItemInDatabase(info, Favorites.CONTAINER_DESKTOP, Favorites.CONTAINER_DESKTOP, screenId, coordinates[0], coordinates[1]); screenId, coordinates[0], coordinates[1]); Loading Loading @@ -489,8 +496,14 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme return screenId; return screenId; } } workspace.addExtraEmptyScreen(); workspace.addExtraEmptyScreens(); screenId = workspace.commitExtraEmptyScreen(); IntSet emptyScreenIds = workspace.commitExtraEmptyScreens(); if (emptyScreenIds.isEmpty()) { // Couldn't create extra empty screens for some reason (e.g. Workspace is loading) return -1; } screenId = emptyScreenIds.getArray().get(0); layout = workspace.getScreenWithId(screenId); layout = workspace.getScreenWithId(screenId); found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY); found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY); Loading src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java +3 −0 Original line number Original line Diff line number Diff line Loading @@ -68,6 +68,9 @@ public class ShortcutMenuAccessibilityDelegate extends LauncherAccessibilityDele final WorkspaceItemInfo info = ((DeepShortcutView) host.getParent()).getFinalInfo(); final WorkspaceItemInfo info = ((DeepShortcutView) host.getParent()).getFinalInfo(); final int[] coordinates = new int[2]; final int[] coordinates = new int[2]; final int screenId = findSpaceOnWorkspace(item, coordinates); final int screenId = findSpaceOnWorkspace(item, coordinates); if (screenId == -1) { return false; } mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> { mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> { mLauncher.getModelWriter().addItemToDatabase(info, mLauncher.getModelWriter().addItemToDatabase(info, LauncherSettings.Favorites.CONTAINER_DESKTOP, LauncherSettings.Favorites.CONTAINER_DESKTOP, Loading Loading
src/com/android/launcher3/Launcher.java +6 −16 Original line number Original line Diff line number Diff line Loading @@ -877,11 +877,11 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche if (dropLayout == null) { if (dropLayout == null) { // it's possible that the add screen was removed because it was // it's possible that the add screen was removed because it was // empty and a re-bind occurred // empty and a re-bind occurred mWorkspace.addExtraEmptyScreen(); mWorkspace.addExtraEmptyScreens(); return mWorkspace.commitExtraEmptyScreen(); IntSet emptyPagesAdded = mWorkspace.commitExtraEmptyScreens(); } else { return emptyPagesAdded.isEmpty() ? -1 : emptyPagesAdded.getArray().get(0); return screenId; } } return screenId; } } @Thunk @Thunk Loading Loading @@ -2181,7 +2181,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche orderedScreenIds.add(firstScreenPosition, Workspace.FIRST_SCREEN_ID); orderedScreenIds.add(firstScreenPosition, Workspace.FIRST_SCREEN_ID); } else if (!FeatureFlags.QSB_ON_FIRST_SCREEN && orderedScreenIds.isEmpty()) { } else if (!FeatureFlags.QSB_ON_FIRST_SCREEN && orderedScreenIds.isEmpty()) { // If there are no screens, we need to have an empty screen // If there are no screens, we need to have an empty screen mWorkspace.addExtraEmptyScreen(); mWorkspace.addExtraEmptyScreens(); } } bindAddScreens(orderedScreenIds); bindAddScreens(orderedScreenIds); Loading @@ -2196,17 +2196,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche // Some empty pages might have been removed while the phone was in a single panel // Some empty pages might have been removed while the phone was in a single panel // mode, so we want to add those empty pages back. // mode, so we want to add those empty pages back. IntSet screenIds = IntSet.wrap(orderedScreenIds); IntSet screenIds = IntSet.wrap(orderedScreenIds); for (int i = 0; i < orderedScreenIds.size(); i++) { orderedScreenIds.forEach(screenId -> screenIds.add(mWorkspace.getPagePair(screenId))); int screenId = orderedScreenIds.get(i); // Don't add the page pair if the page is the last one and if the pair is on the // right, because that would cause a bug when adding new pages. // TODO: (b/196376162) remove this when the new screen id logic is fixed for two // panel in Workspace::commitExtraEmptyScreen if (i == orderedScreenIds.size() - 1 && screenId % 2 == 0) { continue; } screenIds.add(mWorkspace.getPagePair(screenId)); } orderedScreenIds = screenIds.getArray(); orderedScreenIds = screenIds.getArray(); } } Loading
src/com/android/launcher3/Workspace.java +171 −48 Original line number Original line Diff line number Diff line Loading @@ -125,6 +125,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Collections; import java.util.Iterator; import java.util.Iterator; import java.util.List; import java.util.List; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Collectors; Loading Loading @@ -644,18 +645,35 @@ public class Workspace extends PagedView<WorkspacePageIndicator> boolean childOnFinalScreen = false; boolean childOnFinalScreen = false; if (mDragSourceInternal != null) { if (mDragSourceInternal != null) { int dragSourceChildCount = mDragSourceInternal.getChildCount(); if (isTwoPanelEnabled()) { int pagePairScreenId = getPagePair(dragObject.dragInfo.screenId); CellLayout pagePair = mWorkspaceScreens.get(pagePairScreenId); if (pagePair == null) { // TODO: after http://b/198820019 is fixed, remove this throw new IllegalStateException("Page pair is null, " + "dragScreenId: " + dragObject.dragInfo.screenId + ", pagePairScreenId: " + pagePairScreenId + ", mScreenOrder: " + mScreenOrder.toConcatString() ); } dragSourceChildCount += pagePair.getShortcutsAndWidgets().getChildCount(); } // When the drag view content is a LauncherAppWidgetHostView, we should increment the // When the drag view content is a LauncherAppWidgetHostView, we should increment the // drag source child count by 1 because the widget in drag has been detached from its // drag source child count by 1 because the widget in drag has been detached from its // original parent, ShortcutAndWidgetContainer, and reattached to the DragView. // original parent, ShortcutAndWidgetContainer, and reattached to the DragView. int dragSourceChildCount = if (dragObject.dragView.getContentView() instanceof LauncherAppWidgetHostView) { dragObject.dragView.getContentView() instanceof LauncherAppWidgetHostView dragSourceChildCount++; ? mDragSourceInternal.getChildCount() + 1 } : mDragSourceInternal.getChildCount(); if (dragSourceChildCount == 1) { if (dragSourceChildCount == 1) { lastChildOnScreen = true; lastChildOnScreen = true; } } CellLayout cl = (CellLayout) mDragSourceInternal.getParent(); CellLayout cl = (CellLayout) mDragSourceInternal.getParent(); if (indexOfChild(cl) == getChildCount() - 1) { if (getLeftmostVisiblePageForIndex(indexOfChild(cl)) == getLeftmostVisiblePageForIndex(getPageCount() - 1)) { childOnFinalScreen = true; childOnFinalScreen = true; } } } } Loading @@ -664,40 +682,83 @@ public class Workspace extends PagedView<WorkspacePageIndicator> if (lastChildOnScreen && childOnFinalScreen) { if (lastChildOnScreen && childOnFinalScreen) { return; return; } } if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) { insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID); forEachExtraEmptyPageId(extraEmptyPageId -> { if (!mWorkspaceScreens.containsKey(extraEmptyPageId)) { insertNewWorkspaceScreen(extraEmptyPageId); } } }); } } public boolean addExtraEmptyScreen() { /** if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) { * Inserts extra empty pages to the end of the existing workspaces. insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID); * Usually we add one extra empty screen, but when two panel home is enabled we add return true; * two extra screens. **/ public void addExtraEmptyScreens() { forEachExtraEmptyPageId(extraEmptyPageId -> { if (!mWorkspaceScreens.containsKey(extraEmptyPageId)) { insertNewWorkspaceScreen(extraEmptyPageId); } }); } /** * Calls the consumer with all the necessary extra empty page IDs. * On a normal one panel Workspace that means only EXTRA_EMPTY_SCREEN_ID, * but in a two panel scenario this also includes EXTRA_EMPTY_SCREEN_SECOND_ID. */ private void forEachExtraEmptyPageId(Consumer<Integer> callback) { callback.accept(EXTRA_EMPTY_SCREEN_ID); if (isTwoPanelEnabled()) { callback.accept(EXTRA_EMPTY_SCREEN_SECOND_ID); } } return false; } } /** * If two panel home is enabled we convert the last two screens that are visible at the same * time. In other cases we only convert the last page. */ private void convertFinalScreenToEmptyScreenIfNecessary() { private void convertFinalScreenToEmptyScreenIfNecessary() { if (mLauncher.isWorkspaceLoading()) { if (mLauncher.isWorkspaceLoading()) { // Invalid and dangerous operation if workspace is loading // Invalid and dangerous operation if workspace is loading return; return; } } if (hasExtraEmptyScreen() || mScreenOrder.size() == 0) return; int panelCount = getPanelCount(); int finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1); if (hasExtraEmptyScreens() || mScreenOrder.size() < panelCount) { return; } CellLayout finalScreen = mWorkspaceScreens.get(finalScreenId); SparseArray<CellLayout> finalScreens = new SparseArray<>(); // If the final screen is empty, convert it to the extra empty screen int pageCount = mScreenOrder.size(); if (finalScreen != null // First we add the last page(s) to the finalScreens collection. The number of final pages && finalScreen.getShortcutsAndWidgets().getChildCount() == 0 // depends on the panel count. && !finalScreen.isDropPending()) { for (int pageIndex = pageCount - panelCount; pageIndex < pageCount; pageIndex++) { mWorkspaceScreens.remove(finalScreenId); int screenId = mScreenOrder.get(pageIndex); mScreenOrder.removeValue(finalScreenId); CellLayout screen = mWorkspaceScreens.get(screenId); if (screen == null || screen.getShortcutsAndWidgets().getChildCount() != 0 || screen.isDropPending()) { // Final screen doesn't exist or it isn't empty or there's a pending drop return; } finalScreens.append(screenId, screen); } // if this is the last screen, convert it to the empty screen // Then we remove the final screens from the collections (but not from the view hierarchy) mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, finalScreen); // and we store them as extra empty screens. mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID); for (int i = 0; i < finalScreens.size(); i++) { int screenId = finalScreens.keyAt(i); CellLayout screen = finalScreens.get(screenId); mWorkspaceScreens.remove(screenId); mScreenOrder.removeValue(screenId); int newScreenId = mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) ? EXTRA_EMPTY_SCREEN_SECOND_ID : EXTRA_EMPTY_SCREEN_ID; mWorkspaceScreens.put(newScreenId, screen); mScreenOrder.add(newScreenId); } } } } Loading @@ -705,6 +766,23 @@ public class Workspace extends PagedView<WorkspacePageIndicator> removeExtraEmptyScreenDelayed(0, stripEmptyScreens, null); removeExtraEmptyScreenDelayed(0, stripEmptyScreens, null); } } /** * The purpose of this method is to remove empty pages from Workspace. * Empty page(s) from the end of mWorkspaceScreens will always be removed. The pages with * ID = Workspace.EXTRA_EMPTY_SCREEN_IDS will be removed if there are other non-empty pages. * If there are no more non-empty pages left, extra empty page(s) will either stay or get added. * * If stripEmptyScreens is true, all empty pages (not just the ones on the end) will be removed * from the Workspace, and if there are no more pages left then extra empty page(s) will be * added. * * The number of extra empty pages is equal to what getPanelCount() returns. * * After the method returns the possible pages are: * stripEmptyScreens = true : [non-empty pages, extra empty page(s) alone] * stripEmptyScreens = false : [non-empty pages, empty pages (not in the end), * extra empty page(s) alone] */ public void removeExtraEmptyScreenDelayed( public void removeExtraEmptyScreenDelayed( int delay, boolean stripEmptyScreens, Runnable onComplete) { int delay, boolean stripEmptyScreens, Runnable onComplete) { if (mLauncher.isWorkspaceLoading()) { if (mLauncher.isWorkspaceLoading()) { Loading @@ -718,18 +796,26 @@ public class Workspace extends PagedView<WorkspacePageIndicator> return; return; } } // First we convert the last page to an extra page if the last page is empty // and we don't already have an extra page. convertFinalScreenToEmptyScreenIfNecessary(); convertFinalScreenToEmptyScreenIfNecessary(); if (hasExtraEmptyScreen()) { // Then we remove the extra page(s) if they are not the only pages left in Workspace. removeView(mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID)); if (hasExtraEmptyScreens()) { forEachExtraEmptyPageId(extraEmptyPageId -> { removeView(mWorkspaceScreens.get(extraEmptyPageId)); mWorkspaceScreens.remove(extraEmptyPageId); mScreenOrder.removeValue(extraEmptyPageId); }); setCurrentPage(getNextPage()); setCurrentPage(getNextPage()); mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID); mScreenOrder.removeValue(EXTRA_EMPTY_SCREEN_ID); // Update the page indicator to reflect the removed page. // Update the page indicator to reflect the removed page. showPageIndicatorAtCurrentScroll(); showPageIndicatorAtCurrentScroll(); } } if (stripEmptyScreens) { if (stripEmptyScreens) { // This will remove all empty pages from the Workspace. If there are no more pages left, // it will add extra page(s) so that users can put items on at least one page. stripEmptyScreens(); stripEmptyScreens(); } } Loading @@ -738,27 +824,56 @@ public class Workspace extends PagedView<WorkspacePageIndicator> } } } } public boolean hasExtraEmptyScreen() { public boolean hasExtraEmptyScreens() { return mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) && getChildCount() > 1; return mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) && getChildCount() > getPanelCount() && (!isTwoPanelEnabled() || mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_SECOND_ID)); } } public int commitExtraEmptyScreen() { /** * Commits the extra empty pages then returns the screen ids of those new screens. * Usually there's only one extra empty screen, but when two panel home is enabled we commit * two extra screens. * * Returns an empty IntSet in case we cannot commit any new screens. */ public IntSet commitExtraEmptyScreens() { if (mLauncher.isWorkspaceLoading()) { if (mLauncher.isWorkspaceLoading()) { // Invalid and dangerous operation if workspace is loading // Invalid and dangerous operation if workspace is loading return -1; return new IntSet(); } } CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID); IntSet extraEmptyPageIds = new IntSet(); mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID); forEachExtraEmptyPageId(extraEmptyPageId -> mScreenOrder.removeValue(EXTRA_EMPTY_SCREEN_ID); extraEmptyPageIds.add(commitExtraEmptyScreen(extraEmptyPageId))); return extraEmptyPageIds; } int newId = LauncherSettings.Settings.call(getContext().getContentResolver(), private int commitExtraEmptyScreen(int emptyScreenId) { CellLayout cl = mWorkspaceScreens.get(emptyScreenId); mWorkspaceScreens.remove(emptyScreenId); mScreenOrder.removeValue(emptyScreenId); int newScreenId = LauncherSettings.Settings.call(getContext().getContentResolver(), LauncherSettings.Settings.METHOD_NEW_SCREEN_ID) LauncherSettings.Settings.METHOD_NEW_SCREEN_ID) .getInt(LauncherSettings.Settings.EXTRA_VALUE); .getInt(LauncherSettings.Settings.EXTRA_VALUE); mWorkspaceScreens.put(newId, cl); mScreenOrder.add(newId); return newId; // When two panel home is enabled and the last page (the page on the right) doesn't // have any items, then Launcher database doesn't know about this page because it was added // by Launcher::bindAddScreens but wasn't inserted into the database. LauncherSettings's // generate new screen ID method will return the ID for the left page, // so we need to increment it. if (isTwoPanelEnabled() && emptyScreenId == EXTRA_EMPTY_SCREEN_ID && newScreenId % 2 == 1) { newScreenId++; } mWorkspaceScreens.put(newScreenId, cl); mScreenOrder.add(newScreenId); return newScreenId; } } @Override @Override Loading Loading @@ -859,9 +974,10 @@ public class Workspace extends PagedView<WorkspacePageIndicator> } } } } // We enforce at least one page to add new items to. In the case that we remove the last // We enforce at least one page (two pages on two panel home) to add new items to. // such screen, we convert the last screen to the empty screen // In the case that we remove the last such screen(s), we convert the last screen(s) int minScreens = 1; // to the empty screen(s) int minScreens = getPanelCount(); int pageShift = 0; int pageShift = 0; for (int i = 0; i < removeScreens.size(); i++) { for (int i = 0; i < removeScreens.size(); i++) { Loading @@ -871,14 +987,21 @@ public class Workspace extends PagedView<WorkspacePageIndicator> mScreenOrder.removeValue(id); mScreenOrder.removeValue(id); if (getChildCount() > minScreens) { if (getChildCount() > minScreens) { // If this isn't the last page, just remove it if (indexOfChild(cl) < currentPage) { if (indexOfChild(cl) < currentPage) { pageShift++; pageShift++; } } removeView(cl); removeView(cl); } else { } else { // if this is the last screen, convert it to the empty screen // The last page(s) should be converted into extra empty page(s) mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, cl); int extraScreenId = isTwoPanelEnabled() && id % 2 == 1 mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID); // This is the right panel in a two panel scenario ? EXTRA_EMPTY_SCREEN_SECOND_ID // This is either the last screen in a one panel scenario, or the left panel // in a two panel scenario when there are only two empty pages left : EXTRA_EMPTY_SCREEN_ID; mWorkspaceScreens.put(extraScreenId, cl); mScreenOrder.add(extraScreenId); } } } } Loading Loading @@ -1656,8 +1779,8 @@ public class Workspace extends PagedView<WorkspacePageIndicator> } } int screenId = getIdForScreen(dropTargetLayout); int screenId = getIdForScreen(dropTargetLayout); if (screenId == EXTRA_EMPTY_SCREEN_ID) { if (Workspace.EXTRA_EMPTY_SCREEN_IDS.contains(screenId)) { commitExtraEmptyScreen(); commitExtraEmptyScreens(); } } return true; return true; Loading
src/com/android/launcher3/WorkspaceLayoutManager.java +9 −2 Original line number Original line Diff line number Diff line Loading @@ -23,6 +23,7 @@ import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.util.IntSet; public interface WorkspaceLayoutManager { public interface WorkspaceLayoutManager { Loading @@ -30,6 +31,12 @@ public interface WorkspaceLayoutManager { // The screen id used for the empty screen always present at the end. // The screen id used for the empty screen always present at the end. int EXTRA_EMPTY_SCREEN_ID = -201; int EXTRA_EMPTY_SCREEN_ID = -201; // The screen id used for the second empty screen always present at the end for two panel home. int EXTRA_EMPTY_SCREEN_SECOND_ID = -200; // The screen ids used for the empty screens at the end of the workspaces. IntSet EXTRA_EMPTY_SCREEN_IDS = IntSet.wrap(EXTRA_EMPTY_SCREEN_ID, EXTRA_EMPTY_SCREEN_SECOND_ID); // The is the first screen. It is always present, even if its empty. // The is the first screen. It is always present, even if its empty. int FIRST_SCREEN_ID = 0; int FIRST_SCREEN_ID = 0; // This is the second page. On two panel home it is always present, even if its empty. // This is the second page. On two panel home it is always present, even if its empty. Loading Loading @@ -81,9 +88,9 @@ public interface WorkspaceLayoutManager { return; return; } } } } if (screenId == EXTRA_EMPTY_SCREEN_ID) { if (EXTRA_EMPTY_SCREEN_IDS.contains(screenId)) { // This should never happen // This should never happen throw new RuntimeException("Screen id should not be EXTRA_EMPTY_SCREEN_ID"); throw new RuntimeException("Screen id should not be extra empty screen: " + screenId); } } final CellLayout layout; final CellLayout layout; Loading
src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +15 −2 Original line number Original line Diff line number Diff line Loading @@ -45,6 +45,7 @@ import com.android.launcher3.popup.ArrowPopup; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.ShortcutUtil; import com.android.launcher3.util.ShortcutUtil; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.Thunk; import com.android.launcher3.views.OptionsPopupView; import com.android.launcher3.views.OptionsPopupView; Loading Loading @@ -221,6 +222,9 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme } else if (action == ADD_TO_WORKSPACE) { } else if (action == ADD_TO_WORKSPACE) { final int[] coordinates = new int[2]; final int[] coordinates = new int[2]; final int screenId = findSpaceOnWorkspace(item, coordinates); final int screenId = findSpaceOnWorkspace(item, coordinates); if (screenId == -1) { return false; } mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> { mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> { if (item instanceof AppInfo) { if (item instanceof AppInfo) { WorkspaceItemInfo info = ((AppInfo) item).makeWorkspaceItem(); WorkspaceItemInfo info = ((AppInfo) item).makeWorkspaceItem(); Loading Loading @@ -250,6 +254,9 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme final int[] coordinates = new int[2]; final int[] coordinates = new int[2]; final int screenId = findSpaceOnWorkspace(item, coordinates); final int screenId = findSpaceOnWorkspace(item, coordinates); if (screenId == -1) { return false; } mLauncher.getModelWriter().moveItemInDatabase(info, mLauncher.getModelWriter().moveItemInDatabase(info, Favorites.CONTAINER_DESKTOP, Favorites.CONTAINER_DESKTOP, screenId, coordinates[0], coordinates[1]); screenId, coordinates[0], coordinates[1]); Loading Loading @@ -489,8 +496,14 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme return screenId; return screenId; } } workspace.addExtraEmptyScreen(); workspace.addExtraEmptyScreens(); screenId = workspace.commitExtraEmptyScreen(); IntSet emptyScreenIds = workspace.commitExtraEmptyScreens(); if (emptyScreenIds.isEmpty()) { // Couldn't create extra empty screens for some reason (e.g. Workspace is loading) return -1; } screenId = emptyScreenIds.getArray().get(0); layout = workspace.getScreenWithId(screenId); layout = workspace.getScreenWithId(screenId); found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY); found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY); Loading
src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java +3 −0 Original line number Original line Diff line number Diff line Loading @@ -68,6 +68,9 @@ public class ShortcutMenuAccessibilityDelegate extends LauncherAccessibilityDele final WorkspaceItemInfo info = ((DeepShortcutView) host.getParent()).getFinalInfo(); final WorkspaceItemInfo info = ((DeepShortcutView) host.getParent()).getFinalInfo(); final int[] coordinates = new int[2]; final int[] coordinates = new int[2]; final int screenId = findSpaceOnWorkspace(item, coordinates); final int screenId = findSpaceOnWorkspace(item, coordinates); if (screenId == -1) { return false; } mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> { mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> { mLauncher.getModelWriter().addItemToDatabase(info, mLauncher.getModelWriter().addItemToDatabase(info, LauncherSettings.Favorites.CONTAINER_DESKTOP, LauncherSettings.Favorites.CONTAINER_DESKTOP, Loading