/* * Copyright (C) 2011 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.talkback.menurules; import android.view.Menu; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import com.android.talkback.contextmenu.ContextMenu; import com.android.talkback.contextmenu.ContextMenuItem; import com.android.talkback.contextmenu.ContextSubMenu; import com.google.android.marvin.talkback.TalkBackService; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; /** * Rule-based processor for adding items to the local breakout menu. */ public class NodeMenuRuleProcessor { private static final LinkedList<NodeMenuRule> mRules = new LinkedList<>(); private static final NodeMenuRule mRuleCustomAction; static { // Rules are matched in the order they are added, but any rule that // accepts will be able to modify the menu. mRules.add(new RuleSpannables()); mRules.add(new RuleEditText()); mRules.add(new RuleViewPager()); mRules.add(new RuleGranularity()); mRules.add(new RuleUnlabeledImage()); mRules.add(new RuleSuggestions()); mRules.add(new RuleSeekBar()); mRuleCustomAction = new RuleCustomAction(); mRules.add(mRuleCustomAction); } private final TalkBackService mService; public NodeMenuRuleProcessor(TalkBackService service) { mService = service; } /** * Populates a {@link Menu} with items specific to the provided node * based on {@link NodeMenuRule}s. * * @param menu The menu to populate. * @param node The node with which to populate the menu. * @return {@code true} if successful, {@code false} otherwise. */ public boolean prepareMenuForNode(ContextMenu menu, AccessibilityNodeInfoCompat node) { if (node == null) { return false; } // Always reset the menu since it is based on the current cursor. menu.clear(); // Track which rules accept the node. final LinkedList<NodeMenuRule> matchingRules = new LinkedList<>(); for (NodeMenuRule rule : mRules) { if (rule.accept(mService, node)) { matchingRules.add(rule); } } List<List<ContextMenuItem>> menuItems = new ArrayList<>(); List<CharSequence> subMenuTitles = new ArrayList<>(); boolean canCollapseMenu = false; for (NodeMenuRule rule : matchingRules) { List<ContextMenuItem> ruleResults = rule.getMenuItemsForNode(mService, menu.getMenuItemBuilder(), node); if (ruleResults != null && ruleResults.size() > 0) { menuItems.add(ruleResults); subMenuTitles.add(rule.getUserFriendlyMenuName(mService)); } canCollapseMenu |= rule.canCollapseMenu(); } boolean needCollapse = canCollapseMenu && menuItems.size() == 1; if (needCollapse) { for (ContextMenuItem menuItem : menuItems.get(0)) { menu.add(menuItem); } } else { int size = menuItems.size(); for (int i = 0; i < size; i++) { List<ContextMenuItem> items = menuItems.get(i); CharSequence subMenuName = subMenuTitles.get(i); ContextSubMenu subMenu = menu.addSubMenu(0, 0, 0, subMenuName); subMenu.getItem().setEnabled(true); for (ContextMenuItem menuItem : items) { subMenu.add(menuItem); } } } return menu.size() != 0; } public boolean prepareCustomActionMenuForNode(ContextMenu menu, AccessibilityNodeInfoCompat node) { if (node == null) { return false; } // Always reset the menu since it is based on the current cursor. menu.clear(); if (!mRuleCustomAction.accept(mService, node)) { return false; } List<ContextMenuItem> menuItems = mRuleCustomAction.getMenuItemsForNode(mService, menu.getMenuItemBuilder(), node); if (menuItems == null || menuItems.size() == 0) { return false; } for (ContextMenuItem menuItem : menuItems) { menu.add(menuItem); } return menu.size() != 0; } }