|
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; |
@@ -181,6 +186,35 @@ public boolean performAccessibilityAction(View host, int action, Bundle args) { |
181 | 186 | } |
182 | 187 | }; |
183 | 188 | ViewCompat.setAccessibilityDelegate(this, editTextAccessibilityDelegate); |
| 189 | + ActionMode.Callback customActionModeCallback = |
| 190 | + new ActionMode.Callback() { |
| 191 | + /* |
| 192 | + * Editor onCreateActionMode adds the cut, copy, paste, share, autofill, |
| 193 | + * and paste as plain text items to the context menu. |
| 194 | + */ |
| 195 | + @Override |
| 196 | + public boolean onCreateActionMode(ActionMode mode, Menu menu) { |
| 197 | + menu.removeItem(android.R.id.pasteAsPlainText); |
| 198 | + return true; |
| 199 | + } |
| 200 | + |
| 201 | + @Override |
| 202 | + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { |
| 203 | + return true; |
| 204 | + } |
| 205 | + |
| 206 | + @Override |
| 207 | + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { |
| 208 | + return false; |
| 209 | + } |
| 210 | + |
| 211 | + @Override |
| 212 | + public void onDestroyActionMode(ActionMode mode) {} |
| 213 | + }; |
| 214 | + setCustomSelectionActionModeCallback(customActionModeCallback); |
| 215 | + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { |
| 216 | + setCustomInsertionActionModeCallback(customActionModeCallback); |
| 217 | + } |
184 | 218 | } |
185 | 219 |
|
186 | 220 | @Override |
@@ -269,6 +303,37 @@ public InputConnection onCreateInputConnection(EditorInfo outAttrs) { |
269 | 303 | return inputConnection; |
270 | 304 | } |
271 | 305 |
|
| 306 | + /* |
| 307 | + * Called when a context menu option for the text view is selected. |
| 308 | + * React Native replaces copy (as rich text) with copy as plain text. |
| 309 | + */ |
| 310 | + @Override |
| 311 | + public boolean onTextContextMenuItem(int id) { |
| 312 | + if (id == android.R.id.paste) { |
| 313 | + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { |
| 314 | + id = android.R.id.pasteAsPlainText; |
| 315 | + } else { |
| 316 | + ClipboardManager clipboard = |
| 317 | + (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); |
| 318 | + ClipData previousClipData = clipboard.getPrimaryClip(); |
| 319 | + if (previousClipData != null) { |
| 320 | + for (int i = 0; i < previousClipData.getItemCount(); i++) { |
| 321 | + final CharSequence text = previousClipData.getItemAt(i).coerceToText(getContext()); |
| 322 | + final CharSequence paste = (text instanceof Spanned) ? text.toString() : text; |
| 323 | + if (paste != null) { |
| 324 | + ClipData clipData = ClipData.newPlainText(null, text); |
| 325 | + clipboard.setPrimaryClip(clipData); |
| 326 | + } |
| 327 | + } |
| 328 | + boolean actionPerformed = super.onTextContextMenuItem(id); |
| 329 | + clipboard.setPrimaryClip(previousClipData); |
| 330 | + return actionPerformed; |
| 331 | + } |
| 332 | + } |
| 333 | + } |
| 334 | + return super.onTextContextMenuItem(id); |
| 335 | + } |
| 336 | + |
272 | 337 | @Override |
273 | 338 | public void clearFocus() { |
274 | 339 | setFocusableInTouchMode(false); |
|
0 commit comments