diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java index 8944088213..b8c4a2147f 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java @@ -210,6 +210,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable WorkspaceItemInfo info = predictions.get(i); PredictedAppIcon icon = PredictedAppIcon.createIcon(mSampleHotseat, info); icon.setEnabled(false); + icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); icon.verifyHighRes(); CellLayout.LayoutParams lp = new CellLayout.LayoutParams(i, 0, 1, 1); mSampleHotseat.addViewToCellLayout(icon, i, info.getViewId(), lp, true); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java index d3bb4f9062..9bc097527c 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java @@ -349,7 +349,10 @@ public class HotseatPredictionController implements DragController.DragListener, mHotSeatItemsCount); } - private void pinPrediction(ItemInfo info) { + /** + * Pins a predicted app icon into place. + */ + public void pinPrediction(ItemInfo info) { PredictedAppIcon icon = (PredictedAppIcon) mHotseat.getChildAt( mHotseat.getCellXFromOrder(info.rank), mHotseat.getCellYFromOrder(info.rank)); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java index 4bbb48ce83..304c77f3da 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.uioverrides; +import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.PIN_PREDICTION; import static com.android.launcher3.graphics.IconShape.getShape; import android.content.Context; @@ -26,15 +27,19 @@ import android.graphics.Rect; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityNodeInfo; import androidx.core.graphics.ColorUtils; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.WorkspaceItemInfo; +import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.graphics.IconPalette; +import com.android.launcher3.hybridhotseat.HotseatPredictionController; import com.android.launcher3.icons.IconNormalizer; import com.android.launcher3.touch.ItemClickHandler; import com.android.launcher3.touch.ItemLongClickListener; @@ -43,7 +48,8 @@ import com.android.launcher3.views.DoubleShadowBubbleTextView; /** * A BubbleTextView with a ring around it's drawable */ -public class PredictedAppIcon extends DoubleShadowBubbleTextView { +public class PredictedAppIcon extends DoubleShadowBubbleTextView implements + LauncherAccessibilityDelegate.AccessibilityActionHandler { private static final float RING_EFFECT_RATIO = 0.11f; @@ -97,6 +103,13 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView { super.applyFromWorkspaceItem(info); int color = IconPalette.getMutedColor(info.bitmap.color, 0.54f); mIconRingPaint.setColor(ColorUtils.setAlphaComponent(color, 200)); + if (mIsPinned) { + setContentDescription(info.contentDescription); + } else { + setContentDescription( + getContext().getString(R.string.hotseat_prediction_content_description, + info.contentDescription)); + } } /** @@ -104,9 +117,9 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView { */ public void pin(WorkspaceItemInfo info) { if (mIsPinned) return; + mIsPinned = true; applyFromWorkspaceItem(info); setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE); - mIsPinned = true; ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true; invalidate(); } @@ -121,6 +134,27 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView { verifyHighRes(); } + @Override + public void addSupportedAccessibilityActions(AccessibilityNodeInfo accessibilityNodeInfo) { + accessibilityNodeInfo.addAction( + new AccessibilityNodeInfo.AccessibilityAction(PIN_PREDICTION, + getContext().getText(R.string.pin_prediction))); + } + + @Override + public boolean performAccessibilityAction(int action, ItemInfo info) { + QuickstepLauncher launcher = Launcher.cast(Launcher.getLauncher(getContext())); + if (action == PIN_PREDICTION) { + if (launcher == null || launcher.getHotseatPredictionController() == null) { + return false; + } + HotseatPredictionController controller = launcher.getHotseatPredictionController(); + controller.pinPrediction(info); + return true; + } + return false; + } + @Override public void getIconBounds(Rect outBounds) { super.getIconBounds(outBounds); diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml index 9ef38c0fa5..c6b1477851 100644 --- a/quickstep/res/values/strings.xml +++ b/quickstep/res/values/strings.xml @@ -90,6 +90,9 @@ App suggestions added to empty space + + Predicted app: %1$s + Try the back gesture diff --git a/res/values/config.xml b/res/values/config.xml index 1675a986d3..ef6761387c 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -109,6 +109,7 @@ + diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index 423f2bbf1c..6f0ebd2a09 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -67,7 +67,7 @@ public class DeleteDropTarget extends ButtonDropTarget { public boolean supportsAccessibilityDrop(ItemInfo info, View view) { if (info instanceof WorkspaceItemInfo) { // Support the action unless the item is in a context menu. - return info.screenId >= 0; + return canRemove(info); } return (info instanceof LauncherAppWidgetInfo) diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index f45c0b19d2..414ababc98 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -56,6 +56,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme public static final int REMOVE = R.id.action_remove; public static final int UNINSTALL = R.id.action_uninstall; public static final int DISMISS_PREDICTION = R.id.action_dismiss_prediction; + public static final int PIN_PREDICTION = R.id.action_pin_prediction; public static final int RECONFIGURE = R.id.action_reconfigure; protected static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace; protected static final int MOVE = R.id.action_move; @@ -120,6 +121,10 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme if (!(host.getTag() instanceof ItemInfo)) return; ItemInfo item = (ItemInfo) host.getTag(); + if (host instanceof AccessibilityActionHandler) { + ((AccessibilityActionHandler) host).addSupportedAccessibilityActions(info); + } + // If the request came from keyboard, do not add custom shortcuts as that is already // exposed as a direct shortcut if (!fromKeyboard && ShortcutUtil.supportsShortcuts(item)) { @@ -164,7 +169,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme private boolean itemSupportsAccessibleDrag(ItemInfo item) { if (item instanceof WorkspaceItemInfo) { // Support the action unless the item is in a context menu. - return item.screenId >= 0; + return item.screenId >= 0 && item.container != Favorites.CONTAINER_HOTSEAT_PREDICTION; } return (item instanceof LauncherAppWidgetInfo) || (item instanceof FolderInfo); @@ -195,7 +200,10 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme } } } - + if (host instanceof AccessibilityActionHandler + && ((AccessibilityActionHandler) host).performAccessibilityAction(action, item)) { + return true; + } if (action == MOVE) { beginAccessibleDrag(host, item); } else if (action == ADD_TO_WORKSPACE) { @@ -466,4 +474,20 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme } return screenId; } + + /** + * An interface allowing views to handle their own action. + */ + public interface AccessibilityActionHandler { + + /** + * performs accessibility action and returns true on success + */ + boolean performAccessibilityAction(int action, ItemInfo itemInfo); + + /** + * adds all the accessibility actions that can be handled. + */ + void addSupportedAccessibilityActions(AccessibilityNodeInfo accessibilityNodeInfo); + } }