24

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.

Text selection handle with paste menu

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).

CJBS
  • 15,147
  • 6
  • 86
  • 135
  • @MarcinOrlowski There are business reasons - that I can't discuss here - as to why this need to be done. This is not for an app on the 'app store'. – CJBS Jan 12 '15 at 21:27
  • http://stackoverflow.com/questions/6275299/how-to-disable-copy-paste-from-to-edittext – Bhavin Chauhan Jan 19 '15 at 04:17
  • @BhavinChauhan - thanks for the link, but all answers there are various flavours of disabling long click, disabling the action toolbar's copy/paste (both of which I've done), or altering the text in the clipboard. – CJBS Jan 20 '15 at 06:00
  • Hi, have found any solution except custom edittext ? The same scenario, iwant to use it dynamically. Is there any suggestions? – SARATH V Oct 13 '20 at 12:04

16 Answers16

28

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:

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.

Community
  • 1
  • 1
CJBS
  • 15,147
  • 6
  • 86
  • 135
  • Your solution doesn't work unfortunately, because even if you created boolean canPaste() methopd in your custom EditText, the original base method will be called from the Editor class. So the private method cannot be owerriten the such way to call false. – Vladimir Ryhlitskiy May 27 '15 at 16:08
  • @VladimirRyhlitskiy Have you actually tried the code, or is this supposition (or a defect in your compiler)? The code that is posted above is alive and working well for me. In my stack trace, the call from `Editor$ActionPopupWindow.show()` (where it calls `boolean canPaste = mTextView.canPaste();`) calls `NoMenuEditText.canPaste()`, which makes sense, because the canPaste() in the local context (that of SimpleEditText) will be called first. Perhaps you should try it. – CJBS May 27 '15 at 16:52
  • 1
    It is not working on my Nexus 5 with Android 5.1. Tried it right now. I have the value in clipboard and I see the following with debugger: http://c2n.me/3inoPZG. So the mTextView has correct type - my custom edit text where canPaste() method with returned false value was defined. And the value is true anyway. Also, I have breakpoint in the canPaste() method of my view and the code doesn't go to it. – Vladimir Ryhlitskiy May 27 '15 at 17:56
  • @VladimirRyhlitskiy I tested this on v4.4.2. The underlying code may have changed between v4.4.2 and v5.1. – CJBS May 27 '15 at 18:07
  • I had considered using reflection to solve this, and although the approach in this (my) answer isn't perfect (replacing a private method), I prefer to not use reflection for performance and type-safety reasons. Thanks for sharing - one benefit is that it's far less code than I've written to solve it. – CJBS May 27 '15 at 18:16
  • yes, you are right. It works on Samsung with android 4.3 well, and canPaste() method of my custom view is called and returns false. It is very strange for me that the method is called... – Vladimir Ryhlitskiy May 27 '15 at 18:21
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/79036/discussion-between-cjbs-and-vladimir-ryhlitskiy). – CJBS May 28 '15 at 15:59
  • good anwser, but on 5.1 ART runtime, seems no longer override the package-private method any more. – Gohan Sep 03 '15 at 07:11
  • @Gohan Have a look at Vladimir's answer below -- he asserts that it works on Android v5.1, although it does use Reflection. http://stackoverflow.com/a/30490027/3063884 – CJBS Sep 03 '15 at 15:41
  • @CJBS tried that one, but not work on my nexus 6 with 5.1.1 android, mInsertionControllerEnabled set false, still show the paste tips. (I want to make a edittext with copyable readonly text, but the annoying paste tips always popup) – Gohan Sep 06 '15 at 09:08
  • 38
    why the hell android doesn't give us the option to disable copy paste ,everything need to be a hack in android – has19 Aug 12 '16 at 12:57
  • above class not working for me as well,i need to disable only paste option. – Richa Feb 18 '20 at 07:37
  • This is not working Redmi MI phones. Menu is showing on Tap including copy/paste options. – Sharp Edge Mar 25 '22 at 06:41
22

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"
Murtaza Khursheed Hussain
  • 15,176
  • 7
  • 58
  • 83
  • 2
    This doesn't solve my problem. (I already have this implemented, but I probably should have mentioned in the question). Disabling the long click prevents the "Paste/Replace" menu from appearing when the mouse is clicked and held (i.e. long touch). The scenario I'm describing is when the mouse is clicked (single touch) within the text. When this happens, the text selection handle appears, and when the text selection handle itself is clicked, then the "paste" menu option appears. This is what I'm trying to prevent. – CJBS Jan 19 '15 at 18:49
  • (Note that this was also an answer from the question I cited in the post - http://stackoverflow.com/a/13821250/3063884 and http://stackoverflow.com/a/26029022/3063884) – CJBS Jan 19 '15 at 19:06
  • usually answer is related to most nearest situation. let me look into your problem again. – Murtaza Khursheed Hussain Jan 20 '15 at 04:50
  • A couple of things (I noticed your edit): I don't want to disable the text selection handle (from question: "I want to keep this behaviour.") -- I want that for allowing easy repositioning of the cursor. It's the "PASTE" context menu that I don't want. The update to your post would make the cursor handle disappear (unless I'm mistaken). Also, to reproduce my scenario, (and it might sound obvious), but for the "PASTE" menu to appear, there needs to be some text in the clipboard. – CJBS Jan 20 '15 at 05:02
