blob: 493ec01941890eb044681376465605ce6bd4b94f [file] [log] [blame]
Hans Boehm84614952014-11-25 18:46:17 -08001/*
2 * Copyright (C) 2015 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.res.Resources;
20import android.content.Context;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070021import android.app.Activity;
22import android.util.Log;
Hans Boehm84614952014-11-25 18:46:17 -080023import android.view.View;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070024import android.widget.Button;
25
Hans Boehm84614952014-11-25 18:46:17 -080026import java.text.DecimalFormatSymbols;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070027import java.util.HashMap;
28import java.util.Locale;
Hans Boehm84614952014-11-25 18:46:17 -080029
Hans Boehm013969e2015-04-13 20:29:47 -070030// KeyMap instances are not meaningful; everything here is static.
31// All functions are either pure, or are assumed to be called only from
32// a single UI thread.
33
Hans Boehm84614952014-11-25 18:46:17 -080034public class KeyMaps {
Hans Boehm013969e2015-04-13 20:29:47 -070035 // Map key id to corresponding (internationalized) display string.
36 // Pure function.
Hans Boehm84614952014-11-25 18:46:17 -080037 public static String toString(int id, Context context) {
38 Resources res = context.getResources();
39 switch(id) {
40 case R.id.const_pi: return res.getString(R.string.const_pi);
41 case R.id.const_e: return res.getString(R.string.const_e);
42 case R.id.op_sqrt: return res.getString(R.string.op_sqrt);
43 case R.id.op_fact: return res.getString(R.string.op_fact);
44 case R.id.fun_sin: return res.getString(R.string.fun_sin)
45 + res.getString(R.string.lparen);
Hans Boehm013969e2015-04-13 20:29:47 -070046 case R.id.fun_cos: return res.getString(R.string.fun_cos)
Hans Boehm84614952014-11-25 18:46:17 -080047 + res.getString(R.string.lparen);
48 case R.id.fun_tan: return res.getString(R.string.fun_tan)
49 + res.getString(R.string.lparen);
50 case R.id.fun_arcsin: return res.getString(R.string.fun_arcsin)
51 + res.getString(R.string.lparen);
Hans Boehm013969e2015-04-13 20:29:47 -070052 case R.id.fun_arccos: return res.getString(R.string.fun_arccos)
Hans Boehm84614952014-11-25 18:46:17 -080053 + res.getString(R.string.lparen);
54 case R.id.fun_arctan: return res.getString(R.string.fun_arctan)
55 + res.getString(R.string.lparen);
56 case R.id.fun_ln: return res.getString(R.string.fun_ln)
57 + res.getString(R.string.lparen);
58 case R.id.fun_log: return res.getString(R.string.fun_log)
59 + res.getString(R.string.lparen);
60 case R.id.lparen: return res.getString(R.string.lparen);
61 case R.id.rparen: return res.getString(R.string.rparen);
62 case R.id.op_pow: return res.getString(R.string.op_pow);
63 case R.id.op_mul: return res.getString(R.string.op_mul);
64 case R.id.op_div: return res.getString(R.string.op_div);
65 case R.id.op_add: return res.getString(R.string.op_add);
66 case R.id.op_sub: return res.getString(R.string.op_sub);
67 case R.id.dec_point: return res.getString(R.string.dec_point);
68 case R.id.digit_0: return res.getString(R.string.digit_0);
69 case R.id.digit_1: return res.getString(R.string.digit_1);
70 case R.id.digit_2: return res.getString(R.string.digit_2);
71 case R.id.digit_3: return res.getString(R.string.digit_3);
72 case R.id.digit_4: return res.getString(R.string.digit_4);
73 case R.id.digit_5: return res.getString(R.string.digit_5);
74 case R.id.digit_6: return res.getString(R.string.digit_6);
75 case R.id.digit_7: return res.getString(R.string.digit_7);
76 case R.id.digit_8: return res.getString(R.string.digit_8);
77 case R.id.digit_9: return res.getString(R.string.digit_9);
78 default: return "?oops?";
79 }
80 }
81
82 // Does a button id correspond to a binary operator?
Hans Boehm013969e2015-04-13 20:29:47 -070083 // Pure function.
Hans Boehm84614952014-11-25 18:46:17 -080084 public static boolean isBinary(int id) {
85 switch(id) {
86 case R.id.op_pow:
87 case R.id.op_mul:
88 case R.id.op_div:
89 case R.id.op_add:
90 case R.id.op_sub:
91 return true;
92 default:
93 return false;
94 }
95 }
96
97 // Does a button id correspond to a suffix operator?
98 public static boolean isSuffix(int id) {
99 return id == R.id.op_fact;
100 }
101
102 public static final int NOT_DIGIT = 10;
103
Hans Boehm08e8f322015-04-21 13:18:38 -0700104 public static final String ELLIPSIS = "\u2026";
105
Hans Boehm84614952014-11-25 18:46:17 -0800106 // Map key id to digit or NOT_DIGIT
Hans Boehm013969e2015-04-13 20:29:47 -0700107 // Pure function.
Hans Boehm84614952014-11-25 18:46:17 -0800108 public static int digVal(int id) {
109 switch (id) {
110 case R.id.digit_0:
111 return 0;
112 case R.id.digit_1:
113 return 1;
114 case R.id.digit_2:
115 return 2;
116 case R.id.digit_3:
117 return 3;
118 case R.id.digit_4:
119 return 4;
120 case R.id.digit_5:
121 return 5;
122 case R.id.digit_6:
123 return 6;
124 case R.id.digit_7:
125 return 7;
126 case R.id.digit_8:
127 return 8;
128 case R.id.digit_9:
129 return 9;
130 default:
131 return NOT_DIGIT;
132 }
133 }
134
135 // Map digit to corresponding key. Inverse of above.
Hans Boehm013969e2015-04-13 20:29:47 -0700136 // Pure function.
Hans Boehm84614952014-11-25 18:46:17 -0800137 public static int keyForDigVal(int v) {
138 switch(v) {
139 case 0:
140 return R.id.digit_0;
141 case 1:
142 return R.id.digit_1;
143 case 2:
144 return R.id.digit_2;
145 case 3:
146 return R.id.digit_3;
147 case 4:
148 return R.id.digit_4;
149 case 5:
150 return R.id.digit_5;
151 case 6:
152 return R.id.digit_6;
153 case 7:
154 return R.id.digit_7;
155 case 8:
156 return R.id.digit_8;
157 case 9:
158 return R.id.digit_9;
159 default:
160 return View.NO_ID;
161 }
162 }
163
Hans Boehm013969e2015-04-13 20:29:47 -0700164 static char mDecimalPt =
Hans Boehm84614952014-11-25 18:46:17 -0800165 DecimalFormatSymbols.getInstance().getDecimalSeparator();
166
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700167 static char mPiChar;
168
169 static char mFactChar;
170
171 static HashMap<String, Integer> sKeyValForFun;
172 // Key value corresponding to given function name.
173 // We include both localized and English names.
174
Hans Boehm013969e2015-04-13 20:29:47 -0700175 static HashMap<Character, String> sOutputForResultChar;
176 // Result string corresponding to a character in the
177 // calculator result.
178 // The string values in the map are expected to be one character
179 // long.
180
181 static String sLocaleForMaps = "none";
182 // Locale string corresponding to preceding map and character
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700183 // constants.
184 // We recompute the map if this is not the current locale.
185
Hans Boehm013969e2015-04-13 20:29:47 -0700186 static Activity mActivity; // Activity to use for looking up
187 // buttons.
188
189 // Called only by UI thread.
190 public static void setActivity(Activity a) {
191 mActivity = a;
192 }
193
Hans Boehm84614952014-11-25 18:46:17 -0800194 // Return the button id corresponding to the supplied character
195 // or NO_ID
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700196 // Called only by UI thread.
Hans Boehm013969e2015-04-13 20:29:47 -0700197 public static int keyForChar(char c) {
198 validateMaps();
Hans Boehm84614952014-11-25 18:46:17 -0800199 if (Character.isDigit(c)) {
200 int i = Character.digit(c, 10);
201 return KeyMaps.keyForDigVal(i);
202 }
Hans Boehm84614952014-11-25 18:46:17 -0800203 switch (c) {
204 case '.':
205 return R.id.dec_point;
206 case '-':
207 return R.id.op_sub;
208 case '+':
209 return R.id.op_add;
210 case '*':
211 return R.id.op_mul;
212 case '/':
213 return R.id.op_div;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700214 // TODO: We have an issue if any of the localized function
215 // names start with 'e' or 'p'. That doesn't currently appear
216 // to be the case. In fact the first letters of the Latin
217 // allphabet ones seem rather predictable.
Hans Boehm84614952014-11-25 18:46:17 -0800218 case 'e':
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700219 case 'E':
Hans Boehm84614952014-11-25 18:46:17 -0800220 return R.id.const_e;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700221 case 'p':
222 case 'P':
223 return R.id.const_pi;
Hans Boehm84614952014-11-25 18:46:17 -0800224 case '^':
225 return R.id.op_pow;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700226 case '!':
227 return R.id.op_fact;
228 case '(':
229 return R.id.lparen;
230 case ')':
231 return R.id.rparen;
Hans Boehm84614952014-11-25 18:46:17 -0800232 default:
Hans Boehm013969e2015-04-13 20:29:47 -0700233 if (c == mDecimalPt) return R.id.dec_point;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700234 if (c == mPiChar) return R.id.const_pi;
235 // pi is not translated, but it might be typable on
236 // a Greek keyboard, so we check ...
Hans Boehm84614952014-11-25 18:46:17 -0800237 return View.NO_ID;
238 }
239 }
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700240
241 // Add information corresponding to the given button id to
242 // sKeyValForFun.
Hans Boehm013969e2015-04-13 20:29:47 -0700243 static void addButtonToFunMap(int button_id) {
244 Button button = (Button)mActivity.findViewById(button_id);
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700245 sKeyValForFun.put(button.getText().toString(), button_id);
246 }
247
Hans Boehm013969e2015-04-13 20:29:47 -0700248 // Ditto, but for sOutputForResultChar.
249 static void addButtonToOutputMap(char c, int button_id) {
250 Button button = (Button)mActivity.findViewById(button_id);
251 sOutputForResultChar.put(c, button.getText().toString());
252 }
253
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700254 // Ensure that the preceding map and character constants are
255 // initialized and correspond to the current locale.
256 // Called only by a single thread, namely the UI thread.
Hans Boehm013969e2015-04-13 20:29:47 -0700257 static void validateMaps() {
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700258 Locale locale = Locale.getDefault();
259 String lname = locale.toString();
Hans Boehm013969e2015-04-13 20:29:47 -0700260 if (lname != sLocaleForMaps) {
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700261 Log.v ("Calculator", "Setting local to: " + lname);
262 sKeyValForFun = new HashMap<String, Integer>();
263 sKeyValForFun.put("sin", R.id.fun_sin);
264 sKeyValForFun.put("cos", R.id.fun_cos);
265 sKeyValForFun.put("tan", R.id.fun_tan);
266 sKeyValForFun.put("arcsin", R.id.fun_arcsin);
267 sKeyValForFun.put("arccos", R.id.fun_arccos);
268 sKeyValForFun.put("arctan", R.id.fun_arctan);
269 sKeyValForFun.put("asin", R.id.fun_arcsin);
270 sKeyValForFun.put("acos", R.id.fun_arccos);
271 sKeyValForFun.put("atan", R.id.fun_arctan);
272 sKeyValForFun.put("ln", R.id.fun_ln);
273 sKeyValForFun.put("log", R.id.fun_log);
274 sKeyValForFun.put("sqrt", R.id.op_sqrt); // special treatment
Hans Boehm013969e2015-04-13 20:29:47 -0700275 addButtonToFunMap(R.id.fun_sin);
276 addButtonToFunMap(R.id.fun_cos);
277 addButtonToFunMap(R.id.fun_tan);
278 addButtonToFunMap(R.id.fun_arcsin);
279 addButtonToFunMap(R.id.fun_arccos);
280 addButtonToFunMap(R.id.fun_arctan);
281 addButtonToFunMap(R.id.fun_ln);
282 addButtonToFunMap(R.id.fun_log);
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700283
284 // Set locale-dependent character "constants"
Hans Boehm013969e2015-04-13 20:29:47 -0700285 mDecimalPt =
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700286 DecimalFormatSymbols.getInstance().getDecimalSeparator();
Hans Boehm013969e2015-04-13 20:29:47 -0700287 Resources res = mActivity.getResources();
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700288 mPiChar = mFactChar = 0;
289 String piString = res.getString(R.string.const_pi);
290 if (piString.length() == 1) mPiChar = piString.charAt(0);
291 String factString = res.getString(R.string.op_fact);
292 if (factString.length() == 1) mFactChar = factString.charAt(0);
293
Hans Boehm013969e2015-04-13 20:29:47 -0700294 sOutputForResultChar = new HashMap<Character, String>();
295 sOutputForResultChar.put('e', "E");
296 sOutputForResultChar.put('E', "E");
297 sOutputForResultChar.put('.', String.valueOf(mDecimalPt));
Hans Boehm08e8f322015-04-21 13:18:38 -0700298 sOutputForResultChar.put(ELLIPSIS.charAt(0), ELLIPSIS);
Hans Boehm013969e2015-04-13 20:29:47 -0700299 sOutputForResultChar.put('/', "/");
300 // Translate numbers for fraction display, but not
301 // the separating slash, which appears to be
302 // universal.
303 addButtonToOutputMap('-', R.id.op_sub);
304 for (int i = 0; i <= 9; ++i) {
305 addButtonToOutputMap((char)('0' + i), keyForDigVal(i));
306 }
307
308 sLocaleForMaps = lname;
309
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700310 }
311 }
312
313 // Return function button id for the substring of s starting
314 // at pos and ending with the next "(".
315 // Return NO_ID if there is none.
316 // We check for both standard English names and localized
317 // button labels, though those don't seem to differ much.
318 // Called only by a single thread, namely the UI thread.
Hans Boehm013969e2015-04-13 20:29:47 -0700319 public static int funForString(String s, int pos) {
320 validateMaps();
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700321 int parenPos = s.indexOf('(', pos);
322 if (parenPos != -1) {
323 String funString = s.substring(pos, parenPos);
324 Integer keyValue = sKeyValForFun.get(funString);
325 if (keyValue == null) return View.NO_ID;
326 return keyValue;
327 }
328 return View.NO_ID;
329 }
Hans Boehm013969e2015-04-13 20:29:47 -0700330
331 // Called only by UI thread.
332 public static String translateResult(String s) {
333 StringBuilder result = new StringBuilder();
334 int len = s.length();
335 validateMaps();
336 for (int i = 0; i < len; ++i) {
337 char c = s.charAt(i);
338 String translation = sOutputForResultChar.get(c);
339 if (translation == null) {
340 // Should not get here.
341 Log.v("Calculator", "Bad character:" + c);
342 result.append(String.valueOf(c));
343 } else {
344 result.append(translation);
345 }
346 }
347 return result.toString();
348 }
349
Hans Boehm84614952014-11-25 18:46:17 -0800350}