diff options
7 files changed, 151 insertions, 41 deletions
diff --git a/res/values/config.xml b/res/values/config.xml index bdedff0df..c962decd0 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -101,4 +101,5 @@ <item type="id" name="action_move_screen_backwards" /> <item type="id" name="action_move_screen_forwards" /> <item type="id" name="action_resize" /> + <item type="id" name="action_deep_shortcuts" /> </resources> diff --git a/res/values/strings.xml b/res/values/strings.xml index 7bd9ff759..757e29df5 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -249,4 +249,7 @@ <!-- Accessibility confirmation for widget resize. --> <string name="widget_resized">Widget resized to width <xliff:g id="number" example="2">%1$s</xliff:g> height <xliff:g id="number" example="1">%2$s</xliff:g></string> + <!-- Accessibility action to show quick actions menu for an icon. [CHAR_LIMIT=30] --> + <string name="action_deep_shortcut" translatable="false">Quick links</string> + </resources> diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index b476392b4..33b3ad347 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -652,6 +652,13 @@ public class BubbleTextView extends TextView } /** + * Returns true if the view can show custom shortcuts. + */ + public boolean hasDeepShortcuts() { + return !mLauncher.getShortcutIdsForItem((ItemInfo) getTag()).isEmpty(); + } + + /** * Returns the start delay when animating between certain {@link FastBitmapDrawable} states. */ private static int getStartDelayForStateChange(final FastBitmapDrawable.State fromState, diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index 6bf8abf30..0562cf54b 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -18,6 +18,7 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import com.android.launcher3.AppInfo; import com.android.launcher3.AppWidgetResizeFrame; +import com.android.launcher3.BubbleTextView; import com.android.launcher3.CellLayout; import com.android.launcher3.DeleteDropTarget; import com.android.launcher3.DragSource; @@ -36,6 +37,8 @@ import com.android.launcher3.ShortcutInfo; import com.android.launcher3.UninstallDropTarget; import com.android.launcher3.Workspace; import com.android.launcher3.dragndrop.DragController.DragListener; +import com.android.launcher3.shortcuts.DeepShortcutTextView; +import com.android.launcher3.shortcuts.DeepShortcutsContainer; import com.android.launcher3.util.Thunk; import java.util.ArrayList; @@ -45,13 +48,14 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme private static final String TAG = "LauncherAccessibilityDelegate"; - private static final int REMOVE = R.id.action_remove; - private static final int INFO = R.id.action_info; - private static final int UNINSTALL = R.id.action_uninstall; - private static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace; - private static final int MOVE = R.id.action_move; - private static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace; - private static final int RESIZE = R.id.action_resize; + protected static final int REMOVE = R.id.action_remove; + protected static final int INFO = R.id.action_info; + protected static final int UNINSTALL = R.id.action_uninstall; + protected static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace; + protected static final int MOVE = R.id.action_move; + protected static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace; + protected static final int RESIZE = R.id.action_resize; + protected static final int DEEP_SHORTCUTS = R.id.action_deep_shortcuts; public enum DragType { ICON, @@ -65,7 +69,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme public View item; } - private final SparseArray<AccessibilityAction> mActions = new SparseArray<>(); + protected final SparseArray<AccessibilityAction> mActions = new SparseArray<>(); @Thunk final Launcher mLauncher; private DragInfo mDragInfo = null; @@ -88,14 +92,24 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme launcher.getText(R.string.action_move_to_workspace))); mActions.put(RESIZE, new AccessibilityAction(RESIZE, launcher.getText(R.string.action_resize))); + mActions.put(DEEP_SHORTCUTS, new AccessibilityAction(DEEP_SHORTCUTS, + launcher.getText(R.string.action_deep_shortcut))); } @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(host, info); + addActions(host, info); + } + + protected void addActions(View host, AccessibilityNodeInfo info) { if (!(host.getTag() instanceof ItemInfo)) return; ItemInfo item = (ItemInfo) host.getTag(); + if (host instanceof BubbleTextView && ((BubbleTextView) host).hasDeepShortcuts()) { + info.addAction(mActions.get(DEEP_SHORTCUTS)); + } + if (DeleteDropTarget.supportsAccessibleDrop(item)) { info.addAction(mActions.get(REMOVE)); } @@ -215,6 +229,9 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme } }) .show(); + return true; + } else if (action == DEEP_SHORTCUTS) { + return DeepShortcutsContainer.showForIcon((BubbleTextView) host) != null; } return false; } @@ -397,7 +414,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme /** * Find empty space on the workspace and returns the screenId. */ - private long findSpaceOnWorkspace(ItemInfo info, int[] outCoordinates) { + protected long findSpaceOnWorkspace(ItemInfo info, int[] outCoordinates) { Workspace workspace = mLauncher.getWorkspace(); ArrayList<Long> workspaceScreens = workspace.getScreenOrder(); long screenId; diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java new file mode 100644 index 000000000..ff70279ea --- /dev/null +++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2016 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.view.View; +import android.view.accessibility.AccessibilityNodeInfo; + +import com.android.launcher3.ItemInfo; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherModel; +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.R; +import com.android.launcher3.ShortcutInfo; + +import java.util.ArrayList; + +/** + * Extension of {@link LauncherAccessibilityDelegate} with actions specific to shortcuts in + * deep shortcuts menu. + */ +public class ShortcutMenuAccessibilityDelegate extends LauncherAccessibilityDelegate { + + public ShortcutMenuAccessibilityDelegate(Launcher launcher) { + super(launcher); + } + + @Override + protected void addActions(View host, AccessibilityNodeInfo info) { + info.addAction(mActions.get(ADD_TO_WORKSPACE)); + } + + @Override + public boolean performAction(View host, ItemInfo item, int action) { + if (action == ADD_TO_WORKSPACE) { + final ShortcutInfo info = (ShortcutInfo) item; + final int[] coordinates = new int[2]; + final long screenId = findSpaceOnWorkspace(item, coordinates); + Runnable onComplete = new Runnable() { + @Override + public void run() { + LauncherModel.addItemToDatabase(mLauncher, info, + LauncherSettings.Favorites.CONTAINER_DESKTOP, + screenId, coordinates[0], coordinates[1]); + ArrayList<ItemInfo> itemList = new ArrayList<>(); + itemList.add(info); + mLauncher.bindItems(itemList, 0, itemList.size(), true); + mLauncher.closeShortcutsContainer(); + announceConfirmation(R.string.item_added_to_workspace); + } + }; + + if (!mLauncher.showWorkspace(true, onComplete)) { + onComplete.run(); + } + return true; + } + return false; + } +} diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java index c9eee81e4..fdc0bd22f 100644 --- a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java +++ b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java @@ -30,6 +30,7 @@ import android.os.Handler; import android.os.Looper; import android.text.TextUtils; import android.util.AttributeSet; +import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -48,6 +49,7 @@ import com.android.launcher3.R; import com.android.launcher3.ShortcutInfo; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; +import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragLayer; @@ -70,10 +72,12 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC UserEventDispatcher.LaunchSourceProvider { private static final String TAG = "ShortcutsContainer"; - private Launcher mLauncher; - private DeepShortcutManager mDeepShortcutsManager; + private final Launcher mLauncher; + private final DeepShortcutManager mDeepShortcutsManager; private final int mDragDeadzone; private final int mStartDragThreshold; + private final ShortcutMenuAccessibilityDelegate mAccessibilityDelegate; + private BubbleTextView mDeferredDragIcon; private int mActivePointerId; private int[] mTouchDown = null; @@ -115,6 +119,7 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC mDragDeadzone = ViewConfiguration.get(context).getScaledTouchSlop(); mStartDragThreshold = getResources().getDimensionPixelSize( R.dimen.deep_shortcuts_start_drag_threshold); + mAccessibilityDelegate = new ShortcutMenuAccessibilityDelegate(mLauncher); } public DeepShortcutsContainer(Context context, AttributeSet attrs) { @@ -128,12 +133,14 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC public void populateAndShow(final BubbleTextView originalIcon, final List<String> ids) { // Add dummy views first, and populate with real shortcut info when ready. final int spacing = getResources().getDimensionPixelSize(R.dimen.deep_shortcuts_spacing); - final LayoutInflater inflator = mLauncher.getLayoutInflater(); + final LayoutInflater inflater = mLauncher.getLayoutInflater(); for (int i = 0; i < ids.size(); i++) { - final View shortcut = inflator.inflate(R.layout.deep_shortcut, this, false); + final DeepShortcutView shortcut = + (DeepShortcutView) inflater.inflate(R.layout.deep_shortcut, this, false); if (i < ids.size() - 1) { ((LayoutParams) shortcut.getLayoutParams()).bottomMargin = spacing; } + shortcut.getBubbleText().setAccessibilityDelegate(mAccessibilityDelegate); addView(shortcut); } @@ -501,4 +508,26 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC // TODO: add target.rank targetParent.containerType = LauncherLogProto.DEEPSHORTCUTS; } + + /** + * Shows the shortcuts container for {@param icon} + * @return the container if shown or null. + */ + public static DeepShortcutsContainer showForIcon(BubbleTextView icon) { + Launcher launcher = Launcher.getLauncher(icon.getContext()); + List<String> ids = launcher.getShortcutIdsForItem((ItemInfo) icon.getTag()); + if (!ids.isEmpty()) { + // There are shortcuts associated with the app, so defer its drag. + final DeepShortcutsContainer container = + (DeepShortcutsContainer) launcher.getLayoutInflater().inflate( + R.layout.deep_shortcuts_container, launcher.getDragLayer(), false); + container.setVisibility(View.INVISIBLE); + launcher.getDragLayer().addView(container); + container.populateAndShow(icon, ids); + icon.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, + HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + return container; + } + return null; + } } diff --git a/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java b/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java index f3b0d0402..f94595b54 100644 --- a/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java +++ b/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java @@ -1,9 +1,6 @@ package com.android.launcher3.shortcuts; -import android.content.Context; import android.os.SystemClock; -import android.view.HapticFeedbackConstants; -import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; @@ -11,14 +8,10 @@ import android.view.ViewParent; import com.android.launcher3.BubbleTextView; import com.android.launcher3.CheckLongPressHelper; -import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; -import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.dragndrop.DragLayer; -import java.util.List; - /** * A {@link android.view.View.OnTouchListener} that creates a {@link DeepShortcutsContainer} and * forwards touch events to it. This listener should be put on any icon that supports shortcuts. @@ -58,6 +51,7 @@ public class ShortcutsContainerListener implements View.OnTouchListener, /** If true, the gesture is not handled. The value is reset when next gesture starts. */ private boolean mIgnoreCurrentGesture; + private DeepShortcutsContainer mShortcutsContainer; public ShortcutsContainerListener(BubbleTextView icon) { mSrcIcon = icon; @@ -78,8 +72,7 @@ public class ShortcutsContainerListener implements View.OnTouchListener, if (event.getAction() == MotionEvent.ACTION_DOWN) { // There are no shortcuts associated with this item, // so return to normal touch handling. - mIgnoreCurrentGesture = - (mLauncher.getShortcutIdsForItem((ItemInfo) v.getTag())).isEmpty(); + mIgnoreCurrentGesture = !mSrcIcon.hasDeepShortcuts(); mTouchDown[0] = (int) event.getX(); mTouchDown[1] = (int) event.getY(); @@ -134,21 +127,8 @@ public class ShortcutsContainerListener implements View.OnTouchListener, * @return true to start forwarding, false otherwise */ protected boolean onForwardingStarted() { - List<String> ids = mLauncher.getShortcutIdsForItem((ItemInfo) mSrcIcon.getTag()); - if (!ids.isEmpty()) { - // There are shortcuts associated with the app, so defer its drag. - LayoutInflater layoutInflater = (LayoutInflater) mLauncher.getSystemService - (Context.LAYOUT_INFLATER_SERVICE); - final DeepShortcutsContainer deepShortcutsContainer = (DeepShortcutsContainer) - layoutInflater.inflate(R.layout.deep_shortcuts_container, mDragLayer, false); - deepShortcutsContainer.setVisibility(View.INVISIBLE); - mDragLayer.addView(deepShortcutsContainer); - deepShortcutsContainer.populateAndShow(mSrcIcon, ids); - mSrcIcon.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, - HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); - return true; - } - return false; + mShortcutsContainer = DeepShortcutsContainer.showForIcon(mSrcIcon); + return mShortcutsContainer != null; } /** @@ -157,6 +137,7 @@ public class ShortcutsContainerListener implements View.OnTouchListener, * @return true to stop forwarding, false otherwise */ protected boolean onForwardingStopped() { + mShortcutsContainer = null; return true; } @@ -223,8 +204,8 @@ public class ShortcutsContainerListener implements View.OnTouchListener, private void onLongPress() { clearCallbacks(); - final View src = mSrcIcon; - if (!src.isEnabled() || mLauncher.getShortcutIdsForItem((ItemInfo) src.getTag()).isEmpty()) { + final BubbleTextView src = mSrcIcon; + if (!src.isEnabled() || !src.hasDeepShortcuts()) { // Ignore long-press if the view is disabled or doesn't have shortcuts. return; } @@ -254,8 +235,7 @@ public class ShortcutsContainerListener implements View.OnTouchListener, */ private boolean onTouchForwarded(MotionEvent srcEvent) { final View src = mSrcIcon; - - final DeepShortcutsContainer dst = mLauncher.getOpenShortcutsContainer(); + final DeepShortcutsContainer dst = mShortcutsContainer; if (dst == null) { return false; } |