|
10 | 10 | import static com.facebook.react.uimanager.UIManagerHelper.getReactContext;
|
11 | 11 | import static com.facebook.react.views.text.TextAttributeProps.UNSET;
|
12 | 12 |
|
| 13 | +import android.content.ClipData; |
| 14 | +import android.content.ClipboardManager; |
13 | 15 | import android.content.Context;
|
14 | 16 | import android.graphics.Color;
|
15 | 17 | import android.graphics.Paint;
|
|
28 | 30 | import android.text.method.KeyListener;
|
29 | 31 | import android.text.method.QwertyKeyListener;
|
30 | 32 | import android.util.TypedValue;
|
| 33 | +import android.view.ActionMode; |
31 | 34 | import android.view.Gravity;
|
32 | 35 | import android.view.KeyEvent;
|
| 36 | +import android.view.Menu; |
| 37 | +import android.view.MenuItem; |
33 | 38 | import android.view.MotionEvent;
|
34 | 39 | import android.view.View;
|
35 | 40 | import android.view.accessibility.AccessibilityNodeInfo;
|
@@ -180,6 +185,35 @@ public boolean performAccessibilityAction(View host, int action, Bundle args) {
|
180 | 185 | }
|
181 | 186 | };
|
182 | 187 | ViewCompat.setAccessibilityDelegate(this, editTextAccessibilityDelegate);
|
| 188 | + ActionMode.Callback customActionModeCallback = |
| 189 | + new ActionMode.Callback() { |
| 190 | + /* |
| 191 | + * Editor onCreateActionMode adds the cut, copy, paste, share, autofill, |
| 192 | + * and paste as plain text items to the context menu. |
| 193 | + */ |
| 194 | + @Override |
| 195 | + public boolean onCreateActionMode(ActionMode mode, Menu menu) { |
| 196 | + menu.removeItem(android.R.id.pasteAsPlainText); |
| 197 | + return true; |
| 198 | + } |
| 199 | + |
| 200 | + @Override |
| 201 | + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { |
| 202 | + return true; |
| 203 | + } |
| 204 | + |
| 205 | + @Override |
| 206 | + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { |
| 207 | + return false; |
| 208 | + } |
| 209 | + |
| 210 | + @Override |
| 211 | + public void onDestroyActionMode(ActionMode mode) {} |
| 212 | + }; |
| 213 | + setCustomSelectionActionModeCallback(customActionModeCallback); |
| 214 | + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { |
| 215 | + setCustomInsertionActionModeCallback(customActionModeCallback); |
| 216 | + } |
183 | 217 | }
|
184 | 218 |
|
185 | 219 | @Override
|
@@ -268,6 +302,37 @@ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
268 | 302 | return inputConnection;
|
269 | 303 | }
|
270 | 304 |
|
| 305 | + /* |
| 306 | + * Called when a context menu option for the text view is selected. |
| 307 | + * React Native replaces copy (as rich text) with copy as plain text. |
| 308 | + */ |
| 309 | + @Override |
| 310 | + public boolean onTextContextMenuItem(int id) { |
| 311 | + if (id == android.R.id.paste) { |
| 312 | + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { |
| 313 | + id = android.R.id.pasteAsPlainText; |
| 314 | + } else { |
| 315 | + ClipboardManager clipboard = |
| 316 | + (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); |
| 317 | + ClipData previousClipData = clipboard.getPrimaryClip(); |
| 318 | + if (previousClipData != null) { |
| 319 | + for (int i = 0; i < previousClipData.getItemCount(); i++) { |
| 320 | + final CharSequence text = previousClipData.getItemAt(i).coerceToText(getContext()); |
| 321 | + final CharSequence paste = (text instanceof Spanned) ? text.toString() : text; |
| 322 | + if (paste != null) { |
| 323 | + ClipData clipData = ClipData.newPlainText(null, text); |
| 324 | + clipboard.setPrimaryClip(clipData); |
| 325 | + } |
| 326 | + } |
| 327 | + boolean actionPerformed = super.onTextContextMenuItem(id); |
| 328 | + clipboard.setPrimaryClip(previousClipData); |
| 329 | + return actionPerformed; |
| 330 | + } |
| 331 | + } |
| 332 | + } |
| 333 | + return super.onTextContextMenuItem(id); |
| 334 | + } |
| 335 | + |
271 | 336 | @Override
|
272 | 337 | public void clearFocus() {
|
273 | 338 | setFocusableInTouchMode(false);
|
|
0 commit comments