1b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project/*
2b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
3b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project *
4b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project * you may not use this file except in compliance with the License.
6b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project * You may obtain a copy of the License at
7b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project *
8b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project *
10b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project * See the License for the specific language governing permissions and
14b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project * limitations under the License.
15b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project */
16b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
17b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Projectpackage com.android.calculator2;
18b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
19582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikovimport com.android.calculator2.CalculatorDisplay.Scroll;
20582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov
21520cbbb8a5cb728eb9244a9c2ef5c917b56be756Mindy Pereiraimport android.text.TextUtils;
22b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Projectimport android.view.KeyEvent;
23b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Projectimport android.widget.EditText;
24b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Projectimport android.content.Context;
25caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereiraimport android.content.res.Resources;
26b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
27caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereiraimport java.util.HashMap;
2857fa18e9f76005d29cf1708017259d3527e68ca4Mindy Pereiraimport java.util.Locale;
29caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereiraimport java.util.Map.Entry;
30caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereiraimport java.util.Set;
3157fa18e9f76005d29cf1708017259d3527e68ca4Mindy Pereira
32b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Projectimport org.javia.arity.Symbols;
33b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Projectimport org.javia.arity.SyntaxException;
34b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
35b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Projectclass Logic {
36b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    private CalculatorDisplay mDisplay;
37b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    private Symbols mSymbols = new Symbols();
38b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    private History mHistory;
39b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    private String  mResult = "";
40b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    private boolean mIsError = false;
41e08c8309119c462f04ec33746d63b895e4f0417dMihai Preda    private int mLineLength = 0;
42b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
43b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    private static final String INFINITY_UNICODE = "\u221e";
44b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
45582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov    public static final String MARKER_EVALUATE_ON_RESUME = "?";
46582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov
47c249f2edd5d7e03cae1970576e5909d012e0166eVikram Aggarwal    // the two strings below are the result of Double.toString() for Infinity & NaN
48b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    // they are not output to the user and don't require internationalization
492501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov    private static final String INFINITY = "Infinity";
50b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    private static final String NAN      = "NaN";
51b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
52b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    static final char MINUS = '\u2212';
53b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
54b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    private final String mErrorString;
55b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
562501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov    public final static int DELETE_MODE_BACKSPACE = 0;
572501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov    public final static int DELETE_MODE_CLEAR = 1;
582501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov
592501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov    private int mDeleteMode = DELETE_MODE_BACKSPACE;
602501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov
612501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov    public interface Listener {
622501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov        void onDeleteModeChange();
632501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov    }
642501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov
652501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov    private Listener mListener;
66caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira    private Context mContext;
67caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira    private Set<Entry<String, String>> mTranslationsSet;
682501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov
692501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov    Logic(Context context, History history, CalculatorDisplay display) {
70caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira        mContext = context;
71caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira        mErrorString = mContext.getResources().getString(R.string.error);
72b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        mHistory = history;
73b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        mDisplay = display;
74b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        mDisplay.setLogic(this);
75b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    }
76b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
772501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov    public void setListener(Listener listener) {
782501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov        this.mListener = listener;
792501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov    }
802501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov
812501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov    public void setDeleteMode(int mode) {
822501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov        if (mDeleteMode != mode) {
832501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov            mDeleteMode = mode;
842501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov            mListener.onDeleteModeChange();
852501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov        }
862501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov    }
872501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov
882501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov    public int getDeleteMode() {
892501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov        return mDeleteMode;
902501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov    }
912501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov
92e08c8309119c462f04ec33746d63b895e4f0417dMihai Preda    void setLineLength(int nDigits) {
93e08c8309119c462f04ec33746d63b895e4f0417dMihai Preda        mLineLength = nDigits;
94e08c8309119c462f04ec33746d63b895e4f0417dMihai Preda    }
95e08c8309119c462f04ec33746d63b895e4f0417dMihai Preda
96b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    boolean eatHorizontalMove(boolean toLeft) {
97b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        EditText editText = mDisplay.getEditText();
98b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        int cursorPos = editText.getSelectionStart();
992501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov        return toLeft ? cursorPos == 0 : cursorPos >= editText.length();
100b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    }
101b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
102b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    private String getText() {
103b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        return mDisplay.getText().toString();
104b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    }
105b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
106b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    void insert(String delta) {
107c7e6cba324ca1b7e23a28cb86f4146568c1e8cb4Dmitri Plotnikov        mDisplay.insert(delta);
1082501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov        setDeleteMode(DELETE_MODE_BACKSPACE);
109b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    }
110b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
1119e131447a0270f66f025147a243a1fda0e3fea14Mindy Pereira    public void onTextChanged() {
1129e131447a0270f66f025147a243a1fda0e3fea14Mindy Pereira        setDeleteMode(DELETE_MODE_BACKSPACE);
1139e131447a0270f66f025147a243a1fda0e3fea14Mindy Pereira    }
1149e131447a0270f66f025147a243a1fda0e3fea14Mindy Pereira
115582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov    public void resumeWithHistory() {
116582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        clearWithHistory(false);
117b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    }
118b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
119b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    private void clearWithHistory(boolean scroll) {
120582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        String text = mHistory.getText();
121582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        if (MARKER_EVALUATE_ON_RESUME.equals(text)) {
122582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            if (!mHistory.moveToPrevious()) {
123582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov                text = "";
124582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            }
125582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            text = mHistory.getText();
126582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            evaluateAndShowResult(text, CalculatorDisplay.Scroll.NONE);
127582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        } else {
128582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            mResult = "";
129582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            mDisplay.setText(
130582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov                    text, scroll ? CalculatorDisplay.Scroll.UP : CalculatorDisplay.Scroll.NONE);
131582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            mIsError = false;
132582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        }
133b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    }
134b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
135b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    private void clear(boolean scroll) {
13657a15b2b8d73c3f3edcbbbc1f9e2c450e3ec9538Andrew Flynn        mHistory.enter("");
137b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        mDisplay.setText("", scroll ? CalculatorDisplay.Scroll.UP : CalculatorDisplay.Scroll.NONE);
138b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        cleared();
139b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    }
140b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
141b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    void cleared() {
142b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        mResult = "";
143b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        mIsError = false;
144b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        updateHistory();
1452501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov
1462501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov        setDeleteMode(DELETE_MODE_BACKSPACE);
147b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    }
148b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
149b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    boolean acceptInsert(String delta) {
150b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        String text = getText();
151b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        return !mIsError &&
1522501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov            (!mResult.equals(text) ||
153b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project             isOperator(delta) ||
154b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project             mDisplay.getSelectionStart() != text.length());
155b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    }
156b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
157b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    void onDelete() {
158b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        if (getText().equals(mResult) || mIsError) {
159b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project            clear(false);
160b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        } else {
161b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project            mDisplay.dispatchKeyEvent(new KeyEvent(0, KeyEvent.KEYCODE_DEL));
162b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project            mResult = "";
163b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        }
164b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    }
165b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
166b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    void onClear() {
1672501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov        clear(mDeleteMode == DELETE_MODE_CLEAR);
168b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    }
169b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
170b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    void onEnter() {
1712501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov        if (mDeleteMode == DELETE_MODE_CLEAR) {
172582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            clearWithHistory(false); // clear after an Enter on result
173b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        } else {
174582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            evaluateAndShowResult(getText(), CalculatorDisplay.Scroll.UP);
175582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        }
176582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov    }
177582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov
178582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov    public void evaluateAndShowResult(String text, Scroll scroll) {
179582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        try {
180582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            String result = evaluate(text);
181582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            if (!text.equals(result)) {
182582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov                mHistory.enter(text);
183582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov                mResult = result;
184582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov                mDisplay.setText(mResult, scroll);
1852501811e0d216bce30ce01823ed1a8191333d4c1Dmitri Plotnikov                setDeleteMode(DELETE_MODE_CLEAR);
186b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project            }
187582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        } catch (SyntaxException e) {
188582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            mIsError = true;
189582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            mResult = mErrorString;
190582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            mDisplay.setText(mResult, scroll);
191582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            setDeleteMode(DELETE_MODE_CLEAR);
192b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        }
193b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    }
194b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
195b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    void onUp() {
196b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        String text = getText();
197b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        if (!text.equals(mResult)) {
198b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project            mHistory.update(text);
199b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        }
200b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        if (mHistory.moveToPrevious()) {
201b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project            mDisplay.setText(mHistory.getText(), CalculatorDisplay.Scroll.DOWN);
202b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        }
203b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    }
204b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
205b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    void onDown() {
206b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        String text = getText();
207b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        if (!text.equals(mResult)) {
208b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project            mHistory.update(text);
209b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        }
210b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        if (mHistory.moveToNext()) {
211b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project            mDisplay.setText(mHistory.getText(), CalculatorDisplay.Scroll.UP);
212b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        }
213b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    }
214b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
215b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    void updateHistory() {
216582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        String text = getText();
217520cbbb8a5cb728eb9244a9c2ef5c917b56be756Mindy Pereira        // Don't set the ? marker for empty text or the error string.
218520cbbb8a5cb728eb9244a9c2ef5c917b56be756Mindy Pereira        // There is no need to evaluate those later.
219520cbbb8a5cb728eb9244a9c2ef5c917b56be756Mindy Pereira        if (!TextUtils.isEmpty(text) && !TextUtils.equals(text, mErrorString)
220520cbbb8a5cb728eb9244a9c2ef5c917b56be756Mindy Pereira                && text.equals(mResult)) {
221582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            mHistory.update(MARKER_EVALUATE_ON_RESUME);
222582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        } else {
223582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            mHistory.update(getText());
224582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        }
225b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    }
226b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
227b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    String evaluate(String input) throws SyntaxException {
228b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        if (input.trim().equals("")) {
229b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project            return "";
230b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        }
231b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
232b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        // drop final infix operators (they can only result in error)
233b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        int size = input.length();
234b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        while (size > 0 && isOperator(input.charAt(size - 1))) {
235b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project            input = input.substring(0, size - 1);
236b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project            --size;
237b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        }
238caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira        // Find and replace any translated mathematical functions.
239caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira        input = replaceTranslations(input);
240582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        double value = mSymbols.eval(input);
241582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov
242582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        String result = "";
243582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        for (int precision = mLineLength; precision > 6; precision--) {
244582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            result = tryFormattingWithPrecision(value, precision);
245582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            if (result.length() <= mLineLength) {
246582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov                break;
247582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            }
248582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        }
249582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        return result.replace('-', MINUS).replace(INFINITY, INFINITY_UNICODE);
250582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov    }
251582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov
252caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira    private void addTranslation(HashMap<String, String> map, int t, int m) {
253caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira        Resources res = mContext.getResources();
254caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira        String translated = res.getString(t);
255caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira        String math = res.getString(m);
256caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira        if (!TextUtils.equals(translated, math)) {
257caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira            map.put(translated, math);
258caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira        }
259caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira    }
260caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira
261caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira    private String replaceTranslations(String input) {
262caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira        if (mTranslationsSet == null) {
263caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira            HashMap<String, String> map = new HashMap<String, String>();
264caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira            addTranslation(map, R.string.sin, R.string.sin_mathematical_value);
265caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira            addTranslation(map, R.string.cos, R.string.cos_mathematical_value);
266caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira            addTranslation(map, R.string.tan, R.string.tan_mathematical_value);
26754e8b090cb47e0dfabf5f069f6460e2caacea60fMindy Pereira            addTranslation(map, R.string.e, R.string.e_mathematical_value);
26854e8b090cb47e0dfabf5f069f6460e2caacea60fMindy Pereira            addTranslation(map, R.string.ln, R.string.ln_mathematical_value);
26954e8b090cb47e0dfabf5f069f6460e2caacea60fMindy Pereira            addTranslation(map, R.string.lg, R.string.lg_mathematical_value);
270caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira            mTranslationsSet = map.entrySet();
271caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira        }
272caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira        for (Entry<String, String> entry : mTranslationsSet) {
273caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira            input = input.replace(entry.getKey(), entry.getValue());
274caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira        }
275caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira        return input;
276caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira    }
277caccbfbeaf0c42b86b6e26ff48c18c4fc136e28fMindy Pereira
278582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov    private String tryFormattingWithPrecision(double value, int precision) {
279582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        // The standard scientific formatter is basically what we need. We will
280582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        // start with what it produces and then massage it a bit.
28157fa18e9f76005d29cf1708017259d3527e68ca4Mindy Pereira        String result = String.format(Locale.US, "%" + mLineLength + "." + precision + "g", value);
282b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        if (result.equals(NAN)) { // treat NaN as Error
283b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project            mIsError = true;
284b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project            return mErrorString;
285b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        }
286582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        String mantissa = result;
287582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        String exponent = null;
288582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        int e = result.indexOf('e');
289582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        if (e != -1) {
290582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            mantissa = result.substring(0, e);
291582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov
292582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            // Strip "+" and unnecessary 0's from the exponent
293582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            exponent = result.substring(e + 1);
294582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            if (exponent.startsWith("+")) {
295582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov                exponent = exponent.substring(1);
296582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            }
297582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            exponent = String.valueOf(Integer.parseInt(exponent));
298582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        } else {
299582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            mantissa = result;
300582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        }
301582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov
302582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        int period = mantissa.indexOf('.');
303582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        if (period == -1) {
304582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            period = mantissa.indexOf(',');
305582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        }
306582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        if (period != -1) {
307582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            // Strip trailing 0's
308582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            while (mantissa.length() > 0 && mantissa.endsWith("0")) {
309582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov                mantissa = mantissa.substring(0, mantissa.length() - 1);
310582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            }
311582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            if (mantissa.length() == period + 1) {
312582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov                mantissa = mantissa.substring(0, mantissa.length() - 1);
313582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            }
314582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        }
315582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov
316582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        if (exponent != null) {
317582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            result = mantissa + 'e' + exponent;
318582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        } else {
319582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov            result = mantissa;
320582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        }
321582273d77e6ee8335447cbd524e7b47b851c7b46Dmitri Plotnikov        return result;
322b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    }
323b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
324b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    static boolean isOperator(String text) {
325b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        return text.length() == 1 && isOperator(text.charAt(0));
326b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    }
327b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project
328b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    static boolean isOperator(char c) {
329b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        //plus minus times div
330b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project        return "+\u2212\u00d7\u00f7/*".indexOf(c) != -1;
331b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project    }
332b301ed2e1720fc9190eaf99ab33b5f49eabcebc5The Android Open Source Project}
333