diff --git a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java index 754782bd49..65cdcf06ba 100644 --- a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java +++ b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java @@ -53,14 +53,17 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory { public static final int ERROR_PERMISSIONS = 1; private static final String TAG = "TaskOverlayFactoryGo"; - // Empty constructor required for ResourceBasedOverride - public TaskOverlayFactoryGo(Context context) {} + private AssistContentRequester mContentRequester; + + public TaskOverlayFactoryGo(Context context) { + mContentRequester = new AssistContentRequester(context); + } /** * Create a new overlay instance for the given View */ public TaskOverlayGo createOverlay(TaskThumbnailView thumbnailView) { - return new TaskOverlayGo(thumbnailView); + return new TaskOverlayGo(thumbnailView, mContentRequester); } /** @@ -72,9 +75,12 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory { private String mTaskPackageName; private String mWebUrl; private boolean mAssistPermissionsEnabled; + private AssistContentRequester mFactoryContentRequester; - private TaskOverlayGo(TaskThumbnailView taskThumbnailView) { + private TaskOverlayGo(TaskThumbnailView taskThumbnailView, + AssistContentRequester assistContentRequester) { super(taskThumbnailView); + mFactoryContentRequester = assistContentRequester; } /** @@ -105,9 +111,7 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory { } int taskId = task.key.id; - AssistContentRequester contentRequester = - new AssistContentRequester(mApplicationContext); - contentRequester.requestAssistContent(taskId, this::onAssistContentReceived); + mFactoryContentRequester.requestAssistContent(taskId, this::onAssistContentReceived); } /** Provide Assist Content to the overlay. */ diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index dd63cfaa68..d9c33aeeee 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -45,8 +45,9 @@ 2.25dp - 1.75dp - 0.75dp + 1.5dp + 1dp + 5dp 16dp 70dp diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 34c08d6b2e..43d03b3cc6 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -1416,14 +1416,14 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener : APP_LAUNCH_ALPHA_DOWN_DURATION; iconAlphaStart = hasSplashScreen && !hasDifferentAppIcon ? 0 : 1f; - // TOOD: Share value from shell when available. - final float windowIconSize = Utilities.pxFromSp(108, r.getDisplayMetrics()); + final int windowIconSize = ResourceUtils.getDimenByName("starting_surface_icon_size", + r, 108); cropCenterXStart = windowTargetBounds.centerX(); cropCenterYStart = windowTargetBounds.centerY(); - cropWidthStart = (int) windowIconSize; - cropHeightStart = (int) windowIconSize; + cropWidthStart = windowIconSize; + cropHeightStart = windowIconSize; cropWidthEnd = windowTargetBounds.width(); cropHeightEnd = windowTargetBounds.height(); diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java index c6ea953aff..180af0bd9f 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java @@ -21,6 +21,7 @@ import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_BOTH import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.os.SystemClock; import android.view.MotionEvent; import android.view.View; import android.view.animation.Interpolator; @@ -72,6 +73,7 @@ public abstract class TaskViewTouchController private float mProgressMultiplier; private float mEndDisplacement; private FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck(); + private Float mOverrideVelocity = null; private TaskView mTaskBeingDragged; @@ -268,6 +270,7 @@ public abstract class TaskViewTouchController mCurrentAnimation.pause(); } mFlingBlockCheck.unblockFling(); + mOverrideVelocity = null; } @Override @@ -283,19 +286,36 @@ public abstract class TaskViewTouchController mFlingBlockCheck.onEvent(); } - // Once halfway through task dismissal interpolation, switch from reversible dragging-task - // animation to playing the remaining task translation animations - if (mCurrentAnimation.getProgressFraction() < ANIMATION_PROGRESS_FRACTION_MIDPOINT) { - // Halve the value as we are animating the drag across the full length for only the - // first half of the progress - mCurrentAnimation.setPlayFraction( - Utilities.boundToRange(totalDisplacement * mProgressMultiplier / 2, 0, 1)); + if (isGoingUp) { + if (mCurrentAnimation.getProgressFraction() < ANIMATION_PROGRESS_FRACTION_MIDPOINT) { + // Halve the value when dismissing, as we are animating the drag across the full + // length for only the first half of the progress + mCurrentAnimation.setPlayFraction( + Utilities.boundToRange(totalDisplacement * mProgressMultiplier / 2, 0, 1)); + } else { + // Set mOverrideVelocity to control task dismiss velocity in onDragEnd + int velocityDimenId = R.dimen.default_task_dismiss_drag_velocity; + if (mRecentsView.showAsGrid()) { + if (mTaskBeingDragged.isFocusedTask()) { + velocityDimenId = + R.dimen.default_task_dismiss_drag_velocity_grid_focus_task; + } else { + velocityDimenId = R.dimen.default_task_dismiss_drag_velocity_grid; + } + } + mOverrideVelocity = -mTaskBeingDragged.getResources().getDimension(velocityDimenId); + + // Once halfway through task dismissal interpolation, switch from reversible + // dragging-task animation to playing the remaining task translation animations + final long now = SystemClock.uptimeMillis(); + MotionEvent upAction = MotionEvent.obtain(now, now, + MotionEvent.ACTION_UP, 0.0f, 0.0f, 0); + mDetector.onTouchEvent(upAction); + upAction.recycle(); + } } else { - float dragVelocity = -mTaskBeingDragged.getResources().getDimension( - mRecentsView.showAsGrid() ? R.dimen.default_task_dismiss_drag_velocity_grid - : R.dimen.default_task_dismiss_drag_velocity); - onDragEnd(dragVelocity); - return true; + mCurrentAnimation.setPlayFraction( + Utilities.boundToRange(totalDisplacement * mProgressMultiplier, 0, 1)); } return true; @@ -303,6 +323,10 @@ public abstract class TaskViewTouchController @Override public void onDragEnd(float velocity) { + if (mOverrideVelocity != null) { + velocity = mOverrideVelocity; + mOverrideVelocity = null; + } // Limit velocity, as very large scalar values make animations play too quickly float maxTaskDismissDragVelocity = mTaskBeingDragged.getResources().getDimension( R.dimen.max_task_dismiss_drag_velocity); diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index be927e0107..306032cff2 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -972,6 +972,10 @@ public abstract class AbsSwipeUpHandler, } if (endTarget == HOME) { duration = HOME_DURATION; + // Early detach the nav bar once the endTarget is determined as HOME + if (mRecentsAnimationController != null) { + mRecentsAnimationController.detachNavigationBarFromApp(true); + } } else if (endTarget == RECENTS) { if (mRecentsView != null) { int nearestPage = mRecentsView.getDestinationPage(); @@ -1496,7 +1500,7 @@ public abstract class AbsSwipeUpHandler, if (LIVE_TILE.get()) { mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED); if (mRecentsAnimationController != null) { - mRecentsAnimationController.getController().detachNavigationBarFromApp(true); + mRecentsAnimationController.detachNavigationBarFromApp(true); } } else if (!hasTargets() || mRecentsAnimationController == null) { // If there are no targets or the animation not started, then there is nothing to finish diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java index 462f71440c..50d0569cf2 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java @@ -154,6 +154,14 @@ public class RecentsAnimationController { }); } + /** + * @see RecentsAnimationControllerCompat#detachNavigationBarFromApp + */ + @UiThread + public void detachNavigationBarFromApp(boolean moveHomeToTop) { + UI_HELPER_EXECUTOR.execute(() -> mController.detachNavigationBarFromApp(moveHomeToTop)); + } + /** * Sets the final surface transaction on a Task. This is used by Launcher to notify the system * that animating Activity to PiP has completed and the associated task surface should be diff --git a/quickstep/src/com/android/quickstep/util/AssistContentRequester.java b/quickstep/src/com/android/quickstep/util/AssistContentRequester.java index 71c6382323..b1e38ebcbb 100644 --- a/quickstep/src/com/android/quickstep/util/AssistContentRequester.java +++ b/quickstep/src/com/android/quickstep/util/AssistContentRequester.java @@ -54,6 +54,7 @@ public class AssistContentRequester { private final IActivityTaskManager mActivityTaskManager; private final String mPackageName; private final Executor mCallbackExecutor; + private final Executor mSystemInteractionExecutor; // If system loses the callback, our internal cache of original callback will also get cleared. private final Map mPendingCallbacks = @@ -63,6 +64,7 @@ public class AssistContentRequester { mActivityTaskManager = ActivityTaskManager.getService(); mPackageName = context.getApplicationContext().getPackageName(); mCallbackExecutor = Executors.MAIN_EXECUTOR; + mSystemInteractionExecutor = Executors.UI_HELPER_EXECUTOR; } /** @@ -71,13 +73,16 @@ public class AssistContentRequester { * @param taskId to query for the content. * @param callback to call when the content is available, called on the main thread. */ - public void requestAssistContent(int taskId, Callback callback) { - try { - mActivityTaskManager.requestAssistDataForTask( - new AssistDataReceiver(callback, this), taskId, mPackageName); - } catch (RemoteException e) { - Log.e(TAG, "Requesting assist content failed for task: " + taskId, e); - } + public void requestAssistContent(final int taskId, final Callback callback) { + // ActivityTaskManager interaction here is synchronous, so call off the main thread. + mSystemInteractionExecutor.execute(() -> { + try { + mActivityTaskManager.requestAssistDataForTask( + new AssistDataReceiver(callback, this), taskId, mPackageName); + } catch (RemoteException e) { + Log.e(TAG, "Requesting assist content failed for task: " + taskId, e); + } + }); } private void executeOnMainExecutor(Runnable callback) { diff --git a/res/color-night-v31/widgets_picker_scrim.xml b/res/color-night-v31/widgets_picker_scrim.xml new file mode 100644 index 0000000000..be7010b80b --- /dev/null +++ b/res/color-night-v31/widgets_picker_scrim.xml @@ -0,0 +1,22 @@ + + + + + diff --git a/res/color-v31/widgets_picker_scrim.xml b/res/color-v31/widgets_picker_scrim.xml new file mode 100644 index 0000000000..648824ac1e --- /dev/null +++ b/res/color-v31/widgets_picker_scrim.xml @@ -0,0 +1,22 @@ + + + + + diff --git a/res/color/widgets_picker_scrim.xml b/res/color/widgets_picker_scrim.xml new file mode 100644 index 0000000000..1cf97f61f7 --- /dev/null +++ b/res/color/widgets_picker_scrim.xml @@ -0,0 +1,22 @@ + + + + + diff --git a/res/layout/add_item_confirmation_activity.xml b/res/layout/add_item_confirmation_activity.xml index ddc98156e9..9439baf1b3 100644 --- a/res/layout/add_item_confirmation_activity.xml +++ b/res/layout/add_item_confirmation_activity.xml @@ -31,7 +31,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/add_item_dialog_background" - android:padding="24dp" + android:paddingTop="24dp" android:theme="?attr/widgetsTheme" android:layout_gravity="bottom" android:orientation="vertical"> @@ -42,6 +42,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" + android:paddingHorizontal="24dp" android:textColor="?android:attr/textColorPrimary" android:textSize="24sp" android:ellipsize="end" @@ -53,6 +54,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" + android:paddingHorizontal="24dp" android:paddingTop="8dp" android:text="@string/add_item_request_drag_hint" android:textSize="14sp" diff --git a/res/layout/widgets_full_sheet.xml b/res/layout/widgets_full_sheet.xml index a01aa2c581..dca3e79c62 100644 --- a/res/layout/widgets_full_sheet.xml +++ b/res/layout/widgets_full_sheet.xml @@ -25,8 +25,7 @@ android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="?android:attr/colorBackground" - android:elevation="4dp"> + android:background="?android:attr/colorBackground"> = 4 && ENABLE_TWO_PANEL_HOME.get(); ArrayList allOptions = getPredefinedDeviceProfiles(context, gridName, isSplitDisplay); diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java index 5dae5a672d..b4288ce1f5 100644 --- a/src/com/android/launcher3/dragndrop/AddItemActivity.java +++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java @@ -59,6 +59,7 @@ import com.android.launcher3.pm.PinRequestHelper; import com.android.launcher3.util.SystemUiController; import com.android.launcher3.views.AbstractSlideInView; import com.android.launcher3.views.BaseDragLayer; +import com.android.launcher3.widget.AddItemWidgetsBottomSheet; import com.android.launcher3.widget.LauncherAppWidgetHost; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.launcher3.widget.NavigableAppWidgetHostView; @@ -89,6 +90,7 @@ public class AddItemActivity extends BaseActivity private LauncherAppState mApp; private InvariantDeviceProfile mIdp; private BaseDragLayer mDragLayer; + private AddItemWidgetsBottomSheet mSlideInView; private WidgetCell mWidgetCell; @@ -124,8 +126,6 @@ public class AddItemActivity extends BaseActivity mDragLayer = findViewById(R.id.add_item_drag_layer); mDragLayer.recreateControllers(); mDragLayer.setInsets(mDeviceProfile.getInsets()); - AbstractSlideInView slideInView = findViewById(R.id.add_item_bottom_sheet); - slideInView.addOnCloseListener(this); mWidgetCell = findViewById(R.id.widget_cell); if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) { @@ -151,6 +151,9 @@ public class AddItemActivity extends BaseActivity TextView widgetAppName = findViewById(R.id.widget_appName); widgetAppName.setText(getApplicationInfo().labelRes); + mSlideInView = findViewById(R.id.add_item_bottom_sheet); + mSlideInView.addOnCloseListener(this); + mSlideInView.show(); setupNavBarColor(); } @@ -279,7 +282,7 @@ public class AddItemActivity extends BaseActivity */ public void onCancelClick(View v) { logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_CANCELLED); - finish(); + mSlideInView.close(/* animate= */ true); } /** @@ -290,7 +293,7 @@ public class AddItemActivity extends BaseActivity ItemInstallQueue.INSTANCE.get(this).queueItem(mRequest.getShortcutInfo()); logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_PLACED_AUTOMATICALLY); mRequest.accept(); - finish(); + mSlideInView.close(/* animate= */ true); return; } @@ -313,7 +316,7 @@ public class AddItemActivity extends BaseActivity mWidgetOptions.putInt(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId); mRequest.accept(mWidgetOptions); logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_PLACED_AUTOMATICALLY); - finish(); + mSlideInView.close(/* animate= */ true); } @Override diff --git a/src/com/android/launcher3/views/WidgetsEduView.java b/src/com/android/launcher3/views/WidgetsEduView.java index e69cb5b04b..c6fa98a456 100644 --- a/src/com/android/launcher3/views/WidgetsEduView.java +++ b/src/com/android/launcher3/views/WidgetsEduView.java @@ -35,8 +35,6 @@ public class WidgetsEduView extends AbstractSlideInView implements Ins private static final int DEFAULT_CLOSE_DURATION = 200; - protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000; - private Rect mInsets = new Rect(); private View mEduView; @@ -87,7 +85,7 @@ public class WidgetsEduView extends AbstractSlideInView implements Ins @Override protected int getScrimColor(Context context) { - return FINAL_SCRIM_BG_COLOR; + return context.getResources().getColor(R.color.widgets_picker_scrim); } @Override diff --git a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java index 9e0830309e..804973f423 100644 --- a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java @@ -23,8 +23,11 @@ import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; import android.util.AttributeSet; +import android.view.ViewGroup; +import android.view.ViewParent; import com.android.launcher3.Insettable; +import com.android.launcher3.R; import com.android.launcher3.dragndrop.AddItemActivity; import com.android.launcher3.views.AbstractSlideInView; @@ -48,6 +51,17 @@ public class AddItemWidgetsBottomSheet extends AbstractSlideInView } protected int getScrimColor(Context context) { - int alpha = context.getResources().getInteger(R.integer.extracted_color_gradient_alpha); - return setColorAlphaBound(context.getColor(R.color.wallpaper_popup_scrim), alpha); + return context.getResources().getColor(R.color.widgets_picker_scrim); } @Override diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java index 7963431932..3936ec8e80 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java @@ -25,6 +25,7 @@ import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.widget.TableRow; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.Adapter; @@ -48,8 +49,10 @@ import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.OptionalInt; import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.IntStream; /** * Recycler view adapter for the widget tray. @@ -87,6 +90,7 @@ public class WidgetsListAdapter extends Adapter implements OnHeaderC || new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user) .equals(mWidgetsContentVisiblePackageUserKey); @Nullable private Predicate mFilter = null; + @Nullable private RecyclerView mRecyclerView; public WidgetsListAdapter(Context context, LayoutInflater layoutInflater, WidgetPreviewLoader widgetPreviewLoader, IconCache iconCache, @@ -106,6 +110,16 @@ public class WidgetsListAdapter extends Adapter implements OnHeaderC layoutInflater, /*onHeaderClickListener=*/ this, /* listAdapter= */ this)); } + @Override + public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { + mRecyclerView = recyclerView; + } + + @Override + public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) { + mRecyclerView = null; + } + public void setFilter(Predicate filter) { mFilter = filter; } @@ -168,12 +182,10 @@ public class WidgetsListAdapter extends Adapter implements OnHeaderC mAllEntries.forEach(entry -> { if (entry instanceof WidgetsListHeaderEntry) { ((WidgetsListHeaderEntry) entry).setIsWidgetListShown( - new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user) - .equals(mWidgetsContentVisiblePackageUserKey)); + isHeaderForVisibleContent(entry)); } else if (entry instanceof WidgetsListSearchHeaderEntry) { ((WidgetsListSearchHeaderEntry) entry).setIsWidgetListShown( - new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user) - .equals(mWidgetsContentVisiblePackageUserKey)); + isHeaderForVisibleContent(entry)); } }); List newVisibleEntries = mAllEntries.stream() @@ -183,6 +195,13 @@ public class WidgetsListAdapter extends Adapter implements OnHeaderC mDiffReporter.process(mVisibleEntries, newVisibleEntries, mRowComparator); } + private boolean isHeaderForVisibleContent(WidgetsListBaseEntry entry) { + return (entry instanceof WidgetsListHeaderEntry + || entry instanceof WidgetsListSearchHeaderEntry) + && new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user) + .equals(mWidgetsContentVisiblePackageUserKey); + } + /** * Resets any expanded widget header. */ @@ -247,12 +266,40 @@ public class WidgetsListAdapter extends Adapter implements OnHeaderC if (showWidgets) { mWidgetsContentVisiblePackageUserKey = packageUserKey; updateVisibleEntries(); + // Scroll the layout manager to the header position to keep it anchored to the same + // position. + scrollToSelectedHeaderPosition(); } else if (packageUserKey.equals(mWidgetsContentVisiblePackageUserKey)) { mWidgetsContentVisiblePackageUserKey = null; updateVisibleEntries(); } } + private void scrollToSelectedHeaderPosition() { + OptionalInt selectedHeaderPosition = + IntStream.range(0, mVisibleEntries.size()) + .filter(index -> isHeaderForVisibleContent(mVisibleEntries.get(index))) + .findFirst(); + RecyclerView.LayoutManager layoutManager = + mRecyclerView == null ? null : mRecyclerView.getLayoutManager(); + if (!selectedHeaderPosition.isPresent() || layoutManager == null) { + return; + } + + // Scroll to the selected header position. LinearLayoutManager scrolls the minimum distance + // necessary, so this will keep the selected header in place during clicks, without + // interrupting the animation. + int position = selectedHeaderPosition.getAsInt(); + if (position == mVisibleEntries.size() - 2) { + // If the selected header is in the last position (-1 for the content), then scroll to + // the final position so the last list of widgets will show. + layoutManager.scrollToPosition(mVisibleEntries.size() - 1); + } else { + // Otherwise, scroll to the position of the selected header. + layoutManager.scrollToPosition(position); + } + } + /** * Sets the max horizontal spans that are allowed for grouping more than one widgets in a table * row. diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java index e30e245452..090362ba33 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java @@ -154,8 +154,25 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch return -1; } - View child = getChildAt(0); - int rowIndex = getChildPosition(child); + int rowIndex = -1; + View child = null; + + LayoutManager layoutManager = getLayoutManager(); + if (layoutManager instanceof LinearLayoutManager) { + // Use the LayoutManager as the source of truth for visible positions. During + // animations, the view group child may not correspond to the visible views that appear + // at the top. + rowIndex = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition(); + child = layoutManager.findViewByPosition(rowIndex); + } + + if (child == null) { + // If the layout manager returns null for any reason, which can happen before layout + // has occurred for the position, then look at the child of this view as a ViewGroup. + child = getChildAt(0); + rowIndex = getChildPosition(child); + } + for (int i = 0; i < getChildCount(); i++) { View view = getChildAt(i); if (view instanceof TableLayout) {