Merge "Set smart selection menu into context menu item"
This commit is contained in:
committed by
Android (Google) Code Review
commit
561d143788
@@ -3090,7 +3090,33 @@ public class Editor {
|
||||
mContextMenuAnchorY = y;
|
||||
}
|
||||
|
||||
void onCreateContextMenu(ContextMenu menu) {
|
||||
private void setAssistContextMenuItems(Menu menu) {
|
||||
final TextClassification textClassification =
|
||||
getSelectionActionModeHelper().getTextClassification();
|
||||
if (textClassification == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final AssistantCallbackHelper helper =
|
||||
new AssistantCallbackHelper(getSelectionActionModeHelper());
|
||||
helper.updateAssistMenuItems(menu, (MenuItem item) -> {
|
||||
getSelectionActionModeHelper()
|
||||
.onSelectionAction(item.getItemId(), item.getTitle().toString());
|
||||
|
||||
if (mProcessTextIntentActionsHandler.performMenuItemAction(item)) {
|
||||
return true;
|
||||
}
|
||||
if (item.getGroupId() == TextView.ID_ASSIST && helper.onAssistMenuItemClicked(item)) {
|
||||
return true;
|
||||
}
|
||||
return mTextView.onTextContextMenuItem(item.getItemId());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the context menu is created.
|
||||
*/
|
||||
public void onCreateContextMenu(ContextMenu menu) {
|
||||
if (mIsBeingLongClicked || Float.isNaN(mContextMenuAnchorX)
|
||||
|| Float.isNaN(mContextMenuAnchorY)) {
|
||||
return;
|
||||
@@ -3153,6 +3179,8 @@ public class Editor {
|
||||
menu.setOptionalIconsVisible(true);
|
||||
menu.setGroupDividerEnabled(true);
|
||||
|
||||
setAssistContextMenuItems(menu);
|
||||
|
||||
final int keyboard = mTextView.getResources().getConfiguration().keyboard;
|
||||
menu.setQwertyMode(keyboard == Configuration.KEYBOARD_QWERTY);
|
||||
} else {
|
||||
@@ -4308,6 +4336,165 @@ public class Editor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for UI component (e.g. ActionMode and ContextMenu) with TextClassification.
|
||||
* @hide
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public class AssistantCallbackHelper {
|
||||
private final Map<MenuItem, OnClickListener> mAssistClickHandlers = new HashMap<>();
|
||||
@Nullable private TextClassification mPrevTextClassification;
|
||||
@NonNull private final SelectionActionModeHelper mHelper;
|
||||
|
||||
public AssistantCallbackHelper(SelectionActionModeHelper helper) {
|
||||
mHelper = helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears callback handlers.
|
||||
*/
|
||||
public void clearCallbackHandlers() {
|
||||
mAssistClickHandlers.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get on click listener assisiated with the MenuItem.
|
||||
*/
|
||||
public OnClickListener getOnClickListener(MenuItem key) {
|
||||
return mAssistClickHandlers.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update menu items.
|
||||
*
|
||||
* Existing assist menu will be cleared and latest assist menu will be added.
|
||||
*/
|
||||
public void updateAssistMenuItems(Menu menu, MenuItem.OnMenuItemClickListener listener) {
|
||||
final TextClassification textClassification = mHelper.getTextClassification();
|
||||
if (mPrevTextClassification == textClassification) {
|
||||
// Already handled.
|
||||
return;
|
||||
}
|
||||
clearAssistMenuItems(menu);
|
||||
if (textClassification == null) {
|
||||
return;
|
||||
}
|
||||
if (!shouldEnableAssistMenuItems()) {
|
||||
return;
|
||||
}
|
||||
if (!textClassification.getActions().isEmpty()) {
|
||||
// Primary assist action (Always shown).
|
||||
final MenuItem item = addAssistMenuItem(menu,
|
||||
textClassification.getActions().get(0), TextView.ID_ASSIST,
|
||||
ACTION_MODE_MENU_ITEM_ORDER_ASSIST, MenuItem.SHOW_AS_ACTION_ALWAYS,
|
||||
listener);
|
||||
item.setIntent(textClassification.getIntent());
|
||||
} else if (hasLegacyAssistItem(textClassification)) {
|
||||
// Legacy primary assist action (Always shown).
|
||||
final MenuItem item = menu.add(TextView.ID_ASSIST, TextView.ID_ASSIST,
|
||||
ACTION_MODE_MENU_ITEM_ORDER_ASSIST,
|
||||
textClassification.getLabel())
|
||||
.setIcon(textClassification.getIcon())
|
||||
.setIntent(textClassification.getIntent());
|
||||
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
mAssistClickHandlers.put(item, TextClassification.createIntentOnClickListener(
|
||||
TextClassification.createPendingIntent(mTextView.getContext(),
|
||||
textClassification.getIntent(),
|
||||
createAssistMenuItemPendingIntentRequestCode())));
|
||||
}
|
||||
final int count = textClassification.getActions().size();
|
||||
for (int i = 1; i < count; i++) {
|
||||
// Secondary assist action (Never shown).
|
||||
addAssistMenuItem(menu, textClassification.getActions().get(i), Menu.NONE,
|
||||
ACTION_MODE_MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START + i - 1,
|
||||
MenuItem.SHOW_AS_ACTION_NEVER, listener);
|
||||
}
|
||||
mPrevTextClassification = textClassification;
|
||||
}
|
||||
|
||||
private MenuItem addAssistMenuItem(Menu menu, RemoteAction action, int itemId, int order,
|
||||
int showAsAction, MenuItem.OnMenuItemClickListener listener) {
|
||||
final MenuItem item = menu.add(TextView.ID_ASSIST, itemId, order, action.getTitle())
|
||||
.setContentDescription(action.getContentDescription());
|
||||
if (action.shouldShowIcon()) {
|
||||
item.setIcon(action.getIcon().loadDrawable(mTextView.getContext()));
|
||||
}
|
||||
item.setShowAsAction(showAsAction);
|
||||
mAssistClickHandlers.put(item,
|
||||
TextClassification.createIntentOnClickListener(action.getActionIntent()));
|
||||
mA11ySmartActions.addAction(action);
|
||||
if (listener != null) {
|
||||
item.setOnMenuItemClickListener(listener);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
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++;
|
||||
}
|
||||
mA11ySmartActions.reset();
|
||||
}
|
||||
|
||||
private boolean hasLegacyAssistItem(TextClassification classification) {
|
||||
// Check whether we have the UI data and action.
|
||||
return (classification.getIcon() != null || !TextUtils.isEmpty(
|
||||
classification.getLabel())) && (classification.getIntent() != null
|
||||
|| classification.getOnClickListener() != null);
|
||||
}
|
||||
|
||||
private boolean shouldEnableAssistMenuItems() {
|
||||
return mTextView.isDeviceProvisioned()
|
||||
&& TextClassificationManager.getSettings(mTextView.getContext())
|
||||
.isSmartTextShareEnabled();
|
||||
}
|
||||
|
||||
private int createAssistMenuItemPendingIntentRequestCode() {
|
||||
return mTextView.hasSelection()
|
||||
? mTextView.getText().subSequence(
|
||||
mTextView.getSelectionStart(), mTextView.getSelectionEnd())
|
||||
.hashCode()
|
||||
: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the assist menu on ActionMode or ContextMenu is called.
|
||||
*/
|
||||
public boolean onAssistMenuItemClicked(MenuItem assistMenuItem) {
|
||||
Preconditions.checkArgument(assistMenuItem.getGroupId() == TextView.ID_ASSIST);
|
||||
|
||||
final TextClassification textClassification =
|
||||
getSelectionActionModeHelper().getTextClassification();
|
||||
if (!shouldEnableAssistMenuItems() || textClassification == null) {
|
||||
// No textClassification result to handle the click. Eat the click.
|
||||
return true;
|
||||
}
|
||||
|
||||
OnClickListener onClickListener = getOnClickListener(assistMenuItem);
|
||||
if (onClickListener == null) {
|
||||
final Intent intent = assistMenuItem.getIntent();
|
||||
if (intent != null) {
|
||||
onClickListener = TextClassification.createIntentOnClickListener(
|
||||
TextClassification.createPendingIntent(
|
||||
mTextView.getContext(), intent,
|
||||
createAssistMenuItemPendingIntentRequestCode()));
|
||||
}
|
||||
}
|
||||
if (onClickListener != null) {
|
||||
onClickListener.onClick(mTextView);
|
||||
stopTextActionMode();
|
||||
}
|
||||
// We tried our best.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An ActionMode Callback class that is used to provide actions while in text insertion or
|
||||
* selection mode.
|
||||
@@ -4320,9 +4507,8 @@ public class Editor {
|
||||
private final RectF mSelectionBounds = new RectF();
|
||||
private final boolean mHasSelection;
|
||||
private final int mHandleHeight;
|
||||
private final Map<MenuItem, OnClickListener> mAssistClickHandlers = new HashMap<>();
|
||||
@Nullable
|
||||
private TextClassification mPrevTextClassification;
|
||||
private final AssistantCallbackHelper mHelper = new AssistantCallbackHelper(
|
||||
getSelectionActionModeHelper());
|
||||
|
||||
TextActionModeCallback(@TextActionMode int mode) {
|
||||
mHasSelection = mode == TextActionMode.SELECTION
|
||||
@@ -4351,7 +4537,7 @@ public class Editor {
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
mAssistClickHandlers.clear();
|
||||
mHelper.clearCallbackHandlers();
|
||||
|
||||
mode.setTitle(null);
|
||||
mode.setSubtitle(null);
|
||||
@@ -4432,14 +4618,14 @@ public class Editor {
|
||||
|
||||
updateSelectAllItem(menu);
|
||||
updateReplaceItem(menu);
|
||||
updateAssistMenuItems(menu);
|
||||
mHelper.updateAssistMenuItems(menu, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||
updateSelectAllItem(menu);
|
||||
updateReplaceItem(menu);
|
||||
updateAssistMenuItems(menu);
|
||||
mHelper.updateAssistMenuItems(menu, null);
|
||||
|
||||
Callback customCallback = getCustomCallback();
|
||||
if (customCallback != null) {
|
||||
@@ -4472,125 +4658,6 @@ public class Editor {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAssistMenuItems(Menu menu) {
|
||||
final TextClassification textClassification =
|
||||
getSelectionActionModeHelper().getTextClassification();
|
||||
if (mPrevTextClassification == textClassification) {
|
||||
// Already handled.
|
||||
return;
|
||||
}
|
||||
clearAssistMenuItems(menu);
|
||||
if (textClassification == null) {
|
||||
return;
|
||||
}
|
||||
if (!shouldEnableAssistMenuItems()) {
|
||||
return;
|
||||
}
|
||||
if (!textClassification.getActions().isEmpty()) {
|
||||
// Primary assist action (Always shown).
|
||||
final MenuItem item = addAssistMenuItem(menu,
|
||||
textClassification.getActions().get(0), TextView.ID_ASSIST,
|
||||
ACTION_MODE_MENU_ITEM_ORDER_ASSIST, MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
item.setIntent(textClassification.getIntent());
|
||||
} else if (hasLegacyAssistItem(textClassification)) {
|
||||
// Legacy primary assist action (Always shown).
|
||||
final MenuItem item = menu.add(
|
||||
TextView.ID_ASSIST, TextView.ID_ASSIST, ACTION_MODE_MENU_ITEM_ORDER_ASSIST,
|
||||
textClassification.getLabel())
|
||||
.setIcon(textClassification.getIcon())
|
||||
.setIntent(textClassification.getIntent());
|
||||
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
mAssistClickHandlers.put(item, TextClassification.createIntentOnClickListener(
|
||||
TextClassification.createPendingIntent(mTextView.getContext(),
|
||||
textClassification.getIntent(),
|
||||
createAssistMenuItemPendingIntentRequestCode())));
|
||||
}
|
||||
final int count = textClassification.getActions().size();
|
||||
for (int i = 1; i < count; i++) {
|
||||
// Secondary assist action (Never shown).
|
||||
addAssistMenuItem(menu, textClassification.getActions().get(i), Menu.NONE,
|
||||
ACTION_MODE_MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START + i - 1,
|
||||
MenuItem.SHOW_AS_ACTION_NEVER);
|
||||
}
|
||||
mPrevTextClassification = textClassification;
|
||||
}
|
||||
|
||||
private MenuItem addAssistMenuItem(Menu menu, RemoteAction action, int itemId, int order,
|
||||
int showAsAction) {
|
||||
final MenuItem item = menu.add(TextView.ID_ASSIST, itemId, order, action.getTitle())
|
||||
.setContentDescription(action.getContentDescription());
|
||||
if (action.shouldShowIcon()) {
|
||||
item.setIcon(action.getIcon().loadDrawable(mTextView.getContext()));
|
||||
}
|
||||
item.setShowAsAction(showAsAction);
|
||||
mAssistClickHandlers.put(item,
|
||||
TextClassification.createIntentOnClickListener(action.getActionIntent()));
|
||||
mA11ySmartActions.addAction(action);
|
||||
return item;
|
||||
}
|
||||
|
||||
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++;
|
||||
}
|
||||
mA11ySmartActions.reset();
|
||||
}
|
||||
|
||||
private boolean hasLegacyAssistItem(TextClassification classification) {
|
||||
// Check whether we have the UI data and and action.
|
||||
return (classification.getIcon() != null || !TextUtils.isEmpty(
|
||||
classification.getLabel())) && (classification.getIntent() != null
|
||||
|| classification.getOnClickListener() != null);
|
||||
}
|
||||
|
||||
private boolean onAssistMenuItemClicked(MenuItem assistMenuItem) {
|
||||
Preconditions.checkArgument(assistMenuItem.getGroupId() == TextView.ID_ASSIST);
|
||||
|
||||
final TextClassification textClassification =
|
||||
getSelectionActionModeHelper().getTextClassification();
|
||||
if (!shouldEnableAssistMenuItems() || textClassification == null) {
|
||||
// No textClassification result to handle the click. Eat the click.
|
||||
return true;
|
||||
}
|
||||
|
||||
OnClickListener onClickListener = mAssistClickHandlers.get(assistMenuItem);
|
||||
if (onClickListener == null) {
|
||||
final Intent intent = assistMenuItem.getIntent();
|
||||
if (intent != null) {
|
||||
onClickListener = TextClassification.createIntentOnClickListener(
|
||||
TextClassification.createPendingIntent(
|
||||
mTextView.getContext(), intent,
|
||||
createAssistMenuItemPendingIntentRequestCode()));
|
||||
}
|
||||
}
|
||||
if (onClickListener != null) {
|
||||
onClickListener.onClick(mTextView);
|
||||
stopTextActionMode();
|
||||
}
|
||||
// We tried our best.
|
||||
return true;
|
||||
}
|
||||
|
||||
private int createAssistMenuItemPendingIntentRequestCode() {
|
||||
return mTextView.hasSelection()
|
||||
? mTextView.getText().subSequence(
|
||||
mTextView.getSelectionStart(), mTextView.getSelectionEnd())
|
||||
.hashCode()
|
||||
: 0;
|
||||
}
|
||||
|
||||
private boolean shouldEnableAssistMenuItems() {
|
||||
return mTextView.isDeviceProvisioned()
|
||||
&& TextClassificationManager.getSettings(mTextView.getContext())
|
||||
.isSmartTextShareEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||
getSelectionActionModeHelper()
|
||||
@@ -4603,7 +4670,7 @@ public class Editor {
|
||||
if (customCallback != null && customCallback.onActionItemClicked(mode, item)) {
|
||||
return true;
|
||||
}
|
||||
if (item.getGroupId() == TextView.ID_ASSIST && onAssistMenuItemClicked(item)) {
|
||||
if (item.getGroupId() == TextView.ID_ASSIST && mHelper.onAssistMenuItemClicked(item)) {
|
||||
return true;
|
||||
}
|
||||
return mTextView.onTextContextMenuItem(item.getItemId());
|
||||
@@ -4633,7 +4700,7 @@ public class Editor {
|
||||
mSelectionModifierCursorController.hide();
|
||||
}
|
||||
|
||||
mAssistClickHandlers.clear();
|
||||
mHelper.clearCallbackHandlers();
|
||||
mRequestingLinkActionMode = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
@UiThread
|
||||
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
|
||||
public final class SelectionActionModeHelper {
|
||||
public class SelectionActionModeHelper {
|
||||
|
||||
private static final String LOG_TAG = "SelectActionModeHelper";
|
||||
|
||||
|
||||
@@ -1607,6 +1607,15 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="android.widget.TextViewContextMenuActivity"
|
||||
android:screenOrientation="locked"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="android.animation.AnimatorSetActivity"
|
||||
android:screenOrientation="locked"
|
||||
android:exported="true">
|
||||
|
||||
32
core/tests/coretests/res/layout/textview_contextmenu.xml
Normal file
32
core/tests/coretests/res/layout/textview_contextmenu.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
**
|
||||
** Copyright 2023, 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.
|
||||
*/
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<EditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/editText"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 android.widget;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.android.frameworks.coretests.R;
|
||||
|
||||
public class TextViewContextMenuActivity extends Activity {
|
||||
@Override
|
||||
public void onCreate(Bundle savedBundleInstance) {
|
||||
super.onCreate(savedBundleInstance);
|
||||
setContentView(R.layout.textview_contextmenu);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 android.widget;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyChar;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.RemoteAction;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.textclassifier.TextClassification;
|
||||
|
||||
import androidx.test.annotation.UiThreadTest;
|
||||
import androidx.test.filters.MediumTest;
|
||||
import androidx.test.rule.ActivityTestRule;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.frameworks.coretests.R;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
/**
|
||||
* TextViewTest tests {@link TextView}.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class TextViewContextMenuTest {
|
||||
private static final String INTENT_ACTION_MOCK_ACTION_TEXT_CLASSIFICATION =
|
||||
"android.text.coretest.textclassifiation";
|
||||
private static final String ACTION_TITLE = "ACTION_TITLE";
|
||||
private static final String ACTION_DESCRIPTION = "ACTION_DESCRIPTION";
|
||||
|
||||
@Rule
|
||||
public final ActivityTestRule<TextViewContextMenuActivity> mActivityRule =
|
||||
new ActivityTestRule<>(TextViewContextMenuActivity.class);
|
||||
|
||||
// Setup MenuItem mock with chaining.
|
||||
private MenuItem newMockMenuItem() {
|
||||
MenuItem mockItem = mock(MenuItem.class);
|
||||
when(mockItem.setAlphabeticShortcut(anyChar())).thenReturn(mockItem);
|
||||
when(mockItem.setAlphabeticShortcut(anyChar(), anyInt())).thenReturn(mockItem);
|
||||
when(mockItem.setOnMenuItemClickListener(any())).thenReturn(mockItem);
|
||||
when(mockItem.setEnabled(anyBoolean())).thenReturn(mockItem);
|
||||
when(mockItem.setIcon(any())).thenReturn(mockItem);
|
||||
when(mockItem.setIntent(any())).thenReturn(mockItem);
|
||||
when(mockItem.setContentDescription(any())).thenReturn(mockItem);
|
||||
return mockItem;
|
||||
}
|
||||
|
||||
private RemoteAction createRemoteAction() {
|
||||
Intent intent = new Intent(INTENT_ACTION_MOCK_ACTION_TEXT_CLASSIFICATION)
|
||||
.setPackage(mActivity.getPackageName());
|
||||
PendingIntent pIntent = PendingIntent.getBroadcast(mActivity, 0, intent,
|
||||
PendingIntent.FLAG_MUTABLE);
|
||||
return new RemoteAction(
|
||||
Icon.createWithResource(mActivity, android.R.drawable.btn_star),
|
||||
ACTION_TITLE, ACTION_DESCRIPTION, pIntent);
|
||||
}
|
||||
|
||||
private Activity mActivity;
|
||||
private SelectionActionModeHelper mMockHelper;
|
||||
private Editor.AssistantCallbackHelper mCallbackHelper;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mActivity = mActivityRule.getActivity();
|
||||
EditText et = mActivity.findViewById(R.id.editText);
|
||||
|
||||
mMockHelper = mock(SelectionActionModeHelper.class);
|
||||
mCallbackHelper = et.getEditorForTesting().new AssistantCallbackHelper(mMockHelper);
|
||||
}
|
||||
|
||||
@UiThreadTest
|
||||
@Test
|
||||
public void testNoMenuInteraction_noTextClassification() {
|
||||
when(mMockHelper.getTextClassification()).thenReturn(null);
|
||||
ContextMenu menu = mock(ContextMenu.class);
|
||||
mCallbackHelper.updateAssistMenuItems(menu, null);
|
||||
verifyNoMoreInteractions(menu);
|
||||
}
|
||||
|
||||
@UiThreadTest
|
||||
@Test
|
||||
public void testAddMenuForTextClassification() {
|
||||
// Setup
|
||||
RemoteAction action = createRemoteAction();
|
||||
TextClassification classification = new TextClassification.Builder()
|
||||
.addAction(action).build();
|
||||
when(mMockHelper.getTextClassification()).thenReturn(classification);
|
||||
|
||||
ContextMenu menu = mock(ContextMenu.class);
|
||||
MenuItem mockMenuItem = newMockMenuItem();
|
||||
when(menu.add(anyInt(), anyInt(), anyInt(), any())).thenReturn(mockMenuItem);
|
||||
|
||||
// Execute
|
||||
mCallbackHelper.updateAssistMenuItems(menu, null);
|
||||
|
||||
// Verify
|
||||
ArgumentCaptor<Integer> idCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||
ArgumentCaptor<CharSequence> titleCaptor = ArgumentCaptor.forClass(CharSequence.class);
|
||||
|
||||
verify(menu, times(1)).add(anyInt(), idCaptor.capture(), anyInt(), titleCaptor.capture());
|
||||
|
||||
assertThat(idCaptor.getValue()).isEqualTo(TextView.ID_ASSIST);
|
||||
assertThat(titleCaptor.getValue().toString()).isEqualTo(ACTION_TITLE);
|
||||
verify(mockMenuItem, times(1)).setContentDescription(eq(ACTION_DESCRIPTION));
|
||||
}
|
||||
|
||||
@UiThreadTest
|
||||
@Test
|
||||
public void testAddMenuForLegacyTextClassification() {
|
||||
// Setup
|
||||
Intent intent = new Intent(INTENT_ACTION_MOCK_ACTION_TEXT_CLASSIFICATION)
|
||||
.setPackage(mActivity.getPackageName());
|
||||
TextClassification classification = new TextClassification.Builder()
|
||||
.setIcon(mActivity.getResources().getDrawable(android.R.drawable.star_on))
|
||||
.setLabel(ACTION_TITLE)
|
||||
.setIntent(intent)
|
||||
.build();
|
||||
when(mMockHelper.getTextClassification()).thenReturn(classification);
|
||||
|
||||
ContextMenu menu = mock(ContextMenu.class);
|
||||
MenuItem mockMenuItem = newMockMenuItem();
|
||||
when(menu.add(anyInt(), anyInt(), anyInt(), any())).thenReturn(mockMenuItem);
|
||||
|
||||
// Execute
|
||||
mCallbackHelper.updateAssistMenuItems(menu, null);
|
||||
|
||||
// Verify
|
||||
ArgumentCaptor<Integer> idCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||
ArgumentCaptor<CharSequence> titleCaptor = ArgumentCaptor.forClass(CharSequence.class);
|
||||
|
||||
verify(menu, times(1)).add(anyInt(), idCaptor.capture(), anyInt(), titleCaptor.capture());
|
||||
|
||||
assertThat(idCaptor.getValue()).isEqualTo(TextView.ID_ASSIST);
|
||||
assertThat(titleCaptor.getValue().toString()).isEqualTo(ACTION_TITLE);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user