1 /* 2 * Copyright (C) 2008 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.view.View; 20 import android.view.KeyEvent; 21 import android.widget.Button; 22 import android.widget.EditText; 23 import android.content.Context; 24 import android.content.res.Configuration; 25 26 import org.javia.arity.Symbols; 27 import org.javia.arity.SyntaxException; 28 import org.javia.arity.Util; 29 30 class Logic { 31 private CalculatorDisplay mDisplay; 32 private Symbols mSymbols = new Symbols(); 33 private History mHistory; 34 private String mResult = ""; 35 private Button mEqualButton; 36 private final String mEnterString; 37 private boolean mIsError = false; 38 private int mLineLength = 0; 39 40 private static final String INFINITY_UNICODE = "\u221e"; 41 42 // the two strings below are the result of Double.toString() for Infinity & NaN 43 // they are not output to the user and don't require internationalization 44 private static final String INFINITY = "Infinity"; 45 private static final String NAN = "NaN"; 46 47 static final char MINUS = '\u2212'; 48 49 private final String mErrorString; 50 Logic(Context context, History history, CalculatorDisplay display, Button equalButton)51 Logic(Context context, History history, CalculatorDisplay display, Button equalButton) { 52 mErrorString = context.getResources().getString(R.string.error); 53 try { 54 // in calculator we use log() for base-10, 55 // unlike in arity-lib where log() is natural logarithm 56 mSymbols.define(mSymbols.compileWithName("log(x)=log10(x)")); 57 } catch (SyntaxException e) { 58 throw new Error("" + e); //never 59 } 60 mHistory = history; 61 mDisplay = display; 62 mDisplay.setLogic(this); 63 mEqualButton = equalButton; 64 mEnterString = context.getText(R.string.enter).toString(); 65 66 clearWithHistory(false); 67 } 68 setLineLength(int nDigits)69 void setLineLength(int nDigits) { 70 mLineLength = nDigits; 71 } 72 eatHorizontalMove(boolean toLeft)73 boolean eatHorizontalMove(boolean toLeft) { 74 EditText editText = mDisplay.getEditText(); 75 int cursorPos = editText.getSelectionStart(); 76 return toLeft ? cursorPos == 0 : cursorPos >= editText.length(); 77 } 78 getText()79 private String getText() { 80 return mDisplay.getText().toString(); 81 } 82 insert(String delta)83 void insert(String delta) { 84 mDisplay.insert(delta); 85 } 86 setText(CharSequence text)87 private void setText(CharSequence text) { 88 mDisplay.setText(text, CalculatorDisplay.Scroll.UP); 89 } 90 clearWithHistory(boolean scroll)91 private void clearWithHistory(boolean scroll) { 92 mDisplay.setText(mHistory.getText(), 93 scroll ? CalculatorDisplay.Scroll.UP : CalculatorDisplay.Scroll.NONE); 94 mResult = ""; 95 mIsError = false; 96 } 97 clear(boolean scroll)98 private void clear(boolean scroll) { 99 mDisplay.setText("", scroll ? CalculatorDisplay.Scroll.UP : CalculatorDisplay.Scroll.NONE); 100 cleared(); 101 } 102 cleared()103 void cleared() { 104 mResult = ""; 105 mIsError = false; 106 updateHistory(); 107 } 108 acceptInsert(String delta)109 boolean acceptInsert(String delta) { 110 String text = getText(); 111 return !mIsError && 112 (!mResult.equals(text) || 113 isOperator(delta) || 114 mDisplay.getSelectionStart() != text.length()); 115 } 116 onDelete()117 void onDelete() { 118 if (getText().equals(mResult) || mIsError) { 119 clear(false); 120 } else { 121 mDisplay.dispatchKeyEvent(new KeyEvent(0, KeyEvent.KEYCODE_DEL)); 122 mResult = ""; 123 } 124 } 125 onClear()126 void onClear() { 127 clear(false); 128 } 129 onEnter()130 void onEnter() { 131 String text = getText(); 132 if (text.equals(mResult)) { 133 clearWithHistory(false); //clear after an Enter on result 134 } else { 135 mHistory.enter(text); 136 try { 137 mResult = evaluate(text); 138 } catch (SyntaxException e) { 139 mIsError = true; 140 mResult = mErrorString; 141 } 142 if (text.equals(mResult)) { 143 //no need to show result, it is exactly what the user entered 144 clearWithHistory(true); 145 } else { 146 setText(mResult); 147 //mEqualButton.setText(mEnterString); 148 } 149 } 150 } 151 onUp()152 void onUp() { 153 String text = getText(); 154 if (!text.equals(mResult)) { 155 mHistory.update(text); 156 } 157 if (mHistory.moveToPrevious()) { 158 mDisplay.setText(mHistory.getText(), CalculatorDisplay.Scroll.DOWN); 159 } 160 } 161 onDown()162 void onDown() { 163 String text = getText(); 164 if (!text.equals(mResult)) { 165 mHistory.update(text); 166 } 167 if (mHistory.moveToNext()) { 168 mDisplay.setText(mHistory.getText(), CalculatorDisplay.Scroll.UP); 169 } 170 } 171 updateHistory()172 void updateHistory() { 173 mHistory.update(getText()); 174 } 175 176 private static final int ROUND_DIGITS = 1; evaluate(String input)177 String evaluate(String input) throws SyntaxException { 178 if (input.trim().equals("")) { 179 return ""; 180 } 181 182 // drop final infix operators (they can only result in error) 183 int size = input.length(); 184 while (size > 0 && isOperator(input.charAt(size - 1))) { 185 input = input.substring(0, size - 1); 186 --size; 187 } 188 189 String result = Util.doubleToString(mSymbols.eval(input), mLineLength, ROUND_DIGITS); 190 if (result.equals(NAN)) { // treat NaN as Error 191 mIsError = true; 192 return mErrorString; 193 } 194 return result.replace('-', MINUS).replace(INFINITY, INFINITY_UNICODE); 195 } 196 isOperator(String text)197 static boolean isOperator(String text) { 198 return text.length() == 1 && isOperator(text.charAt(0)); 199 } 200 isOperator(char c)201 static boolean isOperator(char c) { 202 //plus minus times div 203 return "+\u2212\u00d7\u00f7/*".indexOf(c) != -1; 204 } 205 } 206