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
17package com.android.calculator2;
18
19import android.content.ClipData;
20import android.content.ClipboardManager;
21import android.content.Context;
22import android.content.res.Resources;
23import android.text.Editable;
24import android.text.InputType;
25import android.text.TextUtils;
26import android.util.AttributeSet;
27import android.util.Log;
28import android.view.ActionMode;
29import android.view.ContextMenu;
30import android.view.Menu;
31import android.view.MenuItem;
32import android.view.MotionEvent;
33import android.widget.EditText;
34import android.widget.Toast;
35
36public 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