12

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.

Anton Tananaev
  • 2,458
  • 1
  • 25
  • 48
9

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);
}
Libin
  • 16,967
  • 7
  • 61
  • 83
6

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

  • not work on my nexus 6 with 5.1.1 android, mInsertionControllerEnabled set false, still show the paste tips. (I want to make a edittext with copyable readonly text, but the annoying paste tips always popup) – Gohan Sep 06 '15 at 09:08
6

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
}
Benjamin Charais
  • 1,248
  • 8
  • 17
  • It's better to remove items not in `onCreateActionMode`, but in `onPrepareActionMode`, because the first one runs only during the initialization, whereas the latter one is called also when the menu refreshes. See [the docs](https://developer.android.com/reference/android/view/ActionMode.Callback) – Dmitry K Jul 20 '21 at 07:24
1
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.
user1242611
  • 75
  • 1
  • 7
  • -1 This is almost an exact copy of the answer above by @HardikChauhan http://stackoverflow.com/a/27965349/3063884 -- also see my comment following that answer regarding why it doesn't address the problem (paste menu appears when the Text Selection Handle is clicked). – CJBS Aug 28 '15 at 16:01
1

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?) {}
}

}

mani
  • 709
  • 6
  • 13
1

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);
}
Said Torres
  • 581
  • 3
  • 14
0

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;
Thalescm
  • 155
  • 1
  • 10
  • 1
    What version of the Android SDK is this solution for? This might prevent the paste operation, but doesn't do anything to prevent the 'PASTE' menu from appearing, which was what the original question was for [onTextContextMenuItem(int id) -- Called when a context menu option for the text view is selected.] – CJBS Jul 19 '16 at 21:00
  • Oh I see, this solution worked for my app (which is from API 15+). But I couldn't get the paste from appearing, I've tried all solutions. My user can't long click, can't select the word and any of that, but if he clicks the cursor, the paste menu always appear :( So my thought was, If this will aways appear, at least I'll let him do all other common operations and just block pasting from happen. It's wrong from an UX perspective, but I in my case this functionallity was really needed – Thalescm Jul 20 '16 at 14:23
  • I understand your frustration! – CJBS Jul 20 '16 at 16:26
0

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);
Donald Duck
  • 8,409
  • 22
  • 75
  • 99
Mustafa
  • 69
  • 1
  • 4
  • Please add syntax highlighting to whole code, not just one line. This improves readability. – Top Sekret Jan 31 '17 at 20:22
  • 1
    This might be acceptable in a totally controlled environment, but usually a user puts something on the clipboard with the intention of pasting it later - clearing it without notification would be very annoying. – CJBS Jan 31 '17 at 21:26
0

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.

  1. To disable copy/paste popup.
  2. To disable text selection handler.
  3. Still showing cursor at end of text.
  4. Still showing keyboard.

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);
}

enter image description here

Note, blinking cursor is still showing at end of text. Just that the screenshot unable to capture it.

Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875
  • It will work if and only if you have only 1 EditText in a screen. And yes there will be no menu shown. – Navinpd Jul 02 '18 at 05:39
0

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) {}
    }
Richa
  • 3,165
  • 1
  • 22
  • 26
-1

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.

Ahmed Hegazy
  • 12,395
  • 5
  • 41
  • 64
Hardik Chauhan
  • 2,750
  • 15
  • 30
  • 2
    -1 Thanks for the response. I mentioned in the quetsion that "I've disabled the appearance of the text editing function actionbar (copy/Paste etc.) by consuming the ActionMode Callback event". This response is a copy/paste from an answer in that question (http://stackoverflow.com/a/22756538/3063884) that 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. However, this doesn't address my problem - that the paste menu appears when the Text Selection Handle is clicked. – CJBS Jan 19 '15 at 18:36
-1

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;
}
Oleksandr Kucherenko
  • 1,921
  • 1
  • 17
  • 21
-1

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 enter image description here

Harsh Mittal
  • 449
  • 3
  • 10
  • Ideally this would be included as code-formatted code, rather than an image, which can't be copied/pasted or searched... – CJBS Mar 08 '21 at 23:37
  • you can check in above link added with the image – Harsh Mittal Mar 10 '21 at 07:29
  • 1
    links can become stale, thus on SO, it's preferred to copy and duplicate content related to the answer to be part of the answer. – CJBS Mar 13 '21 at 23:32