/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.calculator2; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.res.Resources; import android.text.Editable; import android.text.InputType; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.view.ActionMode; import android.view.ContextMenu; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.EditText; import android.widget.Toast; import com.google.common.collect.ImmutableMap; public class CalculatorEditText extends EditText { private static final String LOG_TAG = "Calculator2"; private static final int CUT = 0; private static final int COPY = 1; private static final int PASTE = 2; private String[] mMenuItemsStrings; private ImmutableMap sReplacementTable; private String[] sOperators; public CalculatorEditText(Context context, AttributeSet attrs) { super(context, attrs); setCustomSelectionActionModeCallback(new NoTextSelectionMode()); setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); } @Override public boolean onTouchEvent(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_UP) { // Hack to prevent keyboard and insertion handle from showing. cancelLongPress(); } return super.onTouchEvent(event); } @Override public boolean performLongClick() { showContextMenu(); return true; } @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); String mathText = mathParse(getText().toString()); // Parse the string into something more "mathematical" sounding. if (!TextUtils.isEmpty(mathText)) { event.getText().clear(); event.getText().add(mathText); setContentDescription(mathText); } } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setText(mathParse(getText().toString())); } @Override public void onPopulateAccessibilityEvent(AccessibilityEvent event) { // Do nothing. } private String mathParse(String plainText) { String parsedText = plainText; if (!TextUtils.isEmpty(parsedText)) { // Initialize replacement table. initializeReplacementTable(); for (String operator : sOperators) { if (sReplacementTable.containsKey(operator)) { parsedText = parsedText.replace(operator, sReplacementTable.get(operator)); } } } return parsedText; } private synchronized void initializeReplacementTable() { if (sReplacementTable == null) { ImmutableMap.Builder builder = ImmutableMap.builder(); Resources res = getContext().getResources(); sOperators = res.getStringArray(R.array.operators); String[] descs = res.getStringArray(R.array.operatorDescs); int pos = 0; for (String key : sOperators) { builder.put(key, descs[pos]); pos++; } sReplacementTable = builder.build(); } } private class MenuHandler implements MenuItem.OnMenuItemClickListener { public boolean onMenuItemClick(MenuItem item) { return onTextContextMenuItem(item.getTitle()); } } public boolean onTextContextMenuItem(CharSequence title) { boolean handled = false; if (TextUtils.equals(title, mMenuItemsStrings[CUT])) { cutContent(); handled = true; } else if (TextUtils.equals(title, mMenuItemsStrings[COPY])) { copyContent(); handled = true; } else if (TextUtils.equals(title, mMenuItemsStrings[PASTE])) { pasteContent(); handled = true; } return handled; } @Override public void onCreateContextMenu(ContextMenu menu) { MenuHandler handler = new MenuHandler(); if (mMenuItemsStrings == null) { Resources resources = getResources(); mMenuItemsStrings = new String[3]; mMenuItemsStrings[CUT] = resources.getString(android.R.string.cut); mMenuItemsStrings[COPY] = resources.getString(android.R.string.copy); mMenuItemsStrings[PASTE] = resources.getString(android.R.string.paste); } for (int i = 0; i < mMenuItemsStrings.length; i++) { menu.add(Menu.NONE, i, i, mMenuItemsStrings[i]).setOnMenuItemClickListener(handler); } if (getText().length() == 0) { menu.getItem(CUT).setVisible(false); menu.getItem(COPY).setVisible(false); } ClipData primaryClip = getPrimaryClip(); if (primaryClip == null || primaryClip.getItemCount() == 0 || !canPaste(primaryClip.getItemAt(0).coerceToText(getContext()))) { menu.getItem(PASTE).setVisible(false); } } private void setPrimaryClip(ClipData clip) { ClipboardManager clipboard = (ClipboardManager) getContext(). getSystemService(Context.CLIPBOARD_SERVICE); clipboard.setPrimaryClip(clip); } private void copyContent() { final Editable text = getText(); int textLength = text.length(); setSelection(0, textLength); ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService( Context.CLIPBOARD_SERVICE); clipboard.setPrimaryClip(ClipData.newPlainText(null, text)); Toast.makeText(getContext(), R.string.text_copied_toast, Toast.LENGTH_SHORT).show(); setSelection(textLength); } private void cutContent() { final Editable text = getText(); int textLength = text.length(); setSelection(0, textLength); setPrimaryClip(ClipData.newPlainText(null, text)); ((Editable) getText()).delete(0, textLength); setSelection(0); } private ClipData getPrimaryClip() { ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService( Context.CLIPBOARD_SERVICE); return clipboard.getPrimaryClip(); } private void pasteContent() { ClipData clip = getPrimaryClip(); if (clip != null) { for (int i = 0; i < clip.getItemCount(); i++) { CharSequence paste = clip.getItemAt(i).coerceToText(getContext()); if (canPaste(paste)) { ((Editable) getText()).insert(getSelectionEnd(), paste); } } } } private boolean canPaste(CharSequence paste) { boolean canPaste = true; try { Float.parseFloat(paste.toString()); } catch (NumberFormatException e) { Log.e(LOG_TAG, "Error turning string to integer. Ignoring paste.", e); canPaste = false; } return canPaste; } class NoTextSelectionMode implements ActionMode.Callback { @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { return false; } @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { copyContent(); // Prevents the selection action mode on double tap. return false; } @Override public void onDestroyActionMode(ActionMode mode) {} @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; } } }