1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.calculator2; 18 19 import android.content.ClipData; 20 import android.content.ClipboardManager; 21 import android.content.Context; 22 import android.content.res.Resources; 23 import android.text.Editable; 24 import android.text.InputType; 25 import android.text.TextUtils; 26 import android.util.AttributeSet; 27 import android.util.Log; 28 import android.view.ActionMode; 29 import android.view.ContextMenu; 30 import android.view.Menu; 31 import android.view.MenuItem; 32 import android.view.MotionEvent; 33 import android.widget.EditText; 34 import android.widget.Toast; 35 36 public class CalculatorEditText extends EditText { 37 38 private static final String LOG_TAG = "Calculator2"; 39 private static final int CUT = 0; 40 private static final int COPY = 1; 41 private static final int PASTE = 2; 42 private String[] mMenuItemsStrings; 43 44 public CalculatorEditText(Context context, AttributeSet attrs) { 45 super(context, attrs); 46 setCustomSelectionActionModeCallback(new NoTextSelectionMode()); 47 setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); 48 } 49 50 @Override 51 public boolean onTouchEvent(MotionEvent event) { 52 if (event.getActionMasked() == MotionEvent.ACTION_UP) { 53 // Hack to prevent keyboard and insertion handle from showing. 54 cancelLongPress(); 55 } 56 return super.onTouchEvent(event); 57 } 58 59 @Override 60 public boolean performLongClick() { 61 showContextMenu(); 62 return true; 63 } 64 65 private class MenuHandler implements MenuItem.OnMenuItemClickListener { 66 public boolean onMenuItemClick(MenuItem item) { 67 return onTextContextMenuItem(item.getTitle()); 68 } 69 } 70 71 public boolean onTextContextMenuItem(CharSequence title) { 72 boolean handled = false; 73 if (TextUtils.equals(title, mMenuItemsStrings[CUT])) { 74 cutContent(); 75 handled = true; 76 } else if (TextUtils.equals(title, mMenuItemsStrings[COPY])) { 77 copyContent(); 78 handled = true; 79 } else if (TextUtils.equals(title, mMenuItemsStrings[PASTE])) { 80 pasteContent(); 81 handled = true; 82 } 83 return handled; 84 } 85 86 @Override 87 public void onCreateContextMenu(ContextMenu menu) { 88 MenuHandler handler = new MenuHandler(); 89 if (mMenuItemsStrings == null) { 90 Resources resources = getResources(); 91 mMenuItemsStrings = new String[3]; 92 mMenuItemsStrings[CUT] = resources.getString(android.R.string.cut); 93 mMenuItemsStrings[COPY] = resources.getString(android.R.string.copy); 94 mMenuItemsStrings[PASTE] = resources.getString(android.R.string.paste); 95 } 96 for (int i = 0; i < mMenuItemsStrings.length; i++) { 97 menu.add(Menu.NONE, i, i, mMenuItemsStrings[i]).setOnMenuItemClickListener(handler); 98 } 99 if (getText().length() == 0) { 100 menu.getItem(CUT).setVisible(false); 101 menu.getItem(COPY).setVisible(false); 102 } 103 ClipData primaryClip = getPrimaryClip(); 104 if (primaryClip == null || primaryClip.getItemCount() == 0 105 || !canPaste(primaryClip.getItemAt(0).coerceToText(getContext()))) { 106 menu.getItem(PASTE).setVisible(false); 107 } 108 } 109 110 private void setPrimaryClip(ClipData clip) { 111 ClipboardManager clipboard = (ClipboardManager) getContext(). 112 getSystemService(Context.CLIPBOARD_SERVICE); 113 clipboard.setPrimaryClip(clip); 114 } 115 116 private void copyContent() { 117 final Editable text = getText(); 118 int textLength = text.length(); 119 setSelection(0, textLength); 120 ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService( 121 Context.CLIPBOARD_SERVICE); 122 clipboard.setPrimaryClip(ClipData.newPlainText(null, text)); 123 Toast.makeText(getContext(), R.string.text_copied_toast, Toast.LENGTH_SHORT).show(); 124 setSelection(textLength); 125 } 126 127 private void cutContent() { 128 final Editable text = getText(); 129 int textLength = text.length(); 130 setSelection(0, textLength); 131 setPrimaryClip(ClipData.newPlainText(null, text)); 132 ((Editable) getText()).delete(0, textLength); 133 setSelection(0); 134 } 135 136 private ClipData getPrimaryClip() { 137 ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService( 138 Context.CLIPBOARD_SERVICE); 139 return clipboard.getPrimaryClip(); 140 } 141 142 private void pasteContent() { 143 ClipData clip = getPrimaryClip(); 144 if (clip != null) { 145 for (int i = 0; i < clip.getItemCount(); i++) { 146 CharSequence paste = clip.getItemAt(i).coerceToText(getContext()); 147 if (canPaste(paste)) { 148 ((Editable) getText()).insert(getSelectionEnd(), paste); 149 } 150 } 151 } 152 } 153 154 private boolean canPaste(CharSequence paste) { 155 boolean canPaste = true; 156 try { 157 Float.parseFloat(paste.toString()); 158 } catch (NumberFormatException e) { 159 Log.e(LOG_TAG, "Error turning string to integer. Ignoring paste.", e); 160 canPaste = false; 161 } 162 return canPaste; 163 } 164 165 class NoTextSelectionMode implements ActionMode.Callback { 166 @Override 167 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 168 return false; 169 } 170 171 @Override 172 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 173 copyContent(); 174 // Prevents the selection action mode on double tap. 175 return false; 176 } 177 178 @Override 179 public void onDestroyActionMode(ActionMode mode) {} 180 181 @Override 182 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 183 return false; 184 } 185 } 186 } 187