Reputation: 15685
My goal is to have an EditText
that has no fancy features, just the Text Selection Handler for moving the cursor more easily -- so no context menus or pop-ups.
I've disabled the appearance of the text editing function actionbar (copy/Paste etc.) by consuming the ActionMode Callback event, as per this solution.
The middle Middle Text Select Handle (see image below) still appears when text exists in the field and a click occurs within the text. Great! I want to keep this behaviour. What I DON'T want is the "PASTE" menu to appear when the Text Select Handle itself is clicked.
I have also disabled long-click input for the EditText by setting android:longClickable="false"
in the styles XML. Disabling the long click prevents the "Paste/Replace" menu from appearing when the mouse is clicked and held (i.e. long touch), however when the mouse is clicked (single touch) within the text, the text selection handle appears, and when the text selection handle itself is clicked, then the "paste" menu option appears (when there's text in the clipboard). This is what I'm trying to prevent.
From what I can see from the source, the ActionPopupWindow
is what pops up with the PASTE/REPLACE options. ActionPopupWindow is a protected variable (mActionPopupWindow) in the private abstract class HandleView within public class android.widget.Editor...
Short of disabling the clipboard service or editing the Android Source code, is there a way that I can prevent this from showing? I tried to define a new style for android:textSelectHandleWindowStyle
, and set android:visibility
to gone
, but it didn't work (app froze for a while when it would otherwise have shown).
Upvotes: 24
Views: 44222
Reputation: 653
With this simple code you can disable Text Selection Toolbar:
// MainActivity.java
@Override
public void onActionModeStarted(android.view.ActionMode mode) {
mode.getMenu().clear();
super.onActionModeStarted(mode);
}
Upvotes: 2
Reputation: 1368
You can completely remove the menuItem by doing the following:
Java:
ActionMode.Callback callback = new ActionMode.Callback() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
if (menu != null) {
menu.removeItem(android.R.id.paste);
}
return true;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
}
};
mEditText.setCustomInsertionActionModeCallback(callback);
mEditText.setCustomSelectionActionModeCallback(callback);
Kotlin:
val callback = object : ActionMode.Callback {
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
return false
}
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
return true
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
menu?.removeItem(android.R.id.paste)
return true
}
override fun onDestroyActionMode(mode: ActionMode?) {}
}
then for use site in EditText :
fun preventPaste() {
customInsertionActionModeCallback = callback
customSelectionActionModeCallback = callback
}
Upvotes: 6
Reputation: 459
I just used some of the above solutions in Android 11 and it's working fine, you can use the below gist.
https://gist.github.com/harshmittal2810/26429eb426dd1b31750cb33b47f449a6
Upvotes: -1
Reputation: 799
It got it fixed with all the mentioned below 3 changes
fun TextView.disableCopyPaste() {
isLongClickable = false. // change 1 , disable Long click
setTextIsSelectable(false). // change 2 , disable text selection click
//change 3 , return false from all actionmode
customSelectionActionModeCallback = object : ActionMode.Callback {
override fun onCreateActionMode(mode: ActionMode?, menu: Menu): Boolean {
return false
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu): Boolean {
return false
}
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem): Boolean {
return false
}
override fun onDestroyActionMode(mode: ActionMode?) {}
}
}
Upvotes: 2
Reputation: 3193
I have found one easy solution.Hope it will help to someone, Extend Edittetxt class and ovverride below methods.
Same way if you want to disable other options by comparing menu.getItem(i).getTitle()
you can do it.
private class ActionModeCallbackInterceptor implements ActionMode.Callback
{
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return true;
}
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
for(int i =0;i<menu.size();i++){
if(menu.getItem(i).getTitle().toString().equals("Clipboard")
|| menu.getItem(i).getTitle().toString().equals("Paste")) {
menu.getItem(i).setVisible(false);
}
}
return false;
}
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
public void onDestroyActionMode(ActionMode mode) {}
}
Upvotes: 0
Reputation: 2039
Just override one method:
@Override
protected MovementMethod getDefaultMovementMethod() {
// we don't need arrow key, return null will also disable the copy/paste/cut pop-up menu.
return null;
}
Upvotes: -1
Reputation: 42710
I found a simple yet reliable way. The idea is the consume away the touch event, to prevent the touch event reach underlining default code.
maskedEditText.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
focusAndShowKeyboard(view.getContext(), maskedEditText);
// Consume the event.
return true;
}
});
private static void focusAndShowKeyboard(Context context, EditText editText) {
editText.requestFocus();
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
}
Note, blinking cursor is still showing at end of text. Just that the screenshot unable to capture it.
Upvotes: 0
Reputation: 69
If you need remove the PASTE suggestion, clear the clipboard before the long click.
//class
ClipboardManager clipboard;
//oncreate
clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("","");
clipboard.setPrimaryClip(clip);
Upvotes: 0
Reputation: 2523
Here is a hack to disable "paste" popup. You have to override EditText
method:
@Override
public int getSelectionStart() {
for (StackTraceElement element : Thread.currentThread().getStackTrace()) {
if (element.getMethodName().equals("canPaste")) {
return -1;
}
}
return super.getSelectionStart();
}
This solution works on newer versions of Android as well, unlike the accepted answer.
Upvotes: 12
Reputation: 15685
Solution: Override isSuggestionsEnabled
and canPaste
in EditText
.
For the quick solution, copy the class below - this class overrides the EditText
class, and blocks all events accordingly.
For the gritty details, keep reading.
The solution lies in preventing PASTE/REPLACE menu from appearing in the show()
method of the (non-documented) android.widget.Editor
class. Before the menu appears, a check is done to if (!canPaste && !canSuggest) return;
. The two methods that are used as the basis to set these variables are both in the EditText
class:
isSuggestionsEnabled()
is public, and may thus be overridden.canPaste()
is not, and thus must be hidden by introducing a function of the same name in the derived class.So incorporating these updates into a class that also has the setCustomSelectionActionModeCallback, and the disabled long-click, here is the full class to prevent all editing (but still display the text selection handler) for controlling the cursor:
package com.cjbs.widgets;
import android.content.Context;
import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;
/**
* This is a thin veneer over EditText, with copy/paste/spell-check removed.
*/
public class NoMenuEditText extends EditText
{
private final Context context;
/** This is a replacement method for the base TextView class' method of the same name. This
* method is used in hidden class android.widget.Editor to determine whether the PASTE/REPLACE popup
* appears when triggered from the text insertion handle. Returning false forces this window
* to never appear.
* @return false
*/
boolean canPaste()
{
return false;
}
/** This is a replacement method for the base TextView class' method of the same name. This method
* is used in hidden class android.widget.Editor to determine whether the PASTE/REPLACE popup
* appears when triggered from the text insertion handle. Returning false forces this window
* to never appear.
* @return false
*/
@Override
public boolean isSuggestionsEnabled()
{
return false;
}
public NoMenuEditText(Context context)
{
super(context);
this.context = context;
init();
}
public NoMenuEditText(Context context, AttributeSet attrs)
{
super(context, attrs);
this.context = context;
init();
}
public NoMenuEditText(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
this.context = context;
init();
}
private void init()
{
this.setCustomSelectionActionModeCallback(new ActionModeCallbackInterceptor());
this.setLongClickable(false);
}
/**
* Prevents the action bar (top horizontal bar with cut, copy, paste, etc.) from appearing
* by intercepting the callback that would cause it to be created, and returning false.
*/
private class ActionModeCallbackInterceptor implements ActionMode.Callback
{
private final String TAG = NoMenuEditText.class.getSimpleName();
public boolean onCreateActionMode(ActionMode mode, Menu menu) { return false; }
public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; }
public boolean onActionItemClicked(ActionMode mode, MenuItem item) { return false; }
public void onDestroyActionMode(ActionMode mode) {}
}
}
I've tested this in Android v4.4.2 and v4.4.3.
Upvotes: 28
Reputation: 155
None of the above solutions worked for me. I've managed to do my solution (explanation after), which disabled pasting anything on the EditText while maintaining all other operations valid.
Mainly, you have to override this method on your implementation of EditText:
@Override
public boolean onTextContextMenuItem (int id) {
if (id == android.R.id.paste) return false;
return super.onTextContextMenuItem(id);
}
So investigating EditText code, after all checks, paste (and all ContextMenu
actions on the EditText) happen at a method called onTextContextMenuItem
:
public boolean onTextContextMenuItem(int id) {
int min = 0;
int max = mText.length();
if (isFocused()) {
final int selStart = getSelectionStart();
final int selEnd = getSelectionEnd();
min = Math.max(0, Math.min(selStart, selEnd));
max = Math.max(0, Math.max(selStart, selEnd));
}
switch (id) {
case ID_SELECT_ALL:
// This does not enter text selection mode. Text is highlighted, so that it can be
// bulk edited, like selectAllOnFocus does. Returns true even if text is empty.
selectAllText();
return true;
case ID_PASTE:
paste(min, max);
return true;
case ID_CUT:
setPrimaryClip(ClipData.newPlainText(null, getTransformedText(min, max)));
deleteText_internal(min, max);
stopSelectionActionMode();
return true;
case ID_COPY:
setPrimaryClip(ClipData.newPlainText(null, getTransformedText(min, max)));
stopSelectionActionMode();
return true;
}
return false;
}
If you notice, pasting will only occur when id == ID_PASTE
, so, again, looking at the EditText code:
static final int ID_PASTE = android.R.id.paste;
Upvotes: 0
Reputation: 17085
I don't find a way to hide the menu popup , But you can disable from pasting if user tap on the menu
Create a custom EditText
and override the onTextContextMenuItem
method and return false for android.R.id.paste
and android.R.id.pasteAsPlainText
menu id's.
@Override
public boolean onTextContextMenuItem(int id) {
switch (id){
case android.R.id.paste:
case android.R.id.pasteAsPlainText:
return false;
}
return super.onTextContextMenuItem(id);
}
Upvotes: 9
Reputation: 75
Use this in java file
if (android.os.Build.VERSION.SDK_INT < 11) {
editText.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
@Override`enter code here`
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
// TODO Auto-generated method stub
menu.clear();
}
});
} else {
editText.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
return false;
}
public void onDestroyActionMode(ActionMode mode) {
// TODO Auto-generated method stub
}
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
return false;
}
public boolean onActionItemClicked(ActionMode mode,
MenuItem item) {
// TODO Auto-generated method stub
return false;
}`enter code here`
});
}
With this code also add android:textSelectHandle="@drawable/handle"
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<size
android:height="0dp"
android:width="0dp" />
</shape>
By Using these two combinations my problem is solved.
Upvotes: 1
Reputation: 257
Found another solution when the blue view (insertion controller) is not appeared at all. I used reflection to set target boolean field of Editor class. Look at the android.widget.Editor and android.widget.TextView for more details.
Add the following code into your custom EditText (with all previous code in this topic):
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// setInsertionDisabled when user touches the view
this.setInsertionDisabled();
}
return super.onTouchEvent(event);
}
/**
* This method sets TextView#Editor#mInsertionControllerEnabled field to false
* to return false from the Editor#hasInsertionController() method to PREVENT showing
* of the insertionController from EditText
* The Editor#hasInsertionController() method is called in Editor#onTouchUpEvent(MotionEvent event) method.
*/
private void setInsertionDisabled() {
try {
Field editorField = TextView.class.getDeclaredField("mEditor");
editorField.setAccessible(true);
Object editorObject = editorField.get(this);
Class editorClass = Class.forName("android.widget.Editor");
Field mInsertionControllerEnabledField = editorClass.getDeclaredField("mInsertionControllerEnabled");
mInsertionControllerEnabledField.setAccessible(true);
mInsertionControllerEnabledField.set(editorObject, false);
}
catch (Exception ignored) {
// ignore exception here
}
}
Also, maybe you can find the better place than onTouch() to call the target method.
Tested on Android 5.1
Upvotes: 6
Reputation: 15336
or simply just use
yourEditText.setLongClickable(false);
OR in XML
android:longClickable="false"
Update
Actually the user wants to disable the text selection handle itself
1. Create a shape (handle.xml)
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<size
android:height="0dp"
android:width="0dp" />
</shape>
2. In your EditText
android:textSelectHandle="@drawable/handle"
Upvotes: 22
Reputation: 2746
You can use this code:
if (android.os.Build.VERSION.SDK_INT < 11) {
editText.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
// TODO Auto-generated method stub
menu.clear();
}
});
} else {
editText.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
return false;
}
public void onDestroyActionMode(ActionMode mode) {
// TODO Auto-generated method stub
}
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
return false;
}
public boolean onActionItemClicked(ActionMode mode,
MenuItem item) {
// TODO Auto-generated method stub
return false;
}
});
}
Returning false from onCreateActionMode
will disable the cut,copy,paste options in API level greater than 11.
Upvotes: -1