From 49f25681efc7f88f9ead267779acaedba6f05d63 Mon Sep 17 00:00:00 2001 From: Brian Isganitis Date: Tue, 16 Nov 2021 14:30:23 -0500 Subject: [PATCH 01/15] Make action optional for snackbar. Test: Manual Bug: 188222480 Change-Id: I923dcf50633d8ba751d86095f43e97563a53b692 --- src/com/android/launcher3/views/Snackbar.java | 56 ++++++++++++------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/src/com/android/launcher3/views/Snackbar.java b/src/com/android/launcher3/views/Snackbar.java index 49fcd2ebde..f945819c8d 100644 --- a/src/com/android/launcher3/views/Snackbar.java +++ b/src/com/android/launcher3/views/Snackbar.java @@ -28,8 +28,9 @@ import android.view.Gravity; import android.view.MotionEvent; import android.widget.TextView; +import androidx.annotation.Nullable; + import com.android.launcher3.AbstractFloatingView; -import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.R; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.compat.AccessibilityManagerCompat; @@ -44,7 +45,7 @@ public class Snackbar extends AbstractFloatingView { private static final long HIDE_DURATION_MS = 180; private static final int TIMEOUT_DURATION_MS = 4000; - private final BaseDraggingActivity mActivity; + private final ActivityContext mActivity; private Runnable mOnDismissed; public Snackbar(Context context, AttributeSet attrs) { @@ -53,12 +54,19 @@ public class Snackbar extends AbstractFloatingView { public Snackbar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - mActivity = BaseDraggingActivity.fromContext(context); + mActivity = ActivityContext.lookupContext(context); inflate(context, R.layout.snackbar, this); } - public static void show(BaseDraggingActivity activity, int labelStringResId, - int actionStringResId, Runnable onDismissed, Runnable onActionClicked) { + /** Show a snackbar with just a label. */ + public static void show(T activity, int labelStringRedId, + Runnable onDismissed) { + show(activity, labelStringRedId, NO_ID, onDismissed, null); + } + + /** Show a snackbar with a label and action. */ + public static void show(T activity, int labelStringResId, + int actionStringResId, Runnable onDismissed, @Nullable Runnable onActionClicked) { closeOpenViews(activity, true, TYPE_SNACKBAR); Snackbar snackbar = new Snackbar(activity, null); // Set some properties here since inflated xml only contains the children. @@ -87,13 +95,30 @@ public class Snackbar extends AbstractFloatingView { params.setMargins(0, 0, 0, marginBottom + insets.bottom); TextView labelView = snackbar.findViewById(R.id.label); - TextView actionView = snackbar.findViewById(R.id.action); String labelText = res.getString(labelStringResId); - String actionText = res.getString(actionStringResId); - int totalContentWidth = (int) (labelView.getPaint().measureText(labelText) - + actionView.getPaint().measureText(actionText)) + labelView.setText(labelText); + + TextView actionView = snackbar.findViewById(R.id.action); + float actionWidth; + if (actionStringResId != NO_ID) { + String actionText = res.getString(actionStringResId); + actionWidth = actionView.getPaint().measureText(actionText) + + actionView.getPaddingRight() + actionView.getPaddingLeft(); + actionView.setText(actionText); + actionView.setOnClickListener(v -> { + if (onActionClicked != null) { + onActionClicked.run(); + } + snackbar.mOnDismissed = null; + snackbar.close(true); + }); + } else { + actionWidth = 0; + actionView.setVisibility(GONE); + } + + int totalContentWidth = (int) (labelView.getPaint().measureText(labelText) + actionWidth) + labelView.getPaddingRight() + labelView.getPaddingLeft() - + actionView.getPaddingRight() + actionView.getPaddingLeft() + padding * 2; if (totalContentWidth > params.width) { // The text doesn't fit in our standard width so update width to accommodate. @@ -113,17 +138,8 @@ public class Snackbar extends AbstractFloatingView { params.width = maxWidth; } } - labelView.setText(labelText); - actionView.setText(actionText); - actionView.setOnClickListener(v -> { - if (onActionClicked != null) { - onActionClicked.run(); - } - snackbar.mOnDismissed = null; - snackbar.close(true); - }); - snackbar.mOnDismissed = onDismissed; + snackbar.mOnDismissed = onDismissed; snackbar.setAlpha(0); snackbar.setScaleX(0.8f); snackbar.setScaleY(0.8f); From bdb0dd8cfbc3d576c444892e53be2460178a61a9 Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Wed, 3 Nov 2021 16:26:41 -0700 Subject: [PATCH 02/15] Refactor LauncherAccessibilityDelegate so it can be used outside of Launcher Bug: 198438631 Test: used talkback on launcher Change-Id: I991320184ad93816c4ba21fb8fcfe12202bfae25 --- .../QuickstepAccessibilityDelegate.java | 2 +- src/com/android/launcher3/BubbleTextView.java | 4 +- src/com/android/launcher3/Launcher.java | 2 +- .../BaseAccessibilityDelegate.java | 192 ++++++++++++++++ .../LauncherAccessibilityDelegate.java | 206 +++--------------- .../ShortcutMenuAccessibilityDelegate.java | 8 +- .../WorkspaceAccessibilityHelper.java | 2 +- 7 files changed, 233 insertions(+), 183 deletions(-) create mode 100644 src/com/android/launcher3/accessibility/BaseAccessibilityDelegate.java diff --git a/quickstep/src/com/android/launcher3/QuickstepAccessibilityDelegate.java b/quickstep/src/com/android/launcher3/QuickstepAccessibilityDelegate.java index 96559cbb8a..962fd91c2e 100644 --- a/quickstep/src/com/android/launcher3/QuickstepAccessibilityDelegate.java +++ b/quickstep/src/com/android/launcher3/QuickstepAccessibilityDelegate.java @@ -44,7 +44,7 @@ public class QuickstepAccessibilityDelegate extends LauncherAccessibilityDelegat @Override protected boolean performAction(View host, ItemInfo item, int action, boolean fromKeyboard) { - QuickstepLauncher launcher = (QuickstepLauncher) mLauncher; + QuickstepLauncher launcher = (QuickstepLauncher) mContext; if (action == PIN_PREDICTION) { if (launcher.getHotseatPredictionController() == null) { return false; diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 5a6365adf9..f01696517b 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -48,7 +48,7 @@ import android.widget.TextView; import androidx.annotation.Nullable; import androidx.annotation.UiThread; -import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; +import com.android.launcher3.accessibility.BaseAccessibilityDelegate; import com.android.launcher3.dot.DotInfo; import com.android.launcher3.dragndrop.DraggableView; import com.android.launcher3.folder.FolderIcon; @@ -296,7 +296,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, @Override public void setAccessibilityDelegate(AccessibilityDelegate delegate) { - if (delegate instanceof LauncherAccessibilityDelegate) { + if (delegate instanceof BaseAccessibilityDelegate) { super.setAccessibilityDelegate(delegate); } else { // NO-OP diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 0656125c90..ab9b980aa3 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -115,8 +115,8 @@ import androidx.annotation.StringRes; import androidx.annotation.VisibleForTesting; import com.android.launcher3.DropTarget.DragObject; +import com.android.launcher3.accessibility.BaseAccessibilityDelegate.LauncherAction; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; -import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.LauncherAction; import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsStore; import com.android.launcher3.allapps.AllAppsTransitionController; diff --git a/src/com/android/launcher3/accessibility/BaseAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/BaseAccessibilityDelegate.java new file mode 100644 index 0000000000..14b2431662 --- /dev/null +++ b/src/com/android/launcher3/accessibility/BaseAccessibilityDelegate.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.accessibility; + +import android.content.Context; +import android.graphics.Rect; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.SparseArray; +import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; + +import com.android.launcher3.DropTarget; +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.dragndrop.DragController; +import com.android.launcher3.dragndrop.DragOptions; +import com.android.launcher3.model.data.FolderInfo; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.LauncherAppWidgetInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.popup.PopupContainerWithArrow; +import com.android.launcher3.util.Thunk; +import com.android.launcher3.views.ActivityContext; + +import java.util.ArrayList; +import java.util.List; + +public abstract class BaseAccessibilityDelegate + extends View.AccessibilityDelegate implements DragController.DragListener { + + public enum DragType { + ICON, + FOLDER, + WIDGET + } + + public static class DragInfo { + public DragType dragType; + public ItemInfo info; + public View item; + } + + protected final SparseArray mActions = new SparseArray<>(); + protected final T mContext; + + protected DragInfo mDragInfo = null; + + protected BaseAccessibilityDelegate(T context) { + mContext = context; + } + + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + if (host.getTag() instanceof ItemInfo) { + ItemInfo item = (ItemInfo) host.getTag(); + + List actions = new ArrayList<>(); + getSupportedActions(host, item, actions); + actions.forEach(la -> info.addAction(la.accessibilityAction)); + + if (!itemSupportsLongClick(host, item)) { + info.setLongClickable(false); + info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK); + } + } + } + + /** + * Adds all the accessibility actions that can be handled. + */ + protected abstract void getSupportedActions(View host, ItemInfo item, List out); + + private boolean itemSupportsLongClick(View host, ItemInfo info) { + return PopupContainerWithArrow.canShow(host, info); + } + + protected boolean itemSupportsAccessibleDrag(ItemInfo item) { + if (item instanceof WorkspaceItemInfo) { + // Support the action unless the item is in a context menu. + return item.screenId >= 0 + && item.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; + } + return (item instanceof LauncherAppWidgetInfo) + || (item instanceof FolderInfo); + } + + @Override + public boolean performAccessibilityAction(View host, int action, Bundle args) { + if ((host.getTag() instanceof ItemInfo) + && performAction(host, (ItemInfo) host.getTag(), action, false)) { + return true; + } + return super.performAccessibilityAction(host, action, args); + } + + protected abstract boolean performAction( + View host, ItemInfo item, int action, boolean fromKeyboard); + + @Thunk + protected void announceConfirmation(String confirmation) { + mContext.getDragLayer().announceForAccessibility(confirmation); + + } + + public boolean isInAccessibleDrag() { + return mDragInfo != null; + } + + public DragInfo getDragInfo() { + return mDragInfo; + } + + /** + * @param clickedTarget the actual view that was clicked + * @param dropLocation relative to {@param clickedTarget}. If provided, its center is used + * as the actual drop location otherwise the views center is used. + */ + public void handleAccessibleDrop(View clickedTarget, Rect dropLocation, + String confirmation) { + if (!isInAccessibleDrag()) return; + + int[] loc = new int[2]; + if (dropLocation == null) { + loc[0] = clickedTarget.getWidth() / 2; + loc[1] = clickedTarget.getHeight() / 2; + } else { + loc[0] = dropLocation.centerX(); + loc[1] = dropLocation.centerY(); + } + + mContext.getDragLayer().getDescendantCoordRelativeToSelf(clickedTarget, loc); + mContext.getDragController().completeAccessibleDrag(loc); + + if (!TextUtils.isEmpty(confirmation)) { + announceConfirmation(confirmation); + } + } + + protected abstract boolean beginAccessibleDrag(View item, ItemInfo info, boolean fromKeyboard); + + + @Override + public void onDragEnd() { + mContext.getDragController().removeDragListener(this); + mDragInfo = null; + } + + @Override + public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { + // No-op + } + + public class LauncherAction { + public final int keyCode; + public final AccessibilityNodeInfo.AccessibilityAction accessibilityAction; + + private final BaseAccessibilityDelegate mDelegate; + + public LauncherAction(int id, int labelRes, int keyCode) { + this.keyCode = keyCode; + accessibilityAction = new AccessibilityNodeInfo.AccessibilityAction( + id, mContext.getString(labelRes)); + mDelegate = BaseAccessibilityDelegate.this; + } + + /** + * Invokes the action for the provided host + */ + public boolean invokeFromKeyboard(View host) { + if (host != null && host.getTag() instanceof ItemInfo) { + return mDelegate.performAction( + host, (ItemInfo) host.getTag(), accessibilityAction.getId(), true); + } else { + return false; + } + } + } +} diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index 157df5d070..18c05ebd43 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -10,27 +10,19 @@ import android.appwidget.AppWidgetProviderInfo; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; -import android.os.Bundle; import android.os.Handler; -import android.text.TextUtils; import android.util.Log; -import android.util.SparseArray; import android.view.KeyEvent; import android.view.View; -import android.view.View.AccessibilityDelegate; -import android.view.accessibility.AccessibilityNodeInfo; -import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import com.android.launcher3.BubbleTextView; import com.android.launcher3.ButtonDropTarget; import com.android.launcher3.CellLayout; -import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.PendingAddItemInfo; import com.android.launcher3.R; import com.android.launcher3.Workspace; -import com.android.launcher3.dragndrop.DragController.DragListener; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.folder.Folder; @@ -57,7 +49,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -public class LauncherAccessibilityDelegate extends AccessibilityDelegate implements DragListener { +public class LauncherAccessibilityDelegate extends BaseAccessibilityDelegate { private static final String TAG = "LauncherAccessibilityDelegate"; @@ -73,25 +65,8 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme public static final int DEEP_SHORTCUTS = R.id.action_deep_shortcuts; public static final int SHORTCUTS_AND_NOTIFICATIONS = R.id.action_shortcuts_and_notifications; - public enum DragType { - ICON, - FOLDER, - WIDGET - } - - public static class DragInfo { - public DragType dragType; - public ItemInfo info; - public View item; - } - - protected final SparseArray mActions = new SparseArray<>(); - protected final Launcher mLauncher; - - private DragInfo mDragInfo = null; - public LauncherAccessibilityDelegate(Launcher launcher) { - mLauncher = launcher; + super(launcher); mActions.put(REMOVE, new LauncherAction( REMOVE, R.string.remove_drop_target_label, KeyEvent.KEYCODE_X)); @@ -116,25 +91,6 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme } @Override - public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(host, info); - if (host.getTag() instanceof ItemInfo) { - ItemInfo item = (ItemInfo) host.getTag(); - - List actions = new ArrayList<>(); - getSupportedActions(host, item, actions); - actions.forEach(la -> info.addAction(la.accessibilityAction)); - - if (!itemSupportsLongClick(host, item)) { - info.setLongClickable(false); - info.removeAction(AccessibilityAction.ACTION_LONG_CLICK); - } - } - } - - /** - * Adds all the accessibility actions that can be handled. - */ protected void getSupportedActions(View host, ItemInfo item, List out) { // If the request came from keyboard, do not add custom shortcuts as that is already // exposed as a direct shortcut @@ -143,7 +99,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme ? SHORTCUTS_AND_NOTIFICATIONS : DEEP_SHORTCUTS)); } - for (ButtonDropTarget target : mLauncher.getDropTargetBar().getDropTargets()) { + for (ButtonDropTarget target : mContext.getDropTargetBar().getDropTargets()) { if (target.supportsAccessibilityDrop(item, host)) { out.add(mActions.get(target.getAccessibilityAction())); } @@ -183,31 +139,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme return result; } - private boolean itemSupportsLongClick(View host, ItemInfo info) { - return PopupContainerWithArrow.canShow(host, info); - } - - private boolean itemSupportsAccessibleDrag(ItemInfo item) { - if (item instanceof WorkspaceItemInfo) { - // Support the action unless the item is in a context menu. - return item.screenId >= 0 && item.container != Favorites.CONTAINER_HOTSEAT_PREDICTION; - } - return (item instanceof LauncherAppWidgetInfo) - || (item instanceof FolderInfo); - } - @Override - public boolean performAccessibilityAction(View host, int action, Bundle args) { - if ((host.getTag() instanceof ItemInfo) - && performAction(host, (ItemInfo) host.getTag(), action, false)) { - return true; - } - return super.performAccessibilityAction(host, action, args); - } - - /** - * Performs the provided action on the host - */ protected boolean performAction(final View host, final ItemInfo item, int action, boolean fromKeyboard) { if (action == ACTION_LONG_CLICK) { @@ -226,36 +158,36 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme if (screenId == -1) { return false; } - mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> { + mContext.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> { if (item instanceof AppInfo) { WorkspaceItemInfo info = ((AppInfo) item).makeWorkspaceItem(); - mLauncher.getModelWriter().addItemToDatabase(info, + mContext.getModelWriter().addItemToDatabase(info, Favorites.CONTAINER_DESKTOP, screenId, coordinates[0], coordinates[1]); - mLauncher.bindItems( + mContext.bindItems( Collections.singletonList(info), /* forceAnimateIcons= */ true, /* focusFirstItemForAccessibility= */ true); announceConfirmation(R.string.item_added_to_workspace); } else if (item instanceof PendingAddItemInfo) { PendingAddItemInfo info = (PendingAddItemInfo) item; - Workspace workspace = mLauncher.getWorkspace(); + Workspace workspace = mContext.getWorkspace(); workspace.snapToPage(workspace.getPageIndexForScreenId(screenId)); - mLauncher.addPendingItem(info, Favorites.CONTAINER_DESKTOP, + mContext.addPendingItem(info, Favorites.CONTAINER_DESKTOP, screenId, coordinates, info.spanX, info.spanY); } else if (item instanceof WorkspaceItemInfo) { WorkspaceItemInfo info = ((WorkspaceItemInfo) item).clone(); - mLauncher.getModelWriter().addItemToDatabase(info, + mContext.getModelWriter().addItemToDatabase(info, Favorites.CONTAINER_DESKTOP, screenId, coordinates[0], coordinates[1]); - mLauncher.bindItems(Collections.singletonList(info), true, true); + mContext.bindItems(Collections.singletonList(info), true, true); } })); return true; } else if (action == MOVE_TO_WORKSPACE) { - Folder folder = Folder.getOpen(mLauncher); + Folder folder = Folder.getOpen(mContext); folder.close(true); WorkspaceItemInfo info = (WorkspaceItemInfo) item; folder.getInfo().remove(info, false); @@ -265,14 +197,14 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme if (screenId == -1) { return false; } - mLauncher.getModelWriter().moveItemInDatabase(info, + mContext.getModelWriter().moveItemInDatabase(info, Favorites.CONTAINER_DESKTOP, screenId, coordinates[0], coordinates[1]); // Bind the item in next frame so that if a new workspace page was created, // it will get laid out. new Handler().post(() -> { - mLauncher.bindItems(Collections.singletonList(item), true); + mContext.bindItems(Collections.singletonList(item), true); announceConfirmation(R.string.item_moved); }); return true; @@ -280,15 +212,15 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) item; List actions = getSupportedResizeActions(host, info); Rect pos = new Rect(); - mLauncher.getDragLayer().getDescendantRectRelativeToSelf(host, pos); - ArrowPopup popup = OptionsPopupView.show(mLauncher, new RectF(pos), actions, false); + mContext.getDragLayer().getDescendantRectRelativeToSelf(host, pos); + ArrowPopup popup = OptionsPopupView.show(mContext, new RectF(pos), actions, false); popup.requestFocus(); popup.setOnCloseCallback(host::requestFocus); return true; } else if (action == DEEP_SHORTCUTS || action == SHORTCUTS_AND_NOTIFICATIONS) { return PopupContainerWithArrow.showForIcon((BubbleTextView) host) != null; } else { - for (ButtonDropTarget dropTarget : mLauncher.getDropTargetBar().getDropTargets()) { + for (ButtonDropTarget dropTarget : mContext.getDropTargetBar().getDropTargets()) { if (dropTarget.supportsAccessibilityDrop(item, host) && action == dropTarget.getAccessibilityAction()) { dropTarget.onAccessibilityDrop(host, item); @@ -315,7 +247,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0) { if (layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY) || layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY)) { - actions.add(new OptionItem(mLauncher, + actions.add(new OptionItem(mContext, R.string.action_increase_width, R.drawable.ic_widget_width_increase, IGNORE, @@ -323,7 +255,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme } if (info.spanX > info.minSpanX && info.spanX > 1) { - actions.add(new OptionItem(mLauncher, + actions.add(new OptionItem(mContext, R.string.action_decrease_width, R.drawable.ic_widget_width_decrease, IGNORE, @@ -334,7 +266,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) { if (layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1) || layout.isRegionVacant(info.cellX, info.cellY - 1, info.spanX, 1)) { - actions.add(new OptionItem(mLauncher, + actions.add(new OptionItem(mContext, R.string.action_increase_height, R.drawable.ic_widget_height_increase, IGNORE, @@ -342,7 +274,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme } if (info.spanY > info.minSpanY && info.spanY > 1) { - actions.add(new OptionItem(mLauncher, + actions.add(new OptionItem(mContext, R.string.action_decrease_height, R.drawable.ic_widget_height_decrease, IGNORE, @@ -382,58 +314,20 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme } layout.markCellsAsOccupiedForView(host); - WidgetSizes.updateWidgetSizeRanges(((LauncherAppWidgetHostView) host), mLauncher, + WidgetSizes.updateWidgetSizeRanges(((LauncherAppWidgetHostView) host), mContext, info.spanX, info.spanY); host.requestLayout(); - mLauncher.getModelWriter().updateItemInDatabase(info); - announceConfirmation(mLauncher.getString(R.string.widget_resized, info.spanX, info.spanY)); + mContext.getModelWriter().updateItemInDatabase(info); + announceConfirmation(mContext.getString(R.string.widget_resized, info.spanX, info.spanY)); return true; } @Thunk void announceConfirmation(int resId) { - announceConfirmation(mLauncher.getResources().getString(resId)); + announceConfirmation(mContext.getResources().getString(resId)); } - @Thunk void announceConfirmation(String confirmation) { - mLauncher.getDragLayer().announceForAccessibility(confirmation); - - } - - public boolean isInAccessibleDrag() { - return mDragInfo != null; - } - - public DragInfo getDragInfo() { - return mDragInfo; - } - - /** - * @param clickedTarget the actual view that was clicked - * @param dropLocation relative to {@param clickedTarget}. If provided, its center is used - * as the actual drop location otherwise the views center is used. - */ - public void handleAccessibleDrop(View clickedTarget, Rect dropLocation, - String confirmation) { - if (!isInAccessibleDrag()) return; - - int[] loc = new int[2]; - if (dropLocation == null) { - loc[0] = clickedTarget.getWidth() / 2; - loc[1] = clickedTarget.getHeight() / 2; - } else { - loc[0] = dropLocation.centerX(); - loc[1] = dropLocation.centerY(); - } - - mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(clickedTarget, loc); - mLauncher.getDragController().completeAccessibleDrag(loc); - - if (!TextUtils.isEmpty(confirmation)) { - announceConfirmation(confirmation); - } - } - - private boolean beginAccessibleDrag(View item, ItemInfo info, boolean fromKeyboard) { + @Override + protected boolean beginAccessibleDrag(View item, ItemInfo info, boolean fromKeyboard) { if (!itemSupportsAccessibleDrag(info)) { return false; } @@ -449,8 +343,8 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme } Rect pos = new Rect(); - mLauncher.getDragLayer().getDescendantRectRelativeToSelf(item, pos); - mLauncher.getDragController().addDragListener(this); + mContext.getDragLayer().getDescendantRectRelativeToSelf(item, pos); + mContext.getDragController().addDragListener(this); DragOptions options = new DragOptions(); options.isAccessibleDrag = true; @@ -458,31 +352,20 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme options.simulatedDndStartPoint = new Point(pos.centerX(), pos.centerY()); if (fromKeyboard) { - KeyboardDragAndDropView popup = (KeyboardDragAndDropView) mLauncher.getLayoutInflater() - .inflate(R.layout.keyboard_drag_and_drop, mLauncher.getDragLayer(), false); + KeyboardDragAndDropView popup = (KeyboardDragAndDropView) mContext.getLayoutInflater() + .inflate(R.layout.keyboard_drag_and_drop, mContext.getDragLayer(), false); popup.showForIcon(item, info, options); } else { - ItemLongClickListener.beginDrag(item, mLauncher, info, options); + ItemLongClickListener.beginDrag(item, mContext, info, options); } return true; } - @Override - public void onDragStart(DragObject dragObject, DragOptions options) { - // No-op - } - - @Override - public void onDragEnd() { - mLauncher.getDragController().removeDragListener(this); - mDragInfo = null; - } - /** * Find empty space on the workspace and returns the screenId. */ protected int findSpaceOnWorkspace(ItemInfo info, int[] outCoordinates) { - Workspace workspace = mLauncher.getWorkspace(); + Workspace workspace = mContext.getWorkspace(); IntArray workspaceScreens = workspace.getScreenOrder(); int screenId; @@ -520,29 +403,4 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme } return screenId; } - - public class LauncherAction { - public final int keyCode; - public final AccessibilityAction accessibilityAction; - - private final LauncherAccessibilityDelegate mDelegate; - - public LauncherAction(int id, int labelRes, int keyCode) { - this.keyCode = keyCode; - accessibilityAction = new AccessibilityAction(id, mLauncher.getString(labelRes)); - mDelegate = LauncherAccessibilityDelegate.this; - } - - /** - * Invokes the action for the provided host - */ - public boolean invokeFromKeyboard(View host) { - if (host != null && host.getTag() instanceof ItemInfo) { - return mDelegate.performAction( - host, (ItemInfo) host.getTag(), accessibilityAction.getId(), true); - } else { - return false; - } - } - } } diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java index bf5a24b65b..fb847ec9ae 100644 --- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java @@ -71,12 +71,12 @@ public class ShortcutMenuAccessibilityDelegate extends LauncherAccessibilityDele if (screenId == -1) { return false; } - mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> { - mLauncher.getModelWriter().addItemToDatabase(info, + mContext.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> { + mContext.getModelWriter().addItemToDatabase(info, LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId, coordinates[0], coordinates[1]); - mLauncher.bindItems(Collections.singletonList(info), true); - AbstractFloatingView.closeAllOpenViews(mLauncher); + mContext.bindItems(Collections.singletonList(info), true); + AbstractFloatingView.closeAllOpenViews(mContext); announceConfirmation(R.string.item_added_to_workspace); })); return true; diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java index a331924f22..a8624dd17d 100644 --- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java +++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java @@ -22,7 +22,7 @@ import android.view.View; import com.android.launcher3.CellLayout; import com.android.launcher3.R; -import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DragType; +import com.android.launcher3.accessibility.BaseAccessibilityDelegate.DragType; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; From 4e678ff6d5bb6c12ab8e2a76c00600eb61de897d Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Thu, 11 Nov 2021 21:10:03 +0000 Subject: [PATCH 03/15] Separate getTaskViewAt usage getTaskViewAt is used in two different contexts with different assumptions: 1. In the context of iterating over the all TaskViews, where the valid tasks indices are know. 2. In the context of requesting some TaskView by index, where the caller expects null when the input index is invalid. The nullability status of the method differs in these contexts and therefore getTaskViewAt usage can be separated into two different methods. Bug: 205828770 Test: m LauncherGoResLib Change-Id: I42c04c115c309f1849f9dfbb05c74b9b080acf13 (cherry picked from commit 4da3b4bdddbbff7a0dee1f57b3a74c616231e100) --- .../android/quickstep/views/RecentsView.java | 84 +++++++++++-------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 3aa8d4637b..e270cb7c1c 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -107,6 +107,7 @@ import android.widget.ListView; import android.widget.OverScroller; import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.core.graphics.ColorUtils; @@ -181,6 +182,7 @@ import com.android.wm.shell.pip.IPipAnimationListener; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Objects; import java.util.function.Consumer; /** @@ -822,7 +824,7 @@ public abstract class RecentsView 0) { - targetPage = indexOfChild(getTaskViewAt(0)); + targetPage = indexOfChild(requireTaskViewAt(0)); } } else if (currentTaskId != -1) { currentTaskView = getTaskViewByTaskId(currentTaskId); @@ -1452,7 +1454,7 @@ public abstract class RecentsView= 0; i--) { - removeView(getTaskViewAt(i)); + removeView(requireTaskViewAt(i)); } if (indexOfChild(mClearAllButton) != -1) { removeView(mClearAllButton); @@ -1498,7 +1500,7 @@ public abstract class RecentsView= 0; i--) { - TaskView taskView = getTaskViewAt(i); + TaskView taskView = requireTaskViewAt(i); if (mIgnoreResetTaskId != taskView.getTaskIds()[0]) { taskView.resetViewTransforms(); taskView.setIconScaleAndDim(mTaskIconScaledDown ? 0 : 1); @@ -1529,7 +1531,7 @@ public abstract class RecentsView 0 && mTopRowIdSet.size() >= (taskCount - 1) / 2f; // Pick the next focused task from the preferred row. for (int i = 0; i < taskCount; i++) { - TaskView taskView = getTaskViewAt(i); + TaskView taskView = requireTaskViewAt(i); if (taskView == dismissedTaskView) { continue; } @@ -2816,7 +2818,7 @@ public abstract class RecentsView= 0; i--) { - TaskView child = getTaskViewAt(i); + TaskView child = requireTaskViewAt(i); int[] childTaskIds = child.getTaskIds(); if (!mRunningTaskTileHidden || (childTaskIds[0] != runningTaskId && childTaskIds[1] != runningTaskId)) { @@ -3542,6 +3544,14 @@ public abstract class RecentsView 0) { - final View taskView = getTaskViewAt(0); - getTaskViewAt(count - 1).getHitRect(mTaskViewDeadZoneRect); + final View taskView = requireTaskViewAt(0); + requireTaskViewAt(count - 1).getHitRect(mTaskViewDeadZoneRect); mTaskViewDeadZoneRect.union(taskView.getLeft(), taskView.getTop(), taskView.getRight(), taskView.getBottom()); } @@ -4548,7 +4558,7 @@ public abstract class RecentsView Date: Wed, 23 Jun 2021 15:16:39 -0700 Subject: [PATCH 04/15] [DO NOT MERGE] Make the cutout slightly smaller Temporary fix for S. Leaving the bug open for a proper fix in T. Since we reverted the z order back (launcher on top), we should apply this fix from S and find a proper solution in T Bug: 189265196 Test: https://screenshot.googleplex.com/75fgCva736rqwg5 Change-Id: Ice3b189a41f759e090334e360e44543eabc4836d (cherry picked from commit 26f47c711ad94e4f9f7daef7ea45b35b2e51f53f) --- .../android/quickstep/views/TaskThumbnailView.java | 14 ++++++++++++-- .../src/com/android/quickstep/views/TaskView.java | 3 +++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java index d91669ad96..da92551526 100644 --- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java +++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java @@ -19,6 +19,7 @@ package com.android.quickstep.views; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; +import static com.android.launcher3.Utilities.comp; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN; @@ -290,8 +291,17 @@ public class TaskThumbnailView extends View { float cornerRadius) { if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { if (mTask != null && getTaskView().isRunningTask() && !getTaskView().showScreenshot()) { - canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mClearPaint); - canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, + // TODO(b/189265196): Temporary fix to align the surface with the cutout perfectly. + // Round up only when the live tile task is displayed in Overview. + float rounding = comp(mFullscreenParams.mFullscreenProgress); + float left = x + rounding / 2; + float top = y + rounding / 2; + float right = width - rounding; + float bottom = height - rounding; + + canvas.drawRoundRect(left, top, right, bottom, cornerRadius, cornerRadius, + mClearPaint); + canvas.drawRoundRect(left, top, right, bottom, cornerRadius, cornerRadius, mDimmingPaintAfterClearing); return; } diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index e8077cf4a5..b122e7c3a2 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -1531,6 +1531,7 @@ public class TaskView extends FrameLayout implements Reusable { private final float mCornerRadius; private final float mWindowCornerRadius; + public float mFullscreenProgress; public RectF mCurrentDrawnInsets = new RectF(); public float mCurrentDrawnCornerRadius; /** The current scale we apply to the thumbnail to adjust for new left/right insets. */ @@ -1548,6 +1549,8 @@ public class TaskView extends FrameLayout implements Reusable { */ public void setProgress(float fullscreenProgress, float parentScale, float taskViewScale, int previewWidth, DeviceProfile dp, PreviewPositionHelper pph) { + mFullscreenProgress = fullscreenProgress; + RectF insets = pph.getInsetsToDrawInFullscreen(dp); float currentInsetsLeft = insets.left * fullscreenProgress; From 8176366f54e713b253426e165c7982544f28c413 Mon Sep 17 00:00:00 2001 From: Jerry Chang Date: Tue, 30 Nov 2021 17:15:40 +0800 Subject: [PATCH 05/15] Remove stage type from split screen APIs Bug: 198438631 Test: manual check Change-Id: Ic4f41e39013e4d49585da4cda9b28151c549357a --- quickstep/src/com/android/quickstep/SystemUiProxy.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index 2543e6c594..5fd66d82eb 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -593,11 +593,11 @@ public class SystemUiProxy implements ISystemUiProxy, } } - public void startShortcut(String packageName, String shortcutId, int stage, int position, + public void startShortcut(String packageName, String shortcutId, int position, Bundle options, UserHandle user) { if (mSplitScreen != null) { try { - mSplitScreen.startShortcut(packageName, shortcutId, stage, position, options, + mSplitScreen.startShortcut(packageName, shortcutId, position, options, user); } catch (RemoteException e) { Log.w(TAG, "Failed call startShortcut"); @@ -605,11 +605,11 @@ public class SystemUiProxy implements ISystemUiProxy, } } - public void startIntent(PendingIntent intent, Intent fillInIntent, int stage, int position, + public void startIntent(PendingIntent intent, Intent fillInIntent, int position, Bundle options) { if (mSplitScreen != null) { try { - mSplitScreen.startIntent(intent, fillInIntent, stage, position, options); + mSplitScreen.startIntent(intent, fillInIntent, position, options); } catch (RemoteException e) { Log.w(TAG, "Failed call startIntent"); } From 53ae5d85a6cb056ffd191e6b382ebed3e2dd95c2 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Wed, 24 Nov 2021 08:54:25 -0800 Subject: [PATCH 06/15] Tapl Widgets: Don't use container width as gesture margin The test used margin that was equal to the width of the container when injecting a backward swipe. This means the swipe was injected at an x value of 0 in the View's local coordinates. When injecting such events into the system, the coordinates will undergo several transformations, at which point floating point precision errors could accumulate. This could result in the event being dispatched outside the boundary of the view. Change the injection margin so that the swipe is injected in the middle of the View instead of at its edge. Bug: 201777251 Bug: 207146693 Test: atest TaplTestsLauncher3 Change-Id: I4082c2845aef033a6fbe41070061866d6048e21d --- tests/tapl/com/android/launcher3/tapl/Widgets.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java index 6e7264ac0b..0bac2ca258 100644 --- a/tests/tapl/com/android/launcher3/tapl/Widgets.java +++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java @@ -77,7 +77,8 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer { mLauncher.scroll( widgetsContainer, Direction.UP, - new Rect(0, 0, mLauncher.getVisibleBounds(widgetsContainer).width(), 0), + new Rect(0, 0, mLauncher.getRightGestureMarginInContainer(widgetsContainer) + 1, + 0), FLING_STEPS, false); try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) { verifyActiveContainer(); From 5e91cbe0691c29ae077c9b5159f919a3e0e29d21 Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Wed, 10 Nov 2021 13:18:56 -0800 Subject: [PATCH 07/15] Add shortcut drag/drop support to the taskbar. - Added support for shortcut drag/drop - Added support for popup menu shortcut drag/drop Test: long pressed taskbar and launcher icons. long pressed taskbar and launcher shortcuts and popup menu shortcuts. Fixes: 204453506 Bug: 198438631 Change-Id: I09cab335dcbb3a2bfa3020b21f4bcffff9c53e61 --- .../taskbar/TaskbarDragController.java | 39 ++++++++++++-- .../taskbar/TaskbarPopupController.java | 52 ++++++++++++++++++- .../popup/LauncherPopupLiveUpdateHandler.java | 6 +++ .../popup/PopupContainerWithArrow.java | 4 ++ .../popup/PopupLiveUpdateHandler.java | 7 ++- .../ShortcutDragPreviewProvider.java | 13 ++--- 6 files changed, 107 insertions(+), 14 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java index b3a9f8db7c..3315f8cad2 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java @@ -51,6 +51,8 @@ import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.PopupContainerWithArrow; +import com.android.launcher3.shortcuts.DeepShortcutView; +import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ClipDescriptionCompat; import com.android.systemui.shared.system.LauncherAppsCompat; @@ -88,6 +90,21 @@ public class TaskbarDragController extends DragController { - startInternalDrag(btv); + DragView dragView = startInternalDrag(btv, dragPreviewProvider); + if (iconShift != null) { + dragView.animateShift(-iconShift.x, -iconShift.y); + } btv.getIcon().setIsDisabled(true); mControllers.taskbarAutohideSuspendController.updateFlag( TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING, true); @@ -104,7 +124,8 @@ public class TaskbarDragController extends DragController(mContext, container)); + new PopupLiveUpdateHandler(mContext, container) { + @Override + protected void showPopupContainerForIcon(BubbleTextView originalIcon) { + showForIcon(originalIcon); + } + }); // TODO (b/198438631): configure for taskbar/context + container.setPopupItemDragHandler(new TaskbarPopupItemDragHandler()); container.populateAndShow(icon, mPopupDataProvider.getShortcutCountForItem(item), @@ -145,4 +156,43 @@ public class TaskbarPopupController { container.requestFocus(); return container; } + + private class TaskbarPopupItemDragHandler implements + PopupContainerWithArrow.PopupItemDragHandler { + + protected final Point mIconLastTouchPos = new Point(); + + TaskbarPopupItemDragHandler() {} + + @Override + public boolean onTouch(View view, MotionEvent ev) { + // Touched a shortcut, update where it was touched so we can drag from there on + // long click. + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_MOVE: + mIconLastTouchPos.set((int) ev.getX(), (int) ev.getY()); + break; + } + return false; + } + + @Override + public boolean onLongClick(View v) { + // Return early if not the correct view + if (!(v.getParent() instanceof DeepShortcutView)) return false; + + DeepShortcutView sv = (DeepShortcutView) v.getParent(); + sv.setWillDrawIcon(false); + + // Move the icon to align with the center-top of the touch point + Point iconShift = new Point(); + iconShift.x = mIconLastTouchPos.x - sv.getIconCenter().x; + iconShift.y = mIconLastTouchPos.y - mContext.getDeviceProfile().iconSizePx; + + mControllers.taskbarDragController.startDragOnLongClick(sv, iconShift); + + return false; + } + } } diff --git a/src/com/android/launcher3/popup/LauncherPopupLiveUpdateHandler.java b/src/com/android/launcher3/popup/LauncherPopupLiveUpdateHandler.java index 731f439d6c..3e3f633567 100644 --- a/src/com/android/launcher3/popup/LauncherPopupLiveUpdateHandler.java +++ b/src/com/android/launcher3/popup/LauncherPopupLiveUpdateHandler.java @@ -18,6 +18,7 @@ package com.android.launcher3.popup; import android.view.View; import android.view.ViewGroup; +import com.android.launcher3.BubbleTextView; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.model.data.ItemInfo; @@ -86,4 +87,9 @@ public class LauncherPopupLiveUpdateHandler extends PopupLiveUpdateHandler }; } + public void setPopupItemDragHandler(PopupItemDragHandler popupItemDragHandler) { + mPopupItemDragHandler = popupItemDragHandler; + } + public PopupItemDragHandler getItemDragHandler() { return mPopupItemDragHandler; } diff --git a/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java b/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java index 194c22faf0..c5d545225c 100644 --- a/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java +++ b/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java @@ -20,6 +20,7 @@ import static android.view.View.GONE; import android.content.Context; import android.view.View; +import com.android.launcher3.BubbleTextView; import com.android.launcher3.dot.DotInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.notification.NotificationContainer; @@ -36,7 +37,7 @@ import java.util.function.Predicate; * * @param The activity on which the popup shows */ -public class PopupLiveUpdateHandler implements +public abstract class PopupLiveUpdateHandler implements PopupDataProvider.PopupDataChangeListener, View.OnAttachStateChangeListener { protected final T mContext; @@ -103,6 +104,8 @@ public class PopupLiveUpdateHandler impleme @Override public void onSystemShortcutsUpdated() { mPopupContainerWithArrow.close(true); - PopupContainerWithArrow.showForIcon(mPopupContainerWithArrow.getOriginalIcon()); + showPopupContainerForIcon(mPopupContainerWithArrow.getOriginalIcon()); } + + protected abstract void showPopupContainerForIcon(BubbleTextView originalIcon); } diff --git a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java index cecbb0db13..c166bfc83a 100644 --- a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java +++ b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java @@ -23,12 +23,12 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.view.View; -import com.android.launcher3.Launcher; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.DragPreviewProvider; import com.android.launcher3.icons.BitmapRenderer; import com.android.launcher3.icons.FastBitmapDrawable; +import com.android.launcher3.views.ActivityContext; /** * Extension of {@link DragPreviewProvider} which generates bitmaps scaled to the default icon size. @@ -45,7 +45,8 @@ public class ShortcutDragPreviewProvider extends DragPreviewProvider { @Override public Drawable createDrawable() { if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) { - int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx; + int size = ActivityContext.lookupContext(mView.getContext()) + .getDeviceProfile().iconSizePx; return new FastBitmapDrawable( BitmapRenderer.createHardwareBitmap( size + blurSizeOutline, @@ -59,7 +60,7 @@ public class ShortcutDragPreviewProvider extends DragPreviewProvider { private Bitmap createDragBitmapLegacy() { Drawable d = mView.getBackground(); Rect bounds = getDrawableBounds(d); - int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx; + int size = ActivityContext.lookupContext(mView.getContext()).getDeviceProfile().iconSizePx; final Bitmap b = Bitmap.createBitmap( size + blurSizeOutline, size + blurSizeOutline, @@ -84,9 +85,9 @@ public class ShortcutDragPreviewProvider extends DragPreviewProvider { @Override public float getScaleAndPosition(Drawable preview, int[] outPos) { - Launcher launcher = Launcher.getLauncher(mView.getContext()); + ActivityContext context = ActivityContext.lookupContext(mView.getContext()); int iconSize = getDrawableBounds(mView.getBackground()).width(); - float scale = launcher.getDragLayer().getLocationInDragLayer(mView, outPos); + float scale = context.getDragLayer().getLocationInDragLayer(mView, outPos); int iconLeft = mView.getPaddingStart(); if (Utilities.isRtl(mView.getResources())) { @@ -98,7 +99,7 @@ public class ShortcutDragPreviewProvider extends DragPreviewProvider { + mPositionShift.x); outPos[1] += Math.round((scale * mView.getHeight() - preview.getIntrinsicHeight()) / 2 + mPositionShift.y); - float size = launcher.getDeviceProfile().iconSizePx; + float size = context.getDeviceProfile().iconSizePx; return scale * iconSize / size; } } From f6c28a4381e53fe1f2dbb08d6b4d0f8ed98f4a09 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Tue, 30 Nov 2021 14:37:36 -0800 Subject: [PATCH 08/15] Fix potential memory leak by setting mControllers = null Test: N/A Bug: 202511986 Change-Id: Iad7cc89e0e68ce09ebe4ca3ab89392b50b6a82c7 --- .../launcher3/taskbar/FallbackTaskbarUIController.java | 1 + .../launcher3/taskbar/LauncherTaskbarUIController.java | 1 + .../android/launcher3/taskbar/TaskbarUIController.java | 8 +++++++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java index 90c035fed0..f1e67479f5 100644 --- a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java @@ -60,6 +60,7 @@ public class FallbackTaskbarUIController extends TaskbarUIController { @Override protected void onDestroy() { + super.onDestroy(); mRecentsActivity.setTaskbarUIController(null); mRecentsActivity.getStateManager().removeStateListener(mStateListener); } diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index 7d234392c4..2622700871 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -99,6 +99,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController { @Override protected void onDestroy() { + super.onDestroy(); onLauncherResumedOrPaused(false); mTaskbarLauncherStateController.onDestroy(); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java index f713dcabcb..f6bc785a14 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java @@ -18,6 +18,8 @@ package com.android.launcher3.taskbar; import android.graphics.Rect; import android.view.View; +import androidx.annotation.CallSuper; + import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.WorkspaceItemInfo; @@ -33,11 +35,15 @@ public class TaskbarUIController { // Initialized in init. protected TaskbarControllers mControllers; + @CallSuper protected void init(TaskbarControllers taskbarControllers) { mControllers = taskbarControllers; } - protected void onDestroy() { } + @CallSuper + protected void onDestroy() { + mControllers = null; + } protected boolean isTaskbarTouchable() { return true; From 570653346f6c2bbde5eeaf650741b1b573f90a8a Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Wed, 24 Nov 2021 19:54:07 -0800 Subject: [PATCH 09/15] Add task unpinning support for 3 button taskbar Bug: 199544447 Test: Tested on small and large screen Change-Id: Ib7785992ef11825cd07a929e2cb623d02ef246f1 --- .../taskbar/NavbarButtonsViewController.java | 8 +- .../taskbar/TaskbarActivityContext.java | 1 + .../launcher3/taskbar/TaskbarManager.java | 5 +- .../taskbar/TaskbarNavButtonController.java | 99 +++++++++-- .../TaskbarNavButtonControllerTest.java | 159 ++++++++++++++++++ 5 files changed, 255 insertions(+), 17 deletions(-) create mode 100644 quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java index 0ab775694a..ce1e8b6b6e 100644 --- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java @@ -31,6 +31,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_I import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; @@ -86,6 +87,7 @@ public class NavbarButtonsViewController { private static final int FLAG_DISABLE_RECENTS = 1 << 8; private static final int FLAG_DISABLE_BACK = 1 << 9; private static final int FLAG_NOTIFICATION_SHADE_EXPANDED = 1 << 10; + private static final int FLAG_SCREEN_PINNING_ACTIVE = 1 << 10; private static final int MASK_IME_SWITCHER_VISIBLE = FLAG_SWITCHER_SUPPORTED | FLAG_IME_VISIBLE; @@ -152,7 +154,9 @@ public class NavbarButtonsViewController { mPropertyHolders.add(new StatePropertyHolder( mControllers.taskbarViewController.getTaskbarIconAlpha() .getProperty(ALPHA_INDEX_KEYGUARD), - flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0, MultiValueAlpha.VALUE, 1, 0)); + flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 + && (flags & FLAG_SCREEN_PINNING_ACTIVE) == 0, + MultiValueAlpha.VALUE, 1, 0)); mPropertyHolders.add(new StatePropertyHolder(mControllers.taskbarDragLayerController .getKeyguardBgTaskbar(), @@ -286,6 +290,7 @@ public class NavbarButtonsViewController { int shadeExpandedFlags = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED | SYSUI_STATE_QUICK_SETTINGS_EXPANDED; boolean isNotificationShadeExpanded = (sysUiStateFlags & shadeExpandedFlags) != 0; + boolean isScreenPinningActive = (sysUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0; // TODO(b/202218289) we're getting IME as not visible on lockscreen from system updateStateForFlag(FLAG_IME_VISIBLE, isImeVisible); @@ -295,6 +300,7 @@ public class NavbarButtonsViewController { updateStateForFlag(FLAG_DISABLE_RECENTS, isRecentsDisabled); updateStateForFlag(FLAG_DISABLE_BACK, isBackDisabled); updateStateForFlag(FLAG_NOTIFICATION_SHADE_EXPANDED, isNotificationShadeExpanded); + updateStateForFlag(FLAG_SCREEN_PINNING_ACTIVE, isScreenPinningActive); if (mA11yButton != null) { // Only used in 3 button diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index cb9d4a4374..692352b3fd 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -361,6 +361,7 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ mControllers.taskbarStashController.updateStateForSysuiFlags(systemUiStateFlags, fromInit); mControllers.taskbarScrimViewController.updateStateForSysuiFlags(systemUiStateFlags, fromInit); + mControllers.navButtonController.updateSysuiFlags(systemUiStateFlags); } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java index 56e9429120..3cdcdf7f89 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java @@ -29,8 +29,8 @@ import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.hardware.display.DisplayManager; import android.net.Uri; +import android.os.Handler; import android.provider.Settings; -import android.util.Log; import android.view.Display; import androidx.annotation.NonNull; @@ -93,7 +93,8 @@ public class TaskbarManager implements DisplayController.DisplayInfoChangeListen Display display = service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY); mContext = service.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null); - mNavButtonController = new TaskbarNavButtonController(service); + mNavButtonController = new TaskbarNavButtonController(service, + SystemUiProxy.INSTANCE.get(mContext), new Handler()); mUserSetupCompleteListener = isUserSetupComplete -> recreateTaskbar(); mComponentCallbacks = new ComponentCallbacks() { private Configuration mOldConfig = mContext.getResources().getConfiguration(); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java index ae23eda2a4..d23336505a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java @@ -19,8 +19,10 @@ package com.android.launcher3.taskbar; import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS; import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_KEY; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; import android.os.Bundle; +import android.os.Handler; import androidx.annotation.IntDef; @@ -40,6 +42,13 @@ import java.lang.annotation.RetentionPolicy; */ public class TaskbarNavButtonController { + /** Allow some time in between the long press for back and recents. */ + static final int SCREEN_PIN_LONG_PRESS_THRESHOLD = 200; + static final int SCREEN_PIN_LONG_PRESS_RESET = SCREEN_PIN_LONG_PRESS_THRESHOLD + 100; + + private long mLastScreenPinLongPress; + private boolean mScreenPinned; + @Retention(RetentionPolicy.SOURCE) @IntDef(value = { BUTTON_BACK, @@ -57,10 +66,20 @@ public class TaskbarNavButtonController { static final int BUTTON_IME_SWITCH = BUTTON_RECENTS << 1; static final int BUTTON_A11Y = BUTTON_IME_SWITCH << 1; - private final TouchInteractionService mService; + private static final int SCREEN_UNPIN_COMBO = BUTTON_BACK | BUTTON_RECENTS; + private int mLongPressedButtons = 0; - public TaskbarNavButtonController(TouchInteractionService service) { + private final TouchInteractionService mService; + private final SystemUiProxy mSystemUiProxy; + private final Handler mHandler; + + private final Runnable mResetLongPress = this::resetScreenUnpin; + + public TaskbarNavButtonController(TouchInteractionService service, + SystemUiProxy systemUiProxy, Handler handler) { mService = service; + mSystemUiProxy = systemUiProxy; + mHandler = handler; } public void onButtonClick(@TaskbarButton int buttonType) { @@ -72,13 +91,13 @@ public class TaskbarNavButtonController { navigateHome(); break; case BUTTON_RECENTS: - navigateToOverview();; + navigateToOverview(); break; case BUTTON_IME_SWITCH: showIMESwitcher(); break; case BUTTON_A11Y: - notifyImeClick(false /* longClick */); + notifyA11yClick(false /* longClick */); break; } } @@ -89,46 +108,98 @@ public class TaskbarNavButtonController { startAssistant(); return true; case BUTTON_A11Y: - notifyImeClick(true /* longClick */); + notifyA11yClick(true /* longClick */); return true; case BUTTON_BACK: - case BUTTON_IME_SWITCH: case BUTTON_RECENTS: + mLongPressedButtons |= buttonType; + return determineScreenUnpin(); + case BUTTON_IME_SWITCH: default: return false; } } + /** + * Checks if the user has long pressed back and recents buttons + * "together" (within {@link #SCREEN_PIN_LONG_PRESS_THRESHOLD})ms + * If so, then requests the system to turn off screen pinning. + * + * @return true if the long press is a valid user action in attempting to unpin an app + * Will always return {@code false} when screen pinning is not active. + * NOTE: Returning true does not mean that screen pinning has stopped + */ + private boolean determineScreenUnpin() { + long timeNow = System.currentTimeMillis(); + if (!mScreenPinned) { + return false; + } + + if (mLastScreenPinLongPress == 0) { + // First button long press registered, just mark time and wait for second button press + mLastScreenPinLongPress = System.currentTimeMillis(); + mHandler.postDelayed(mResetLongPress, SCREEN_PIN_LONG_PRESS_RESET); + return true; + } + + if ((timeNow - mLastScreenPinLongPress) > SCREEN_PIN_LONG_PRESS_THRESHOLD) { + // Too long in-between presses, reset the clock + resetScreenUnpin(); + return false; + } + + if ((mLongPressedButtons & SCREEN_UNPIN_COMBO) == SCREEN_UNPIN_COMBO) { + // Hooray! They did it (finally...) + mSystemUiProxy.stopScreenPinning(); + mHandler.removeCallbacks(mResetLongPress); + resetScreenUnpin(); + } + return true; + } + + private void resetScreenUnpin() { + mLongPressedButtons = 0; + mLastScreenPinLongPress = 0; + } + + public void updateSysuiFlags(int sysuiFlags) { + mScreenPinned = (sysuiFlags & SYSUI_STATE_SCREEN_PINNING) != 0; + } + private void navigateHome() { mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_HOME); } private void navigateToOverview() { + if (mScreenPinned) { + return; + } TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle"); mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_TOGGLE); } private void executeBack() { - SystemUiProxy.INSTANCE.getNoCreate().onBackPressed(); + mSystemUiProxy.onBackPressed(); } private void showIMESwitcher() { - SystemUiProxy.INSTANCE.getNoCreate().onImeSwitcherPressed(); + mSystemUiProxy.onImeSwitcherPressed(); } - private void notifyImeClick(boolean longClick) { - SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate(); + private void notifyA11yClick(boolean longClick) { if (longClick) { - systemUiProxy.notifyAccessibilityButtonLongClicked(); + mSystemUiProxy.notifyAccessibilityButtonLongClicked(); } else { - systemUiProxy.notifyAccessibilityButtonClicked(mService.getDisplayId()); + mSystemUiProxy.notifyAccessibilityButtonClicked(mService.getDisplayId()); } } private void startAssistant() { + if (mScreenPinned) { + return; + } Bundle args = new Bundle(); args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS); - SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate(); - systemUiProxy.startAssistant(args); + mSystemUiProxy.startAssistant(args); } } diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java new file mode 100644 index 0000000000..ba1a60dd38 --- /dev/null +++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java @@ -0,0 +1,159 @@ +package com.android.launcher3.taskbar; + +import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y; +import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK; +import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME; +import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH; +import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS; +import static com.android.launcher3.taskbar.TaskbarNavButtonController.SCREEN_PIN_LONG_PRESS_THRESHOLD; +import static com.android.quickstep.OverviewCommandHelper.TYPE_HOME; +import static com.android.quickstep.OverviewCommandHelper.TYPE_TOGGLE; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.os.Handler; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.quickstep.OverviewCommandHelper; +import com.android.quickstep.SystemUiProxy; +import com.android.quickstep.TouchInteractionService; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +public class TaskbarNavButtonControllerTest { + + private final static int DISPLAY_ID = 2; + + @Mock + SystemUiProxy mockSystemUiProxy; + @Mock + TouchInteractionService mockService; + @Mock + OverviewCommandHelper mockCommandHelper; + @Mock + Handler mockHandler; + + private TaskbarNavButtonController mNavButtonController; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + when(mockService.getDisplayId()).thenReturn(DISPLAY_ID); + when(mockService.getOverviewCommandHelper()).thenReturn(mockCommandHelper); + mNavButtonController = new TaskbarNavButtonController(mockService, + mockSystemUiProxy, mockHandler); + } + + @Test + public void testPressBack() { + mNavButtonController.onButtonClick(BUTTON_BACK); + verify(mockSystemUiProxy, times(1)).onBackPressed(); + } + + @Test + public void testPressImeSwitcher() { + mNavButtonController.onButtonClick(BUTTON_IME_SWITCH); + verify(mockSystemUiProxy, times(1)).onImeSwitcherPressed(); + } + + @Test + public void testPressA11yShortClick() { + mNavButtonController.onButtonClick(BUTTON_A11Y); + verify(mockSystemUiProxy, times(1)) + .notifyAccessibilityButtonClicked(DISPLAY_ID); + } + + @Test + public void testPressA11yLongClick() { + mNavButtonController.onButtonLongClick(BUTTON_A11Y); + verify(mockSystemUiProxy, times(1)).notifyAccessibilityButtonLongClicked(); + } + + @Test + public void testLongPressHome() { + mNavButtonController.onButtonLongClick(BUTTON_HOME); + verify(mockSystemUiProxy, times(1)).startAssistant(any()); + } + + @Test + public void testPressHome() { + mNavButtonController.onButtonClick(BUTTON_HOME); + verify(mockCommandHelper, times(1)).addCommand(TYPE_HOME); + } + + @Test + public void testPressRecents() { + mNavButtonController.onButtonClick(BUTTON_RECENTS); + verify(mockCommandHelper, times(1)).addCommand(TYPE_TOGGLE); + } + + @Test + public void testPressRecentsWithScreenPinned() { + mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING); + mNavButtonController.onButtonClick(BUTTON_RECENTS); + verify(mockCommandHelper, times(0)).addCommand(TYPE_TOGGLE); + } + + @Test + public void testLongPressBackRecentsNotPinned() { + mNavButtonController.onButtonLongClick(BUTTON_RECENTS); + mNavButtonController.onButtonLongClick(BUTTON_BACK); + verify(mockSystemUiProxy, times(0)).stopScreenPinning(); + } + + @Test + public void testLongPressBackRecentsPinned() { + mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING); + mNavButtonController.onButtonLongClick(BUTTON_RECENTS); + mNavButtonController.onButtonLongClick(BUTTON_BACK); + verify(mockSystemUiProxy, times(1)).stopScreenPinning(); + } + + @Test + public void testLongPressBackRecentsTooLongPinned() { + mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING); + mNavButtonController.onButtonLongClick(BUTTON_RECENTS); + try { + Thread.sleep(SCREEN_PIN_LONG_PRESS_THRESHOLD + 5); + } catch (InterruptedException e) { + e.printStackTrace(); + } + mNavButtonController.onButtonLongClick(BUTTON_BACK); + verify(mockSystemUiProxy, times(0)).stopScreenPinning(); + } + + @Test + public void testLongPressBackRecentsMultipleAttemptPinned() { + mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING); + mNavButtonController.onButtonLongClick(BUTTON_RECENTS); + try { + Thread.sleep(SCREEN_PIN_LONG_PRESS_THRESHOLD + 5); + } catch (InterruptedException e) { + e.printStackTrace(); + } + mNavButtonController.onButtonLongClick(BUTTON_BACK); + verify(mockSystemUiProxy, times(0)).stopScreenPinning(); + + // Try again w/in threshold + mNavButtonController.onButtonLongClick(BUTTON_RECENTS); + mNavButtonController.onButtonLongClick(BUTTON_BACK); + verify(mockSystemUiProxy, times(1)).stopScreenPinning(); + } + + @Test + public void testLongPressHomeScreenPinned() { + mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING); + mNavButtonController.onButtonLongClick(BUTTON_HOME); + verify(mockSystemUiProxy, times(0)).startAssistant(any()); + } +} From b997930afcec95289c845030eb5c381cc99b4190 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Tue, 30 Nov 2021 19:58:00 -0800 Subject: [PATCH 10/15] Treat RTL split placeholder animation same as LTR * Since we're doing all transformations in screen coordinates we don't need to account for RTL. Bug: 202156862 Change-Id: Ibcbf698dbc5b8fabf2647949de6d2718937832d5 --- .../android/quickstep/views/FloatingTaskView.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java index 325ec044fe..a343e0a593 100644 --- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java +++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java @@ -129,9 +129,7 @@ public class FloatingTaskView extends FrameLayout { public void update(RectF position, float progress, float windowRadius) { MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams(); - float dX = mIsRtl - ? position.left - (lp.getMarginStart() - lp.width) - : position.left - lp.getMarginStart(); + float dX = position.left - lp.getMarginStart(); float dY = position.top - lp.topMargin; setTranslationX(dX); @@ -157,16 +155,10 @@ public class FloatingTaskView extends FrameLayout { lp.ignoreInsets = true; // Position the floating view exactly on top of the original lp.topMargin = Math.round(pos.top); - if (mIsRtl) { - lp.setMarginStart(Math.round(mLauncher.getDeviceProfile().widthPx - pos.right)); - } else { - lp.setMarginStart(Math.round(pos.left)); - } + lp.setMarginStart(Math.round(pos.left)); // Set the properties here already to make sure they are available when running the first // animation frame. - int left = mIsRtl - ? mLauncher.getDeviceProfile().widthPx - lp.getMarginStart() - lp.width - : lp.leftMargin; + int left = lp.leftMargin; layout(left, lp.topMargin, left + lp.width, lp.topMargin + lp.height); } From 09a822a6fe32a53cb4378177f920f31a051f89b6 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Fri, 26 Nov 2021 11:44:05 -0800 Subject: [PATCH 11/15] Schedule OverviewCommandHelper callbacks for GroupedTaskView * Schedule runnables to clear pending queue in OverviewCommandHelper similar to how TaskView does it. * End callbacks get run when recents animation finishes in RecentsView in the case of live tile when recents animation is still running OR in directly when the split remote animation finishes Bug: 207845542 Change-Id: I7e858ce55b08cde6436d44f2e857e28b73458f0b --- .../android/quickstep/views/GroupedTaskView.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java index 00f541d4c6..b43626b50e 100644 --- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java +++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java @@ -157,10 +157,20 @@ public class GroupedTaskView extends TaskView { @Nullable @Override public RunnableList launchTaskAnimated() { - getRecentsView().getSplitPlaceholder().launchTasks(this /*groupedTaskView*/, - null /*callback*/, + if (mTask == null || mSecondaryTask == null) { + return null; + } + + RunnableList endCallback = new RunnableList(); + RecentsView recentsView = getRecentsView(); + // Callbacks run from remote animation when recents animation not currently running + recentsView.getSplitPlaceholder().launchTasks(this /*groupedTaskView*/, + success -> endCallback.executeAllAndDestroy(), false /* freezeTaskList */); - return null; + + // Callbacks get run from recentsView for case when recents animation already running + recentsView.addSideTaskLaunchCallback(endCallback); + return endCallback; } @Override From a3fe68dcb107425a7f2cf5978c48f5076a738d35 Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Thu, 4 Nov 2021 14:24:39 -0700 Subject: [PATCH 12/15] Implement AccessibilityDelegateImpl for the Taskbar - Added accessibility actions to open pop up menu - Added accessibility actions to open an app/shortcut to the top/left or bottom/right Fixes: 204453506 Bug: 198438631 Test: attempted all actions individually, attempted combinations of actions (eg. open pop up menu -> move to left/right) Change-Id: I76a4237035a0ebfe88b8b5f147b574bb2629f20c --- quickstep/res/values/config.xml | 4 + quickstep/res/values/strings.xml | 5 + .../taskbar/TaskbarActivityContext.java | 15 +++ ...kbarShortcutMenuAccessibilityDelegate.java | 117 ++++++++++++++++++ 4 files changed, 141 insertions(+) create mode 100644 quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml index 31c0f5f2ab..3a4bb1086f 100644 --- a/quickstep/res/values/config.xml +++ b/quickstep/res/values/config.xml @@ -39,4 +39,8 @@ 23 + + + + diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml index 151b8e4604..70a4a7d5f7 100644 --- a/quickstep/res/values/strings.xml +++ b/quickstep/res/values/strings.xml @@ -233,4 +233,9 @@ Close Done + + + Move to top/left + + Move to bottom/right diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 33d65333df..883515e4b1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -53,6 +53,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.BubbleTextView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.R; @@ -113,6 +114,8 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ // The flag to know if the window is excluded from magnification region computation. private boolean mIsExcludeFromMagnificationRegion = false; + private final TaskbarShortcutMenuAccessibilityDelegate mAccessibilityDelegate; + public TaskbarActivityContext(Context windowContext, DeviceProfile dp, TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider unfoldTransitionProgressProvider) { @@ -148,6 +151,8 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ mLeftCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT); mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT); + mAccessibilityDelegate = new TaskbarShortcutMenuAccessibilityDelegate(this); + // Construct controllers. mControllers = new TaskbarControllers(this, new TaskbarDragController(this), @@ -333,6 +338,11 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ return mControllers.taskbarPopupController.getPopupDataProvider(); } + @Override + public View.AccessibilityDelegate getAccessibilityDelegate() { + return mAccessibilityDelegate; + } + /** * Sets a new data-source for this taskbar instance */ @@ -564,4 +574,9 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ } mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams); } + + public void showPopupMenuForIcon(BubbleTextView btv) { + setTaskbarWindowFullscreen(true); + btv.post(() -> mControllers.taskbarPopupController.showForIcon(btv)); + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java new file mode 100644 index 0000000000..158958217f --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.taskbar; + +import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DEEP_SHORTCUTS; +import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.SHORTCUTS_AND_NOTIFICATIONS; +import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; +import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; + +import android.content.Intent; +import android.content.pm.LauncherApps; +import android.view.KeyEvent; +import android.view.View; + +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.R; +import com.android.launcher3.accessibility.BaseAccessibilityDelegate; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.notification.NotificationListener; +import com.android.launcher3.util.ShortcutUtil; +import com.android.quickstep.SystemUiProxy; +import com.android.systemui.shared.system.LauncherAppsCompat; + +import java.util.List; + +/** + * Accessibility delegate for the Taskbar. This provides an accessible interface for taskbar + * features. + */ +public class TaskbarShortcutMenuAccessibilityDelegate + extends BaseAccessibilityDelegate { + + public static final int MOVE_TO_TOP_OR_LEFT = R.id.action_move_to_top_or_left; + public static final int MOVE_TO_BOTTOM_OR_RIGHT = R.id.action_move_to_bottom_or_right; + + private final LauncherApps mLauncherApps; + + public TaskbarShortcutMenuAccessibilityDelegate(TaskbarActivityContext context) { + super(context); + mLauncherApps = context.getSystemService(LauncherApps.class); + + mActions.put(DEEP_SHORTCUTS, new LauncherAction(DEEP_SHORTCUTS, + R.string.action_deep_shortcut, KeyEvent.KEYCODE_S)); + mActions.put(SHORTCUTS_AND_NOTIFICATIONS, new LauncherAction(DEEP_SHORTCUTS, + R.string.shortcuts_menu_with_notifications_description, KeyEvent.KEYCODE_S)); + mActions.put(MOVE_TO_TOP_OR_LEFT, new LauncherAction( + MOVE_TO_TOP_OR_LEFT, R.string.move_drop_target_top_or_left, KeyEvent.KEYCODE_L)); + mActions.put(MOVE_TO_BOTTOM_OR_RIGHT, new LauncherAction( + MOVE_TO_BOTTOM_OR_RIGHT, + R.string.move_drop_target_bottom_or_right, + KeyEvent.KEYCODE_R)); + } + + @Override + protected void getSupportedActions(View host, ItemInfo item, List out) { + if (ShortcutUtil.supportsShortcuts(item) && FeatureFlags.ENABLE_TASKBAR_POPUP_MENU.get()) { + out.add(mActions.get(NotificationListener.getInstanceIfConnected() != null + ? SHORTCUTS_AND_NOTIFICATIONS : DEEP_SHORTCUTS)); + } + out.add(mActions.get(MOVE_TO_TOP_OR_LEFT)); + out.add(mActions.get(MOVE_TO_BOTTOM_OR_RIGHT)); + } + + @Override + protected boolean performAction(View host, ItemInfo item, int action, boolean fromKeyboard) { + if (item instanceof WorkspaceItemInfo + && (action == MOVE_TO_TOP_OR_LEFT || action == MOVE_TO_BOTTOM_OR_RIGHT)) { + WorkspaceItemInfo info = (WorkspaceItemInfo) item; + int side = action == MOVE_TO_TOP_OR_LEFT + ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT; + + if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + SystemUiProxy.INSTANCE.get(mContext).startShortcut( + info.getIntent().getPackage(), + info.getDeepShortcutId(), + side, + /* bundleOpts= */ null, + info.user); + } else { + SystemUiProxy.INSTANCE.get(mContext).startIntent( + LauncherAppsCompat.getMainActivityLaunchIntent( + mLauncherApps, + item.getIntent().getComponent(), + /* startActivityOptions= */null, + item.user), + new Intent(), side, null); + } + return true; + } else if (action == DEEP_SHORTCUTS || action == SHORTCUTS_AND_NOTIFICATIONS) { + mContext.showPopupMenuForIcon((BubbleTextView) host); + + return true; + } + return false; + } + + @Override + protected boolean beginAccessibleDrag(View item, ItemInfo info, boolean fromKeyboard) { + return false; + } +} From a30749252542988e0dc1d11ddc2cbbf85bc2c32e Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Wed, 1 Dec 2021 15:52:32 +0000 Subject: [PATCH 13/15] Removed onBackPressed handling in SplitSCreenSelectState - So it'll have exact same behavior as OverviewState to exit overview when going back - Also removed RecentsView.cancelSplitSelect as it's longer needed Fix: 181707736 Test: manual Change-Id: Ide4dfc64680ecc9adfe245ae2de1463735b0490e --- .../RecentsViewStateController.java | 1 - .../states/SplitScreenSelectState.java | 5 - .../android/quickstep/views/RecentsView.java | 106 ------------------ 3 files changed, 112 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java index b21d677106..771657b11d 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java @@ -92,7 +92,6 @@ public final class RecentsViewStateController extends builder.add(mRecentsView.createSplitSelectInitAnimation().buildAnim()); mRecentsView.applySplitPrimaryScrollOffset(); } else if (!isSplitSelectionState(toState) && isSplitSelectionState(currentState)) { - builder.add(mRecentsView.cancelSplitSelect(true).buildAnim()); mRecentsView.resetSplitPrimaryScrollOffset(); } diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java index 106375a765..4f5f27a2c0 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java @@ -30,11 +30,6 @@ public class SplitScreenSelectState extends OverviewState { super(id); } - @Override - public void onBackPressed(Launcher launcher) { - launcher.getStateManager().goToState(OVERVIEW); - } - @Override public int getVisibleElements(Launcher launcher) { return SPLIT_PLACHOLDER_VIEW; diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 9cf76b378a..3020dd905d 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -1123,9 +1123,6 @@ public abstract class RecentsView view.getVisibility() != GONE && view != mSplitHiddenTaskView); - - int[] newScroll = new int[getChildCount()]; - getPageScrolls(newScroll, false, SIMPLE_SCROLL_LOGIC); - - boolean needsCurveUpdates = false; - for (int i = mSplitHiddenTaskViewIndex; i >= 0; i--) { - View child = getChildAt(i); - if (child == mSplitHiddenTaskView) { - TaskView taskView = (TaskView) child; - - int dir = mOrientationHandler.getSplitTaskViewDismissDirection(stagePosition, - mActivity.getDeviceProfile()); - FloatProperty dismissingTaskViewTranslate; - Rect hiddenBounds = new Rect(taskView.getLeft(), taskView.getTop(), - taskView.getRight(), taskView.getBottom()); - int distanceDelta = 0; - if (dir == PagedOrientationHandler.SPLIT_TRANSLATE_SECONDARY_NEGATIVE) { - dismissingTaskViewTranslate = taskView - .getSecondaryDissmissTranslationProperty(); - distanceDelta = initialBounds.top - hiddenBounds.top; - taskView.layout(initialBounds.left, hiddenBounds.top, initialBounds.right, - hiddenBounds.bottom); - } else { - dismissingTaskViewTranslate = taskView - .getPrimaryDismissTranslationProperty(); - distanceDelta = initialBounds.left - hiddenBounds.left; - taskView.layout(hiddenBounds.left, initialBounds.top, hiddenBounds.right, - initialBounds.bottom); - if (dir == PagedOrientationHandler.SPLIT_TRANSLATE_PRIMARY_POSITIVE) { - distanceDelta *= -1; - } - } - pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView, - dismissingTaskViewTranslate, - distanceDelta)); - pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView, ALPHA, 1)); - } else { - // If insertion is on last index (furthest from clear all), we directly add the view - // else we translate all views to the right of insertion index further right, - // ignore views to left - if (showAsGrid()) { - // TODO(b/186800707) handle more elegantly for grid - continue; - } - int scrollDiff = newScroll[i] - oldScroll[i]; - if (scrollDiff != 0) { - FloatProperty translationProperty = child instanceof TaskView - ? ((TaskView) child).getPrimaryDismissTranslationProperty() - : mOrientationHandler.getPrimaryViewTranslate(); - - ResourceProvider rp = DynamicResource.provider(mActivity); - SpringProperty sp = new SpringProperty(SpringProperty.FLAG_CAN_SPRING_ON_END) - .setDampingRatio( - rp.getFloat(R.dimen.dismiss_task_trans_x_damping_ratio)) - .setStiffness(rp.getFloat(R.dimen.dismiss_task_trans_x_stiffness)); - pendingAnim.add(ObjectAnimator.ofFloat(child, translationProperty, scrollDiff) - .setDuration(duration), ACCEL, sp); - needsCurveUpdates = true; - } - } - } - - if (needsCurveUpdates) { - pendingAnim.addOnFrameCallback(this::updateCurveProperties); - } - - pendingAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - // TODO(b/186800707) Figure out how to undo for grid view - // Need to handle cases where dismissed task is - // * Top Row - // * Bottom Row - // * Focused Task - updateGridProperties(); - resetFromSplitSelectionState(); - updateScrollSynchronously(); - } - }); - - return pendingAnim; - } - /** TODO(b/181707736) More gracefully handle exiting split selection state */ private void resetFromSplitSelectionState() { if (!mActivity.getDeviceProfile().overviewShowAsGrid) { From 4536c2fc303847fdc29a39837e069f0d01a26afa Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Wed, 1 Dec 2021 17:23:46 +0000 Subject: [PATCH 14/15] Apply/reset split scroll offset regardless of previous state - Also do the same in setState without animation Fix: 208605204 Test: Exit split screen and enter overview Change-Id: I34dd102527dffa90925b6f0fd22465f1fe6e819c --- .../uioverrides/RecentsViewStateController.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java index 771657b11d..19897a1bf8 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java @@ -66,6 +66,12 @@ public final class RecentsViewStateController extends // In Overview, we may be layering app surfaces behind Launcher, so we need to notify // DepthController to prevent optimizations which might occlude the layers behind mLauncher.getDepthController().setHasContentBehindLauncher(state.overviewUi); + + if (isSplitSelectionState(state)) { + mRecentsView.applySplitPrimaryScrollOffset(); + } else { + mRecentsView.resetSplitPrimaryScrollOffset(); + } } @Override @@ -90,8 +96,10 @@ public final class RecentsViewStateController extends LauncherState currentState = mLauncher.getStateManager().getState(); if (isSplitSelectionState(toState) && !isSplitSelectionState(currentState)) { builder.add(mRecentsView.createSplitSelectInitAnimation().buildAnim()); + } + if (isSplitSelectionState(toState)) { mRecentsView.applySplitPrimaryScrollOffset(); - } else if (!isSplitSelectionState(toState) && isSplitSelectionState(currentState)) { + } else { mRecentsView.resetSplitPrimaryScrollOffset(); } From e81af3570b55f522ad09c4a13701352c3ab28ded Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Wed, 1 Dec 2021 17:45:08 +0000 Subject: [PATCH 15/15] Animate right icon of app pair Fix: 208647365 Test: Swipe up and observe right icon of app pair Change-Id: I37a716c156d64c312d132bea80540ce922f3f709 --- .../src/com/android/quickstep/views/GroupedTaskView.java | 6 ++++++ quickstep/src/com/android/quickstep/views/TaskView.java | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java index b43626b50e..9311261624 100644 --- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java +++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java @@ -258,4 +258,10 @@ public class GroupedTaskView extends TaskView { super.updateSnapshotRadius(); mSnapshotView2.setFullscreenParams(mCurrentFullscreenParams); } + + @Override + protected void setIconAndDimTransitionProgress(float progress, boolean invert) { + super.setIconAndDimTransitionProgress(progress, invert); + mIconView2.setAlpha(mIconView.getAlpha()); + } } diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index cca9ecfe62..8dee4e77cb 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -912,7 +912,7 @@ public class TaskView extends FrameLayout implements Reusable { return deviceProfile.overviewShowAsGrid && !isFocusedTask(); } - private void setIconAndDimTransitionProgress(float progress, boolean invert) { + protected void setIconAndDimTransitionProgress(float progress, boolean invert) { if (invert) { progress = 1 - progress; }