Loading core/java/android/widget/Editor.java +118 −41 Original line number Original line Diff line number Diff line Loading @@ -129,7 +129,7 @@ import java.util.Arrays; import java.util.Comparator; import java.util.Comparator; import java.util.HashMap; import java.util.HashMap; import java.util.List; import java.util.List; import java.util.Map; /** /** * Helper class used by TextView to handle editable text views. * Helper class used by TextView to handle editable text views. Loading Loading @@ -163,6 +163,7 @@ public class Editor { private static final int MENU_ITEM_ORDER_REPLACE = 9; private static final int MENU_ITEM_ORDER_REPLACE = 9; private static final int MENU_ITEM_ORDER_AUTOFILL = 10; private static final int MENU_ITEM_ORDER_AUTOFILL = 10; private static final int MENU_ITEM_ORDER_PASTE_AS_PLAIN_TEXT = 11; private static final int MENU_ITEM_ORDER_PASTE_AS_PLAIN_TEXT = 11; private static final int MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START = 50; private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 100; private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 100; @IntDef({MagnifierHandleTrigger.SELECTION_START, @IntDef({MagnifierHandleTrigger.SELECTION_START, Loading Loading @@ -3792,6 +3793,7 @@ public class Editor { private final RectF mSelectionBounds = new RectF(); private final RectF mSelectionBounds = new RectF(); private final boolean mHasSelection; private final boolean mHasSelection; private final int mHandleHeight; private final int mHandleHeight; private final Map<MenuItem, OnClickListener> mAssistClickHandlers = new HashMap<>(); public TextActionModeCallback(boolean hasSelection) { public TextActionModeCallback(boolean hasSelection) { mHasSelection = hasSelection; mHasSelection = hasSelection; Loading Loading @@ -3819,6 +3821,8 @@ public class Editor { @Override @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { public boolean onCreateActionMode(ActionMode mode, Menu menu) { mAssistClickHandlers.clear(); mode.setTitle(null); mode.setTitle(null); mode.setSubtitle(null); mode.setSubtitle(null); mode.setTitleOptionalHint(true); mode.setTitleOptionalHint(true); Loading Loading @@ -3902,14 +3906,14 @@ public class Editor { updateSelectAllItem(menu); updateSelectAllItem(menu); updateReplaceItem(menu); updateReplaceItem(menu); updateAssistMenuItem(menu); updateAssistMenuItems(menu); } } @Override @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { public boolean onPrepareActionMode(ActionMode mode, Menu menu) { updateSelectAllItem(menu); updateSelectAllItem(menu); updateReplaceItem(menu); updateReplaceItem(menu); updateAssistMenuItem(menu); updateAssistMenuItems(menu); Callback customCallback = getCustomCallback(); Callback customCallback = getCustomCallback(); if (customCallback != null) { if (customCallback != null) { Loading Loading @@ -3942,64 +3946,132 @@ public class Editor { } } } } private void updateAssistMenuItem(Menu menu) { private void updateAssistMenuItems(Menu menu) { menu.removeItem(TextView.ID_ASSIST); clearAssistMenuItems(menu); if (!mTextView.isDeviceProvisioned()) { return; } final TextClassification textClassification = final TextClassification textClassification = getSelectionActionModeHelper().getTextClassification(); getSelectionActionModeHelper().getTextClassification(); if (canAssist()) { final int count = textClassification != null ? textClassification.getActionCount() : 0; menu.add(TextView.ID_ASSIST, TextView.ID_ASSIST, MENU_ITEM_ORDER_ASSIST, for (int i = 0; i < count; i++) { textClassification.getLabel()) if (!isValidAssistMenuItem(i)) { .setIcon(textClassification.getIcon()) continue; .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); } final int groupId = TextView.ID_ASSIST; final int order = (i == 0) ? MENU_ITEM_ORDER_ASSIST : MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START + i; final int id = (i == 0) ? TextView.ID_ASSIST : Menu.NONE; final int showAsFlag = (i == 0) ? MenuItem.SHOW_AS_ACTION_ALWAYS : MenuItem.SHOW_AS_ACTION_NEVER; final MenuItem item = menu.add( groupId, id, order, textClassification.getLabel(i)) .setIcon(textClassification.getIcon(i)) .setIntent(textClassification.getIntent(i)); item.setShowAsAction(showAsFlag); mAssistClickHandlers.put(item, textClassification.getOnClickListener(i)); if (id == TextView.ID_ASSIST) { mMetricsLogger.write( mMetricsLogger.write( new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST) new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST) .setType(MetricsEvent.TYPE_OPEN) .setType(MetricsEvent.TYPE_OPEN) .setSubtype(textClassification.getLogType())); .setSubtype(textClassification.getLogType())); } } } } } private void clearAssistMenuItems(Menu menu) { int i = 0; while (i < menu.size()) { final MenuItem menuItem = menu.getItem(i); if (menuItem.getGroupId() == TextView.ID_ASSIST) { menu.removeItem(menuItem.getItemId()); continue; } i++; } } private boolean canAssist() { private boolean isValidAssistMenuItem(int index) { final TextClassification textClassification = final TextClassification textClassification = getSelectionActionModeHelper().getTextClassification(); getSelectionActionModeHelper().getTextClassification(); return mTextView.isDeviceProvisioned() if (!mTextView.isDeviceProvisioned() || textClassification == null && textClassification != null || index < 0 || index >= textClassification.getActionCount()) { && (textClassification.getIcon() != null return false; || !TextUtils.isEmpty(textClassification.getLabel())) } && (textClassification.getOnClickListener() != null final Drawable icon = textClassification.getIcon(index); || (textClassification.getIntent() != null final CharSequence label = textClassification.getLabel(index); && mTextView.getContext().canStartActivityForResult())); final boolean hasUi = icon != null || !TextUtils.isEmpty(label); final OnClickListener onClick = textClassification.getOnClickListener(index); final Intent intent = textClassification.getIntent(index); final boolean hasAction = onClick != null || isSupportedIntent(intent); return hasUi && hasAction; } } @Override private boolean isSupportedIntent(Intent intent) { public boolean onActionItemClicked(ActionMode mode, MenuItem item) { if (intent == null) { getSelectionActionModeHelper().onSelectionAction(item.getItemId()); return false; if (mProcessTextIntentActionsHandler.performMenuItemAction(item)) { return true; } } Callback customCallback = getCustomCallback(); final Context context = mTextView.getContext(); if (customCallback != null && customCallback.onActionItemClicked(mode, item)) { final ResolveInfo info = context.getPackageManager().resolveActivity(intent, 0); final boolean samePackage = context.getPackageName().equals( info.activityInfo.packageName); if (samePackage) { return true; return true; } } final boolean exported = info.activityInfo.exported; final boolean requiresPermission = info.activityInfo.permission != null; final boolean hasPermission = !requiresPermission || context.checkSelfPermission(info.activityInfo.permission) == PackageManager.PERMISSION_GRANTED; return exported && hasPermission; } private boolean onAssistMenuItemClicked(MenuItem assistMenuItem) { Preconditions.checkArgument(assistMenuItem.getGroupId() == TextView.ID_ASSIST); final TextClassification textClassification = final TextClassification textClassification = getSelectionActionModeHelper().getTextClassification(); getSelectionActionModeHelper().getTextClassification(); if (TextView.ID_ASSIST == item.getItemId() && textClassification != null) { if (!mTextView.isDeviceProvisioned() || textClassification == null) { final OnClickListener onClickListener = // No textClassification result to handle the click. Eat the click. textClassification.getOnClickListener(); return true; if (onClickListener != null) { } onClickListener.onClick(mTextView); } else { OnClickListener onClickListener = mAssistClickHandlers.get(assistMenuItem); final Intent intent = textClassification.getIntent(); if (onClickListener == null) { final Intent intent = assistMenuItem.getIntent(); if (intent != null) { if (intent != null) { TextClassification.createStartActivityOnClickListener( onClickListener = TextClassification.createStartActivityOnClickListener( mTextView.getContext(), intent) mTextView.getContext(), intent); .onClick(mTextView); } } } } if (onClickListener != null) { onClickListener.onClick(mTextView); stopTextActionMode(); if (assistMenuItem.getItemId() == TextView.ID_ASSIST) { mMetricsLogger.action( mMetricsLogger.action( MetricsEvent.ACTION_TEXT_SELECTION_MENU_ITEM_ASSIST, MetricsEvent.ACTION_TEXT_SELECTION_MENU_ITEM_ASSIST, textClassification.getLogType()); textClassification.getLogType()); stopTextActionMode(); } } // We tried our best. return true; } @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { getSelectionActionModeHelper().onSelectionAction(item.getItemId()); if (mProcessTextIntentActionsHandler.performMenuItemAction(item)) { return true; } Callback customCallback = getCustomCallback(); if (customCallback != null && customCallback.onActionItemClicked(mode, item)) { return true; } if (item.getGroupId() == TextView.ID_ASSIST && onAssistMenuItemClicked(item)) { return true; return true; } } return mTextView.onTextContextMenuItem(item.getItemId()); return mTextView.onTextContextMenuItem(item.getItemId()); Loading Loading @@ -4028,6 +4100,8 @@ public class Editor { if (mSelectionModifierCursorController != null) { if (mSelectionModifierCursorController != null) { mSelectionModifierCursorController.hide(); mSelectionModifierCursorController.hide(); } } mAssistClickHandlers.clear(); } } @Override @Override Loading Loading @@ -6559,6 +6633,9 @@ public class Editor { private void loadSupportedActivities() { private void loadSupportedActivities() { mSupportedActivities.clear(); mSupportedActivities.clear(); if (!mContext.canStartActivityForResult()) { return; } PackageManager packageManager = mTextView.getContext().getPackageManager(); PackageManager packageManager = mTextView.getContext().getPackageManager(); List<ResolveInfo> unfiltered = List<ResolveInfo> unfiltered = packageManager.queryIntentActivities(createProcessTextIntent(), 0); packageManager.queryIntentActivities(createProcessTextIntent(), 0); Loading Loading
core/java/android/widget/Editor.java +118 −41 Original line number Original line Diff line number Diff line Loading @@ -129,7 +129,7 @@ import java.util.Arrays; import java.util.Comparator; import java.util.Comparator; import java.util.HashMap; import java.util.HashMap; import java.util.List; import java.util.List; import java.util.Map; /** /** * Helper class used by TextView to handle editable text views. * Helper class used by TextView to handle editable text views. Loading Loading @@ -163,6 +163,7 @@ public class Editor { private static final int MENU_ITEM_ORDER_REPLACE = 9; private static final int MENU_ITEM_ORDER_REPLACE = 9; private static final int MENU_ITEM_ORDER_AUTOFILL = 10; private static final int MENU_ITEM_ORDER_AUTOFILL = 10; private static final int MENU_ITEM_ORDER_PASTE_AS_PLAIN_TEXT = 11; private static final int MENU_ITEM_ORDER_PASTE_AS_PLAIN_TEXT = 11; private static final int MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START = 50; private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 100; private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 100; @IntDef({MagnifierHandleTrigger.SELECTION_START, @IntDef({MagnifierHandleTrigger.SELECTION_START, Loading Loading @@ -3792,6 +3793,7 @@ public class Editor { private final RectF mSelectionBounds = new RectF(); private final RectF mSelectionBounds = new RectF(); private final boolean mHasSelection; private final boolean mHasSelection; private final int mHandleHeight; private final int mHandleHeight; private final Map<MenuItem, OnClickListener> mAssistClickHandlers = new HashMap<>(); public TextActionModeCallback(boolean hasSelection) { public TextActionModeCallback(boolean hasSelection) { mHasSelection = hasSelection; mHasSelection = hasSelection; Loading Loading @@ -3819,6 +3821,8 @@ public class Editor { @Override @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { public boolean onCreateActionMode(ActionMode mode, Menu menu) { mAssistClickHandlers.clear(); mode.setTitle(null); mode.setTitle(null); mode.setSubtitle(null); mode.setSubtitle(null); mode.setTitleOptionalHint(true); mode.setTitleOptionalHint(true); Loading Loading @@ -3902,14 +3906,14 @@ public class Editor { updateSelectAllItem(menu); updateSelectAllItem(menu); updateReplaceItem(menu); updateReplaceItem(menu); updateAssistMenuItem(menu); updateAssistMenuItems(menu); } } @Override @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { public boolean onPrepareActionMode(ActionMode mode, Menu menu) { updateSelectAllItem(menu); updateSelectAllItem(menu); updateReplaceItem(menu); updateReplaceItem(menu); updateAssistMenuItem(menu); updateAssistMenuItems(menu); Callback customCallback = getCustomCallback(); Callback customCallback = getCustomCallback(); if (customCallback != null) { if (customCallback != null) { Loading Loading @@ -3942,64 +3946,132 @@ public class Editor { } } } } private void updateAssistMenuItem(Menu menu) { private void updateAssistMenuItems(Menu menu) { menu.removeItem(TextView.ID_ASSIST); clearAssistMenuItems(menu); if (!mTextView.isDeviceProvisioned()) { return; } final TextClassification textClassification = final TextClassification textClassification = getSelectionActionModeHelper().getTextClassification(); getSelectionActionModeHelper().getTextClassification(); if (canAssist()) { final int count = textClassification != null ? textClassification.getActionCount() : 0; menu.add(TextView.ID_ASSIST, TextView.ID_ASSIST, MENU_ITEM_ORDER_ASSIST, for (int i = 0; i < count; i++) { textClassification.getLabel()) if (!isValidAssistMenuItem(i)) { .setIcon(textClassification.getIcon()) continue; .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); } final int groupId = TextView.ID_ASSIST; final int order = (i == 0) ? MENU_ITEM_ORDER_ASSIST : MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START + i; final int id = (i == 0) ? TextView.ID_ASSIST : Menu.NONE; final int showAsFlag = (i == 0) ? MenuItem.SHOW_AS_ACTION_ALWAYS : MenuItem.SHOW_AS_ACTION_NEVER; final MenuItem item = menu.add( groupId, id, order, textClassification.getLabel(i)) .setIcon(textClassification.getIcon(i)) .setIntent(textClassification.getIntent(i)); item.setShowAsAction(showAsFlag); mAssistClickHandlers.put(item, textClassification.getOnClickListener(i)); if (id == TextView.ID_ASSIST) { mMetricsLogger.write( mMetricsLogger.write( new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST) new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST) .setType(MetricsEvent.TYPE_OPEN) .setType(MetricsEvent.TYPE_OPEN) .setSubtype(textClassification.getLogType())); .setSubtype(textClassification.getLogType())); } } } } } private void clearAssistMenuItems(Menu menu) { int i = 0; while (i < menu.size()) { final MenuItem menuItem = menu.getItem(i); if (menuItem.getGroupId() == TextView.ID_ASSIST) { menu.removeItem(menuItem.getItemId()); continue; } i++; } } private boolean canAssist() { private boolean isValidAssistMenuItem(int index) { final TextClassification textClassification = final TextClassification textClassification = getSelectionActionModeHelper().getTextClassification(); getSelectionActionModeHelper().getTextClassification(); return mTextView.isDeviceProvisioned() if (!mTextView.isDeviceProvisioned() || textClassification == null && textClassification != null || index < 0 || index >= textClassification.getActionCount()) { && (textClassification.getIcon() != null return false; || !TextUtils.isEmpty(textClassification.getLabel())) } && (textClassification.getOnClickListener() != null final Drawable icon = textClassification.getIcon(index); || (textClassification.getIntent() != null final CharSequence label = textClassification.getLabel(index); && mTextView.getContext().canStartActivityForResult())); final boolean hasUi = icon != null || !TextUtils.isEmpty(label); final OnClickListener onClick = textClassification.getOnClickListener(index); final Intent intent = textClassification.getIntent(index); final boolean hasAction = onClick != null || isSupportedIntent(intent); return hasUi && hasAction; } } @Override private boolean isSupportedIntent(Intent intent) { public boolean onActionItemClicked(ActionMode mode, MenuItem item) { if (intent == null) { getSelectionActionModeHelper().onSelectionAction(item.getItemId()); return false; if (mProcessTextIntentActionsHandler.performMenuItemAction(item)) { return true; } } Callback customCallback = getCustomCallback(); final Context context = mTextView.getContext(); if (customCallback != null && customCallback.onActionItemClicked(mode, item)) { final ResolveInfo info = context.getPackageManager().resolveActivity(intent, 0); final boolean samePackage = context.getPackageName().equals( info.activityInfo.packageName); if (samePackage) { return true; return true; } } final boolean exported = info.activityInfo.exported; final boolean requiresPermission = info.activityInfo.permission != null; final boolean hasPermission = !requiresPermission || context.checkSelfPermission(info.activityInfo.permission) == PackageManager.PERMISSION_GRANTED; return exported && hasPermission; } private boolean onAssistMenuItemClicked(MenuItem assistMenuItem) { Preconditions.checkArgument(assistMenuItem.getGroupId() == TextView.ID_ASSIST); final TextClassification textClassification = final TextClassification textClassification = getSelectionActionModeHelper().getTextClassification(); getSelectionActionModeHelper().getTextClassification(); if (TextView.ID_ASSIST == item.getItemId() && textClassification != null) { if (!mTextView.isDeviceProvisioned() || textClassification == null) { final OnClickListener onClickListener = // No textClassification result to handle the click. Eat the click. textClassification.getOnClickListener(); return true; if (onClickListener != null) { } onClickListener.onClick(mTextView); } else { OnClickListener onClickListener = mAssistClickHandlers.get(assistMenuItem); final Intent intent = textClassification.getIntent(); if (onClickListener == null) { final Intent intent = assistMenuItem.getIntent(); if (intent != null) { if (intent != null) { TextClassification.createStartActivityOnClickListener( onClickListener = TextClassification.createStartActivityOnClickListener( mTextView.getContext(), intent) mTextView.getContext(), intent); .onClick(mTextView); } } } } if (onClickListener != null) { onClickListener.onClick(mTextView); stopTextActionMode(); if (assistMenuItem.getItemId() == TextView.ID_ASSIST) { mMetricsLogger.action( mMetricsLogger.action( MetricsEvent.ACTION_TEXT_SELECTION_MENU_ITEM_ASSIST, MetricsEvent.ACTION_TEXT_SELECTION_MENU_ITEM_ASSIST, textClassification.getLogType()); textClassification.getLogType()); stopTextActionMode(); } } // We tried our best. return true; } @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { getSelectionActionModeHelper().onSelectionAction(item.getItemId()); if (mProcessTextIntentActionsHandler.performMenuItemAction(item)) { return true; } Callback customCallback = getCustomCallback(); if (customCallback != null && customCallback.onActionItemClicked(mode, item)) { return true; } if (item.getGroupId() == TextView.ID_ASSIST && onAssistMenuItemClicked(item)) { return true; return true; } } return mTextView.onTextContextMenuItem(item.getItemId()); return mTextView.onTextContextMenuItem(item.getItemId()); Loading Loading @@ -4028,6 +4100,8 @@ public class Editor { if (mSelectionModifierCursorController != null) { if (mSelectionModifierCursorController != null) { mSelectionModifierCursorController.hide(); mSelectionModifierCursorController.hide(); } } mAssistClickHandlers.clear(); } } @Override @Override Loading Loading @@ -6559,6 +6633,9 @@ public class Editor { private void loadSupportedActivities() { private void loadSupportedActivities() { mSupportedActivities.clear(); mSupportedActivities.clear(); if (!mContext.canStartActivityForResult()) { return; } PackageManager packageManager = mTextView.getContext().getPackageManager(); PackageManager packageManager = mTextView.getContext().getPackageManager(); List<ResolveInfo> unfiltered = List<ResolveInfo> unfiltered = packageManager.queryIntentActivities(createProcessTextIntent(), 0); packageManager.queryIntentActivities(createProcessTextIntent(), 0); Loading