diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 84ae0fe43a..7cf06050fa 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -176,6 +176,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; +import java.util.Map.Entry; /** * Manages the opening and closing app transitions from Launcher @@ -1342,9 +1343,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener ? Collections.EMPTY_LIST : runningTaskTarget.taskInfo.launchCookies; - return mLauncher.getFirstMatchForAppClose( + return mLauncher.getFirstVisibleElementForAppClose( StableViewInfo.fromLaunchCookies(launchCookies), packageName, - UserHandle.of(runningTaskTarget.taskInfo.userId), true /* supportsAllAppsState */); + UserHandle.of(runningTaskTarget.taskInfo.userId)); } private @NonNull RectF getDefaultWindowTargetRect() { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index ade75ebed6..1eb057f9c3 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -1794,7 +1794,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { folder.animateOpen(); getStatsLogManager().logger().withItemInfo(folder.mInfo).log(LAUNCHER_FOLDER_OPEN); - folder.iterateOverItems((itemInfo, itemView) -> { + folder.mapOverItems((itemInfo, itemView) -> { mControllers.taskbarViewController .setClickAndLongClickListenersForIcon(itemView); // To play haptic when dragging, like other Taskbar items do. diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java index 0fa82aef97..281d236ef1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java @@ -18,6 +18,7 @@ package com.android.launcher3.taskbar; import android.util.SparseArray; import android.view.View; +import androidx.annotation.NonNull; import androidx.annotation.UiThread; import com.android.launcher3.LauncherSettings.Favorites; @@ -120,14 +121,15 @@ public class TaskbarModelCallbacks implements } @Override - public void mapOverItems(ItemOperator op) { + public View mapOverItems(@NonNull ItemOperator op) { final int itemCount = mContainer.getChildCount(); for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) { View item = mContainer.getChildAt(itemIdx); if (item.getTag() instanceof ItemInfo itemInfo && op.evaluate(itemInfo, item)) { - return; + return item; } } + return null; } @Override diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index de8e28639b..7a854ca84d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -76,7 +76,6 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Set; -import java.util.function.Predicate; /** * Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps. @@ -1096,29 +1095,6 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar return mAllAppsButtonTranslationOffset; } - /** - * Finds the first icon to match one of the given matchers, from highest to lowest priority. - * - * @return The first match, or All Apps button if no match was found. - */ - public View getFirstMatch(Predicate... matchers) { - for (Predicate matcher : matchers) { - for (int i = 0; i < getChildCount(); i++) { - View item = getChildAt(i); - if (!(item.getTag() instanceof ItemInfo)) { - // Should only happen for All Apps button. - // Will also happen for Recent/Running app icons. (Which have GroupTask as tags) - continue; - } - ItemInfo info = (ItemInfo) item.getTag(); - if (matcher.test(info)) { - return item; - } - } - } - return mAllAppsButtonContainer; - } - /** * This method only works for bubble bar enabled in persistent task bar and the taskbar is start * aligned. diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index 6ae13d4b18..ec5b872804 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -1191,8 +1191,8 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar * 3) All Apps button */ public View getFirstIconMatch(Predicate matcher) { - Predicate collectionMatcher = ItemInfoMatcher.forFolderMatch(matcher); - return mTaskbarView.getFirstMatch(matcher, collectionMatcher); + View icon = mModelCallbacks.getFirstMatch(matcher, ItemInfoMatcher.forFolderMatch(matcher)); + return icon != null ? icon : mTaskbarView.getAllAppsButtonContainer(); } /** diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index aab8ad12d1..4945eefa6f 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -85,6 +85,7 @@ import android.os.IRemoteCallback; import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; +import android.text.TextUtils; import android.util.AttributeSet; import android.view.Display; import android.view.HapticFeedbackConstants; @@ -117,6 +118,7 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; +import com.android.launcher3.allapps.AllAppsRecyclerView; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.apppairs.AppPairIcon; @@ -166,8 +168,10 @@ import com.android.launcher3.util.RunnableList; import com.android.launcher3.util.SplitConfigurationOptions; import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource; +import com.android.launcher3.util.StableViewInfo; import com.android.launcher3.util.StartActivityParams; import com.android.launcher3.util.TouchController; +import com.android.launcher3.views.FloatingIconView; import com.android.launcher3.widget.LauncherWidgetHolder; import com.android.quickstep.OverviewCommandHelper; import com.android.quickstep.OverviewComponentObserver; @@ -1455,6 +1459,45 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer, mBubbleBarLocation = bubbleBarLocation; } + /** + * Similar to {@link #getFirstHomeElementForAppClose} but also matches all apps if its visible + */ + @Nullable + public View getFirstVisibleElementForAppClose( + @Nullable StableViewInfo svi, String packageName, UserHandle user) { + if (isInState(LauncherState.ALL_APPS)) { + AllAppsRecyclerView activeRecyclerView = getAppsView().getActiveRecyclerView(); + View v = null; + if (svi != null) { + // Preferred item match + v = activeRecyclerView.findViewByPredicate(view -> + view.isAggregatedVisible() + && view.getTag() instanceof ItemInfo info && svi.matches(info)); + } + if (v == null) { + // Package user match + v = activeRecyclerView.findViewByPredicate(view -> + view.isAggregatedVisible() && view.getTag() instanceof ItemInfo info + && info.itemType == ITEM_TYPE_APPLICATION + && info.user.equals(user) + && TextUtils.equals(info.getTargetPackage(), packageName)); + } + + if (v != null && activeRecyclerView.computeVerticalScrollOffset() > 0) { + RectF locationBounds = new RectF(); + FloatingIconView.getLocationBoundsForView(this, v, false, locationBounds, + new Rect()); + if (locationBounds.top < getAppsView().getHeaderBottom()) { + // Icon is covered by scrim, return null to play fallback animation. + return null; + } + } + return v; + } + + return getFirstHomeElementForAppClose(svi, packageName, user); + } + @Override public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { super.dump(prefix, fd, writer, args); diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java index 0a77688956..4c56f35d90 100644 --- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java +++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java @@ -308,10 +308,10 @@ public class LauncherSwipeHandlerV2 extends AbsSwipeUpHandler< return null; } - return mContainer.getFirstMatchForAppClose(StableViewInfo.fromLaunchCookies(launchCookies), + return mContainer.getFirstHomeElementForAppClose( + StableViewInfo.fromLaunchCookies(launchCookies), sourceTaskView.getFirstTask().key.getComponent().getPackageName(), - UserHandle.of(sourceTaskView.getFirstTask().key.userId), - false /* supportsAllAppsState */); + UserHandle.of(sourceTaskView.getFirstTask().key.userId)); } @Override diff --git a/src/com/android/launcher3/DropTargetHandler.kt b/src/com/android/launcher3/DropTargetHandler.kt index 0cc7fc7d72..3c162a2f85 100644 --- a/src/com/android/launcher3/DropTargetHandler.kt +++ b/src/com/android/launcher3/DropTargetHandler.kt @@ -72,9 +72,10 @@ class DropTargetHandler(launcher: Launcher) { AbstractFloatingView.TYPE_WIDGET_RESIZE_FRAME, ) var pageItem: ItemInfo = item - if (item.container <= 0) { - val v = mLauncher.workspace.getHomescreenIconByItemId(item.container) - v?.let { pageItem = v.tag as ItemInfo } + if (item.container >= 0) { + mLauncher.workspace.getViewByItemId(item.container)?.let { + pageItem = it.tag as ItemInfo + } } val pageIds = if (pageItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 728bc3411f..13ffac0900 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -71,9 +71,11 @@ import static com.android.launcher3.LauncherState.NO_OFFSET; import static com.android.launcher3.LauncherState.NO_SCALE; import static com.android.launcher3.LauncherState.SPRING_LOADED; import static com.android.launcher3.Utilities.postAsyncCallback; +import static com.android.launcher3.Workspace.mapOverCellLayouts; import static com.android.launcher3.anim.AnimatorListeners.forEndCallback; import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE; import static com.android.launcher3.config.FeatureFlags.MULTI_SELECT_EDIT_MODE; +import static com.android.launcher3.icons.BitmapRenderer.createHardwareBitmap; import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.HIDE; import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.SHOW; import static com.android.launcher3.logging.StatsLogManager.EventEnum; @@ -125,7 +127,6 @@ import android.content.SharedPreferences; import android.content.res.Configuration; import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; -import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Rect; import android.graphics.RectF; @@ -163,7 +164,6 @@ import androidx.annotation.CallSuper; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; -import androidx.annotation.StringRes; import androidx.annotation.UiThread; import androidx.annotation.VisibleForTesting; import androidx.core.os.BuildCompat; @@ -172,7 +172,6 @@ import androidx.window.embedding.RuleController; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.allapps.ActivityAllAppsContainerView; -import com.android.launcher3.allapps.AllAppsRecyclerView; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.anim.AnimationSuccessListener; @@ -1485,7 +1484,7 @@ public class Launcher extends StatefulActivity @Override public @Nullable FolderIcon findFolderIcon(final int folderIconId) { - return (FolderIcon) mWorkspace.getHomescreenIconByItemId(folderIconId); + return (FolderIcon) mWorkspace.getViewByItemId(folderIconId); } /** @@ -1879,7 +1878,8 @@ public class Launcher extends StatefulActivity if (dropView != null && dropView.containsAppWidgetHostView()) { // Extracting Bitmap from dropView instead of its content view produces the correct // bitmap. - widgetPreviewBitmap = getBitmapFromView(dropView); + widgetPreviewBitmap = createHardwareBitmap( + dropView.getWidth(), dropView.getHeight(), dropView::draw); } } @@ -2049,7 +2049,7 @@ public class Launcher extends StatefulActivity public boolean removeItem(View v, final ItemInfo itemInfo, boolean deleteFromDb, @Nullable final String reason) { if (itemInfo instanceof WorkspaceItemInfo) { - View collectionIcon = mWorkspace.getHomescreenIconByItemId(itemInfo.container); + View collectionIcon = mWorkspace.getViewByItemId(itemInfo.container); if (collectionIcon instanceof FolderIcon) { // Remove the shortcut from the folder before removing it from launcher ((FolderInfo) collectionIcon.getTag()).remove((WorkspaceItemInfo) itemInfo, true); @@ -2428,52 +2428,25 @@ public class Launcher extends StatefulActivity } /** - * Similar to {@link #getFirstMatch} but optimized to finding a suitable view for the app close - * animation. + * Finds the first view on homescreen matching the provided parameters, optimized to finding a + * suitable view for the app close animation. * * @param svi The StableViewInfo of the preferred item to match to if it exists or null * @param packageName The package name of the app to match. * @param user The user of the app to match. - * @param supportsAllAppsState If true and we are in All Apps state, looks for view in All Apps. - * Else we only looks on the workspace. */ - public @Nullable View getFirstMatchForAppClose( - @Nullable StableViewInfo svi, String packageName, - UserHandle user, boolean supportsAllAppsState) { + public @Nullable View getFirstHomeElementForAppClose( + @Nullable StableViewInfo svi, String packageName, UserHandle user) { final Predicate preferredItem = svi == null ? i -> false : svi::matches; - final Predicate packageAndUserAndApp = info -> - info != null - && info.itemType == ITEM_TYPE_APPLICATION - && info.user.equals(user) - && info.getTargetComponent() != null - && TextUtils.equals(info.getTargetComponent().getPackageName(), - packageName); - - if (supportsAllAppsState && isInState(LauncherState.ALL_APPS)) { - AllAppsRecyclerView activeRecyclerView = mAppsView.getActiveRecyclerView(); - View v = getFirstMatch(Collections.singletonList(activeRecyclerView), - preferredItem, packageAndUserAndApp); - - if (v != null && activeRecyclerView.computeVerticalScrollOffset() > 0) { - RectF locationBounds = new RectF(); - FloatingIconView.getLocationBoundsForView(this, v, false, locationBounds, - new Rect()); - if (locationBounds.top < mAppsView.getHeaderBottom()) { - // Icon is covered by scrim, return null to play fallback animation. - return null; - } - } - - return v; - } + final Predicate packageAndUserAndApp = info -> info != null + && info.itemType == ITEM_TYPE_APPLICATION + && info.user.equals(user) + && TextUtils.equals(info.getTargetPackage(), packageName); // Look for the item inside the folder at the current page Folder folder = Folder.getOpen(this); if (folder != null) { - View v = getFirstMatch(Collections.singletonList( - folder.getContent().getCurrentCellLayout().getShortcutsAndWidgets()), - preferredItem, - packageAndUserAndApp); + View v = folder.getFirstMatch(preferredItem, packageAndUserAndApp); if (v == null) { folder.close(isStarted() && !isForceInvisible()); } else { @@ -2481,72 +2454,19 @@ public class Launcher extends StatefulActivity } } - List containers = new ArrayList<>(mWorkspace.getPanelCount() + 1); - containers.add(mWorkspace.getHotseat().getShortcutsAndWidgets()); - mWorkspace.forEachVisiblePage(page - -> containers.add(((CellLayout) page).getShortcutsAndWidgets())); + List containers = new ArrayList<>(mWorkspace.getPanelCount() + 1); + containers.add(mWorkspace.getHotseat()); + mWorkspace.forEachVisiblePage(page -> containers.add((CellLayout) page)); + CellLayout[] containerArray = containers.toArray(new CellLayout[0]); + LauncherBindableItemsContainer visibleContainer = + op -> mapOverCellLayouts(containerArray, op); // Order: Preferred item by itself or in folder, then by matching package/user - return getFirstMatch(containers, preferredItem, forFolderMatch(preferredItem), + return visibleContainer.getFirstMatch( + preferredItem, forFolderMatch(preferredItem), packageAndUserAndApp, forFolderMatch(packageAndUserAndApp)); } - /** - * Finds the first view matching the ordered operators across the given viewgroups in order. - * @param containers List of ViewGroups to scan, in order of preference. - * @param operators List of operators, in order starting from best matching operator. - */ - @Nullable - private static View getFirstMatch(Iterable containers, - final Predicate... operators) { - for (Predicate operator : operators) { - for (ViewGroup container : containers) { - View match = mapOverViewGroup(container, operator); - if (match != null) { - return match; - } - } - } - return null; - } - - /** Convert a {@link View} to {@link Bitmap}. */ - private static Bitmap getBitmapFromView(@Nullable View view) { - if (view == null) { - return null; - } - Bitmap returnedBitmap = - Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(returnedBitmap); - view.draw(canvas); - return returnedBitmap; - } - - /** - * Returns the first view matching the operator in the given ViewGroups, or null if none. - * Forward iteration matters. - */ - @Nullable - private static View mapOverViewGroup(ViewGroup container, Predicate op) { - final int itemCount = container.getChildCount(); - for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) { - View item = container.getChildAt(itemIdx); - if (item.getVisibility() != View.VISIBLE) { - continue; - } - if (item instanceof ViewGroup viewGroup) { - View view = mapOverViewGroup(viewGroup, op); - if (view != null) { - return view; - } - } - if (item.getTag() instanceof ItemInfo itemInfo && op.test(itemInfo)) { - return item; - } - } - return null; - } - private ValueAnimator createNewAppBounceAnimation(View v, int i) { ValueAnimator bounceAnim = new PropertyListBuilder().alpha(1).scale(1).build(v) .setDuration(ItemInstallQueue.NEW_SHORTCUT_BOUNCE_DURATION); @@ -2555,10 +2475,6 @@ public class Launcher extends StatefulActivity return bounceAnim; } - private void announceForAccessibility(@StringRes int stringResId) { - getDragLayer().announceForAccessibility(getString(stringResId)); - } - /** * Informs us that the overlay (-1 screen, typically), has either become visible or invisible. */ diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 559582879e..3cb5480d85 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -67,6 +67,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.core.view.ViewCompat; @@ -3157,7 +3158,7 @@ public class Workspace extends PagedView + "Workspace#onDropCompleted. Please file a bug. "); } } - View cell = getHomescreenIconByItemId(d.originalDragInfo.id); + View cell = getViewByItemId(d.originalDragInfo.id); if (d.cancelled && cell != null) { cell.setVisibility(VISIBLE); } @@ -3306,29 +3307,9 @@ public class Workspace extends PagedView return layouts; } - public View getHomescreenIconByItemId(final int id) { - return getFirstMatch((info, v) -> info != null && info.id == id); - } - public LauncherAppWidgetHostView getWidgetForAppWidgetId(final int appWidgetId) { - return (LauncherAppWidgetHostView) getFirstMatch((info, v) -> - (info instanceof LauncherAppWidgetInfo) && - ((LauncherAppWidgetInfo) info).appWidgetId == appWidgetId); - } - - public View getFirstMatch(final ItemOperator operator) { - final View[] value = new View[1]; - mapOverItems(new ItemOperator() { - @Override - public boolean evaluate(ItemInfo info, View v) { - if (operator.evaluate(info, v)) { - value[0] = v; - return true; - } - return false; - } - }); - return value[0]; + return (LauncherAppWidgetHostView) mapOverItems((info, v) -> + (info instanceof LauncherAppWidgetInfo lawi) && lawi.appWidgetId == appWidgetId); } void clearDropTargets() { @@ -3387,31 +3368,26 @@ public class Workspace extends PagedView } @Override - public void mapOverItems(ItemOperator op) { - for (CellLayout layout : getWorkspaceAndHotseatCellLayouts()) { - if (mapOverCellLayout(layout, op) != null) { - return; - } - } + public View mapOverItems(@NonNull ItemOperator op) { + return mapOverCellLayouts(getWorkspaceAndHotseatCellLayouts(), op); } /** - * Perform {param operator} over all the items in a given {param layout}. - * - * @return The first item that satisfies the operator or null. + * Perform {param op} over all the items in the provided {param layouts} until a match is found */ - public View mapOverCellLayout(CellLayout layout, ItemOperator operator) { - // TODO(b/128460496) Potential race condition where layout is not yet loaded - if (layout == null) { - return null; - } - ShortcutAndWidgetContainer container = layout.getShortcutsAndWidgets(); - // map over all the shortcuts on the workspace - final int itemCount = container.getChildCount(); - for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) { - View item = container.getChildAt(itemIdx); - if (operator.evaluate((ItemInfo) item.getTag(), item)) { - return item; + public static View mapOverCellLayouts(CellLayout[] layouts, ItemOperator op) { + for (CellLayout layout : layouts) { + // TODO(b/128460496) Potential race condition where layout is not yet loaded + if (layout == null) continue; + + ShortcutAndWidgetContainer container = layout.getShortcutsAndWidgets(); + // map over all the shortcuts on the layout + final int itemCount = container.getChildCount(); + for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) { + View item = container.getChildAt(itemIdx); + if (op.evaluate((ItemInfo) item.getTag(), item)) { + return item; + } } } return null; diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 0ce72494a8..28032569b2 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -101,6 +101,7 @@ import com.android.launcher3.model.data.WorkspaceItemFactory; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.pageindicators.PageIndicatorDots; import com.android.launcher3.util.Executors; +import com.android.launcher3.util.LauncherBindableItemsContainer; import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator; import com.android.launcher3.util.Thunk; import com.android.launcher3.views.ActivityContext; @@ -123,7 +124,8 @@ import java.util.stream.Stream; */ public class Folder extends AbstractFloatingView implements ClipPathView, DragSource, View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener, - View.OnFocusChangeListener, DragListener, ExtendedEditText.OnBackKeyListener { + View.OnFocusChangeListener, DragListener, ExtendedEditText.OnBackKeyListener, + LauncherBindableItemsContainer { private static final String TAG = "Launcher.Folder"; private static final boolean DEBUG = false; @@ -1513,8 +1515,10 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo /** * Utility methods to iterate over items of the view */ - public void iterateOverItems(ItemOperator op) { - mContent.iterateOverItems(op); + @Override + @Nullable + public View mapOverItems(@NonNull ItemOperator op) { + return mContent.iterateOverItems(op); } /** diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java index 318b3ceffa..5c1a7553a5 100644 --- a/src/com/android/launcher3/popup/PopupDataProvider.java +++ b/src/com/android/launcher3/popup/PopupDataProvider.java @@ -90,7 +90,7 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan mContext.getContent().mapOverItems(op); Folder folder = Folder.getOpen(mContext); if (folder != null) { - folder.iterateOverItems(op); + folder.mapOverItems(op); } ActivityAllAppsContainerView appsView = mContext.getAppsView(); diff --git a/src/com/android/launcher3/util/LauncherBindableItemsContainer.kt b/src/com/android/launcher3/util/LauncherBindableItemsContainer.kt index 16617962bd..8fdedef77e 100644 --- a/src/com/android/launcher3/util/LauncherBindableItemsContainer.kt +++ b/src/com/android/launcher3/util/LauncherBindableItemsContainer.kt @@ -25,8 +25,10 @@ import com.android.launcher3.model.data.AppPairInfo import com.android.launcher3.model.data.FolderInfo import com.android.launcher3.model.data.ItemInfo import com.android.launcher3.model.data.WorkspaceItemInfo +import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator import com.android.launcher3.views.ActivityContext import com.android.launcher3.widget.PendingAppWidgetHostView +import java.util.function.Predicate /** Interface representing a container which can bind Launcher items with some utility methods */ interface LauncherBindableItemsContainer { @@ -54,11 +56,24 @@ interface LauncherBindableItemsContainer { } mapOverItems(op) - Folder.getOpen(context)?.iterateOverItems(op) + Folder.getOpen(context)?.mapOverItems(op) } - /** Map the [op] over the shortcuts and widgets. */ - fun mapOverItems(op: ItemOperator) + /** Returns the first view, matching the [op] */ + @Deprecated("Use mapOverItems instead", ReplaceWith("mapOverItems(op)")) + fun getFirstMatch(op: ItemOperator): View? = mapOverItems(op) + + /** Finds the first icon to match one of the given matchers, from highest to lowest priority. */ + fun getFirstMatch(vararg matchers: Predicate): View? = + matchers.firstNotNullOfOrNull { mapOverItems { info, _ -> info != null && it.test(info) } } + + fun getViewByItemId(id: Int): View? = mapOverItems { info, _ -> info != null && info.id == id } + + /** + * Map the [op] over the shortcuts and widgets. Once we found the first view which matches, we + * will stop the iteration and return that view. + */ + fun mapOverItems(op: ItemOperator): View? fun interface ItemOperator { diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java index 30af58616c..49adaecff7 100644 --- a/src/com/android/launcher3/views/ActivityContext.java +++ b/src/com/android/launcher3/views/ActivityContext.java @@ -195,7 +195,7 @@ public interface ActivityContext { */ @NonNull default LauncherBindableItemsContainer getContent() { - return op -> { }; + return op -> null; } /** diff --git a/src/com/android/launcher3/views/FloatingSurfaceView.java b/src/com/android/launcher3/views/FloatingSurfaceView.java index 5f8e2c0973..a4055b6c30 100644 --- a/src/com/android/launcher3/views/FloatingSurfaceView.java +++ b/src/com/android/launcher3/views/FloatingSurfaceView.java @@ -159,9 +159,8 @@ public class FloatingSurfaceView extends AbstractFloatingView implements if (mContract == null) { return; } - View icon = mLauncher.getFirstMatchForAppClose(null /* StableViewInfo */, - mContract.componentName.getPackageName(), mContract.user, - false /* supportsAllAppsState */); + View icon = mLauncher.getFirstHomeElementForAppClose(null /* StableViewInfo */, + mContract.componentName.getPackageName(), mContract.user); boolean iconChanged = mIcon != icon; if (iconChanged) { diff --git a/tests/multivalentTests/src/com/android/launcher3/util/LauncherBindableItemsContainerTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/LauncherBindableItemsContainerTest.kt index 93be5f5125..054c90bd3b 100644 --- a/tests/multivalentTests/src/com/android/launcher3/util/LauncherBindableItemsContainerTest.kt +++ b/tests/multivalentTests/src/com/android/launcher3/util/LauncherBindableItemsContainerTest.kt @@ -122,9 +122,10 @@ class LauncherBindableItemsContainerTest { val items = mutableMapOf() - override fun mapOverItems(op: ItemOperator) { - items.forEach { (item, view) -> if (op.evaluate(item, view)) return@forEach } - } + override fun mapOverItems(op: ItemOperator): View? = + items.firstNotNullOfOrNull { (item, view) -> + if (op.evaluate(item, view)) view else null + } fun addIcon(info: WorkspaceItemInfo) { val btv = BubbleTextView(this)