blob: cdfe4e4f452c4dccda42841aa59c86a486fbbd41 [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 Boehm0b3a9fd2015-05-22 11:36:26 -070030/**
31 * Collection of mapping functions between key ids, characters, internationalized
Justin Klaassene2711cb2015-05-28 11:13:17 -070032 * and non-internationalized characters, etc.
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070033 * <p>
34 * KeyMap instances are not meaningful; everything here is static.
35 * All functions are either pure, or are assumed to be called only from a single UI thread.
36 */
Hans Boehm84614952014-11-25 18:46:17 -080037public class KeyMaps {
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070038 /**
39 * Map key id to corresponding (internationalized) display string.
40 * Pure function.
41 */
Justin Klaassene2711cb2015-05-28 11:13:17 -070042 public static String toString(Context context, int id) {
Hans Boehm84614952014-11-25 18:46:17 -080043 switch(id) {
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070044 case R.id.const_pi:
Justin Klaassene2711cb2015-05-28 11:13:17 -070045 return context.getString(R.string.const_pi);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070046 case R.id.const_e:
Justin Klaassene2711cb2015-05-28 11:13:17 -070047 return context.getString(R.string.const_e);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070048 case R.id.op_sqrt:
Justin Klaassene2711cb2015-05-28 11:13:17 -070049 return context.getString(R.string.op_sqrt);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070050 case R.id.op_fact:
Justin Klaassene2711cb2015-05-28 11:13:17 -070051 return context.getString(R.string.op_fact);
52 case R.id.op_pct:
53 return context.getString(R.string.op_pct);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070054 case R.id.fun_sin:
Justin Klaassene2711cb2015-05-28 11:13:17 -070055 return context.getString(R.string.fun_sin) + context.getString(R.string.lparen);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070056 case R.id.fun_cos:
Justin Klaassene2711cb2015-05-28 11:13:17 -070057 return context.getString(R.string.fun_cos) + context.getString(R.string.lparen);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070058 case R.id.fun_tan:
Justin Klaassene2711cb2015-05-28 11:13:17 -070059 return context.getString(R.string.fun_tan) + context.getString(R.string.lparen);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070060 case R.id.fun_arcsin:
Justin Klaassene2711cb2015-05-28 11:13:17 -070061 return context.getString(R.string.fun_arcsin) + context.getString(R.string.lparen);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070062 case R.id.fun_arccos:
Justin Klaassene2711cb2015-05-28 11:13:17 -070063 return context.getString(R.string.fun_arccos) + context.getString(R.string.lparen);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070064 case R.id.fun_arctan:
Justin Klaassene2711cb2015-05-28 11:13:17 -070065 return context.getString(R.string.fun_arctan) + context.getString(R.string.lparen);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070066 case R.id.fun_ln:
Justin Klaassene2711cb2015-05-28 11:13:17 -070067 return context.getString(R.string.fun_ln) + context.getString(R.string.lparen);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070068 case R.id.fun_log:
Justin Klaassene2711cb2015-05-28 11:13:17 -070069 return context.getString(R.string.fun_log) + context.getString(R.string.lparen);
Hans Boehm4db31b42015-05-31 12:19:05 -070070 case R.id.fun_exp:
71 // Button label doesn't work.
72 return context.getString(R.string.exponential) + context.getString(R.string.lparen);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070073 case R.id.lparen:
Justin Klaassene2711cb2015-05-28 11:13:17 -070074 return context.getString(R.string.lparen);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070075 case R.id.rparen:
Justin Klaassene2711cb2015-05-28 11:13:17 -070076 return context.getString(R.string.rparen);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070077 case R.id.op_pow:
Justin Klaassene2711cb2015-05-28 11:13:17 -070078 return context.getString(R.string.op_pow);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070079 case R.id.op_mul:
Justin Klaassene2711cb2015-05-28 11:13:17 -070080 return context.getString(R.string.op_mul);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070081 case R.id.op_div:
Justin Klaassene2711cb2015-05-28 11:13:17 -070082 return context.getString(R.string.op_div);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070083 case R.id.op_add:
Justin Klaassene2711cb2015-05-28 11:13:17 -070084 return context.getString(R.string.op_add);
Hans Boehm8f051c32016-10-03 16:53:58 -070085 case R.id.op_sub:
86 return context.getString(R.string.op_sub);
Hans Boehm4db31b42015-05-31 12:19:05 -070087 case R.id.op_sqr:
88 // Button label doesn't work.
89 return context.getString(R.string.squared);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070090 case R.id.dec_point:
Justin Klaassene2711cb2015-05-28 11:13:17 -070091 return context.getString(R.string.dec_point);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070092 case R.id.digit_0:
Justin Klaassene2711cb2015-05-28 11:13:17 -070093 return context.getString(R.string.digit_0);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070094 case R.id.digit_1:
Justin Klaassene2711cb2015-05-28 11:13:17 -070095 return context.getString(R.string.digit_1);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070096 case R.id.digit_2:
Justin Klaassene2711cb2015-05-28 11:13:17 -070097 return context.getString(R.string.digit_2);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070098 case R.id.digit_3:
Justin Klaassene2711cb2015-05-28 11:13:17 -070099 return context.getString(R.string.digit_3);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700100 case R.id.digit_4:
Justin Klaassene2711cb2015-05-28 11:13:17 -0700101 return context.getString(R.string.digit_4);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700102 case R.id.digit_5:
Justin Klaassene2711cb2015-05-28 11:13:17 -0700103 return context.getString(R.string.digit_5);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700104 case R.id.digit_6:
Justin Klaassene2711cb2015-05-28 11:13:17 -0700105 return context.getString(R.string.digit_6);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700106 case R.id.digit_7:
Justin Klaassene2711cb2015-05-28 11:13:17 -0700107 return context.getString(R.string.digit_7);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700108 case R.id.digit_8:
Justin Klaassene2711cb2015-05-28 11:13:17 -0700109 return context.getString(R.string.digit_8);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700110 case R.id.digit_9:
Justin Klaassene2711cb2015-05-28 11:13:17 -0700111 return context.getString(R.string.digit_9);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700112 default:
Justin Klaassene2711cb2015-05-28 11:13:17 -0700113 return "";
Hans Boehm84614952014-11-25 18:46:17 -0800114 }
115 }
116
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700117 /**
Hans Boehm8f051c32016-10-03 16:53:58 -0700118 * Map key id to a single byte, somewhat human readable, description.
119 * Used to serialize expressions in the database.
120 * The result is in the range 0x20-0x7f.
121 */
122 public static byte toByte(int id) {
123 char result;
124 // We only use characters with single-byte UTF8 encodings in the range 0x20-0x7F.
125 switch(id) {
126 case R.id.const_pi:
127 result = 'p';
128 break;
129 case R.id.const_e:
130 result = 'e';
131 break;
132 case R.id.op_sqrt:
133 result = 'r';
134 break;
135 case R.id.op_fact:
136 result = '!';
137 break;
138 case R.id.op_pct:
139 result = '%';
140 break;
141 case R.id.fun_sin:
142 result = 's';
143 break;
144 case R.id.fun_cos:
145 result = 'c';
146 break;
147 case R.id.fun_tan:
148 result = 't';
149 break;
150 case R.id.fun_arcsin:
151 result = 'S';
152 break;
153 case R.id.fun_arccos:
154 result = 'C';
155 break;
156 case R.id.fun_arctan:
157 result = 'T';
158 break;
159 case R.id.fun_ln:
160 result = 'l';
161 break;
162 case R.id.fun_log:
163 result = 'L';
164 break;
165 case R.id.fun_exp:
166 result = 'E';
167 break;
168 case R.id.lparen:
169 result = '(';
170 break;
171 case R.id.rparen:
172 result = ')';
173 break;
174 case R.id.op_pow:
175 result = '^';
176 break;
177 case R.id.op_mul:
178 result = '*';
179 break;
180 case R.id.op_div:
181 result = '/';
182 break;
183 case R.id.op_add:
184 result = '+';
185 break;
186 case R.id.op_sub:
187 result = '-';
188 break;
189 case R.id.op_sqr:
190 result = '2';
191 break;
192 default:
193 throw new AssertionError("Unexpected key id");
194 }
195 return (byte)result;
196 }
197
198 /**
199 * Map single byte encoding generated by key id generated by toByte back to
200 * key id.
201 */
202 public static int fromByte(byte b) {
203 switch((char)b) {
204 case 'p':
205 return R.id.const_pi;
206 case 'e':
207 return R.id.const_e;
208 case 'r':
209 return R.id.op_sqrt;
210 case '!':
211 return R.id.op_fact;
212 case '%':
213 return R.id.op_pct;
214 case 's':
215 return R.id.fun_sin;
216 case 'c':
217 return R.id.fun_cos;
218 case 't':
219 return R.id.fun_tan;
220 case 'S':
221 return R.id.fun_arcsin;
222 case 'C':
223 return R.id.fun_arccos;
224 case 'T':
225 return R.id.fun_arctan;
226 case 'l':
227 return R.id.fun_ln;
228 case 'L':
229 return R.id.fun_log;
230 case 'E':
231 return R.id.fun_exp;
232 case '(':
233 return R.id.lparen;
234 case ')':
235 return R.id.rparen;
236 case '^':
237 return R.id.op_pow;
238 case '*':
239 return R.id.op_mul;
240 case '/':
241 return R.id.op_div;
242 case '+':
243 return R.id.op_add;
244 case '-':
245 return R.id.op_sub;
246 case '2':
247 return R.id.op_sqr;
248 default:
249 throw new AssertionError("Unexpected single byte operator encoding");
250 }
251 }
252
253 /**
Hans Boehm8a4f81c2015-07-09 10:41:25 -0700254 * Map key id to corresponding (internationalized) descriptive string that can be used
255 * to correctly read back a formula.
256 * Only used for operators and individual characters; not used inside constants.
257 * Returns null when we don't need a descriptive string.
258 * Pure function.
259 */
260 public static String toDescriptiveString(Context context, int id) {
261 switch(id) {
262 case R.id.op_fact:
263 return context.getString(R.string.desc_op_fact);
264 case R.id.fun_sin:
265 return context.getString(R.string.desc_fun_sin)
266 + " " + context.getString(R.string.desc_lparen);
267 case R.id.fun_cos:
268 return context.getString(R.string.desc_fun_cos)
269 + " " + context.getString(R.string.desc_lparen);
270 case R.id.fun_tan:
271 return context.getString(R.string.desc_fun_tan)
272 + " " + context.getString(R.string.desc_lparen);
273 case R.id.fun_arcsin:
274 return context.getString(R.string.desc_fun_arcsin)
275 + " " + context.getString(R.string.desc_lparen);
276 case R.id.fun_arccos:
277 return context.getString(R.string.desc_fun_arccos)
278 + " " + context.getString(R.string.desc_lparen);
279 case R.id.fun_arctan:
280 return context.getString(R.string.desc_fun_arctan)
281 + " " + context.getString(R.string.desc_lparen);
282 case R.id.fun_ln:
283 return context.getString(R.string.desc_fun_ln)
284 + " " + context.getString(R.string.desc_lparen);
285 case R.id.fun_log:
286 return context.getString(R.string.desc_fun_log)
287 + " " + context.getString(R.string.desc_lparen);
288 case R.id.fun_exp:
289 return context.getString(R.string.desc_fun_exp)
290 + " " + context.getString(R.string.desc_lparen);
291 case R.id.lparen:
292 return context.getString(R.string.desc_lparen);
293 case R.id.rparen:
294 return context.getString(R.string.desc_rparen);
295 case R.id.op_pow:
296 return context.getString(R.string.desc_op_pow);
297 case R.id.dec_point:
298 return context.getString(R.string.desc_dec_point);
299 default:
300 return null;
301 }
302 }
303
304 /**
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700305 * Does a button id correspond to a binary operator?
306 * Pure function.
307 */
Hans Boehm84614952014-11-25 18:46:17 -0800308 public static boolean isBinary(int id) {
309 switch(id) {
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700310 case R.id.op_pow:
311 case R.id.op_mul:
312 case R.id.op_div:
313 case R.id.op_add:
314 case R.id.op_sub:
315 return true;
316 default:
317 return false;
Hans Boehm84614952014-11-25 18:46:17 -0800318 }
319 }
320
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700321 /**
Hans Boehm52d477a2016-04-01 17:42:50 -0700322 * Does a button id correspond to a trig function?
Hans Boehm017de982015-06-10 17:46:03 -0700323 * Pure function.
324 */
Hans Boehm52d477a2016-04-01 17:42:50 -0700325 public static boolean isTrigFunc(int id) {
Hans Boehm017de982015-06-10 17:46:03 -0700326 switch(id) {
327 case R.id.fun_sin:
328 case R.id.fun_cos:
329 case R.id.fun_tan:
330 case R.id.fun_arcsin:
331 case R.id.fun_arccos:
332 case R.id.fun_arctan:
Hans Boehm52d477a2016-04-01 17:42:50 -0700333 return true;
334 default:
335 return false;
336 }
337 }
338
339 /**
340 * Does a button id correspond to a function that introduces an implicit lparen?
341 * Pure function.
342 */
343 public static boolean isFunc(int id) {
344 if (isTrigFunc(id)) {
345 return true;
346 }
347 switch(id) {
Hans Boehm017de982015-06-10 17:46:03 -0700348 case R.id.fun_ln:
349 case R.id.fun_log:
350 case R.id.fun_exp:
351 return true;
352 default:
353 return false;
354 }
355 }
356
357 /**
358 * Does a button id correspond to a prefix operator?
359 * Pure function.
360 */
361 public static boolean isPrefix(int id) {
362 switch(id) {
363 case R.id.op_sqrt:
364 case R.id.op_sub:
365 return true;
366 default:
367 return false;
368 }
369 }
370
371 /**
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700372 * Does a button id correspond to a suffix operator?
373 */
Hans Boehm84614952014-11-25 18:46:17 -0800374 public static boolean isSuffix(int id) {
Justin Klaassene2711cb2015-05-28 11:13:17 -0700375 switch (id) {
376 case R.id.op_fact:
377 case R.id.op_pct:
Hans Boehm4db31b42015-05-31 12:19:05 -0700378 case R.id.op_sqr:
Justin Klaassene2711cb2015-05-28 11:13:17 -0700379 return true;
380 default:
381 return false;
382 }
Hans Boehm84614952014-11-25 18:46:17 -0800383 }
384
385 public static final int NOT_DIGIT = 10;
386
Hans Boehm08e8f322015-04-21 13:18:38 -0700387 public static final String ELLIPSIS = "\u2026";
388
Hans Boehm0b9806f2015-06-29 16:07:15 -0700389 public static final char MINUS_SIGN = '\u2212';
390
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700391 /**
392 * Map key id to digit or NOT_DIGIT
393 * Pure function.
394 */
Hans Boehm84614952014-11-25 18:46:17 -0800395 public static int digVal(int id) {
396 switch (id) {
397 case R.id.digit_0:
398 return 0;
399 case R.id.digit_1:
400 return 1;
401 case R.id.digit_2:
402 return 2;
403 case R.id.digit_3:
404 return 3;
405 case R.id.digit_4:
406 return 4;
407 case R.id.digit_5:
408 return 5;
409 case R.id.digit_6:
410 return 6;
411 case R.id.digit_7:
412 return 7;
413 case R.id.digit_8:
414 return 8;
415 case R.id.digit_9:
416 return 9;
417 default:
418 return NOT_DIGIT;
419 }
420 }
421
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700422 /**
423 * Map digit to corresponding key. Inverse of above.
424 * Pure function.
425 */
Hans Boehm84614952014-11-25 18:46:17 -0800426 public static int keyForDigVal(int v) {
427 switch(v) {
428 case 0:
429 return R.id.digit_0;
430 case 1:
431 return R.id.digit_1;
432 case 2:
433 return R.id.digit_2;
434 case 3:
435 return R.id.digit_3;
436 case 4:
437 return R.id.digit_4;
438 case 5:
439 return R.id.digit_5;
440 case 6:
441 return R.id.digit_6;
442 case 7:
443 return R.id.digit_7;
444 case 8:
445 return R.id.digit_8;
446 case 9:
447 return R.id.digit_9;
448 default:
449 return View.NO_ID;
450 }
451 }
452
Hans Boehm425ed0a2015-05-19 18:27:31 -0700453 // The following two are only used for recognizing additional
454 // input characters from a physical keyboard. They are not used
455 // for output internationalization.
456 private static char mDecimalPt;
Hans Boehm84614952014-11-25 18:46:17 -0800457
Hans Boehmffda5282015-05-18 15:00:12 -0700458 private static char mPiChar;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700459
Hans Boehmffda5282015-05-18 15:00:12 -0700460 /**
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700461 * Character used as a placeholder for digits that are currently unknown in a result that
462 * is being computed. We initially generate blanks, and then use this as a replacement
463 * during final translation.
Hans Boehmffda5282015-05-18 15:00:12 -0700464 * <p/>
465 * Note: the character must correspond closely to the width of a digit,
466 * otherwise the UI will visibly shift once the computation is finished.
467 */
468 private static final char CHAR_DIGIT_UNKNOWN = '\u2007';
469
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700470 /**
471 * Map typed function name strings to corresponding button ids.
472 * We (now redundantly?) include both localized and English names.
473 */
Hans Boehmffda5282015-05-18 15:00:12 -0700474 private static HashMap<String, Integer> sKeyValForFun;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700475
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700476 /**
477 * Result string corresponding to a character in the calculator result.
478 * The string values in the map are expected to be one character long.
479 */
Hans Boehmffda5282015-05-18 15:00:12 -0700480 private static HashMap<Character, String> sOutputForResultChar;
Hans Boehm013969e2015-04-13 20:29:47 -0700481
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700482 /**
Hans Boehma04e0fa2016-11-22 11:53:18 -0800483 * Locale corresponding to preceding map and character constants.
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700484 * We recompute the map if this is not the current locale.
485 */
Hans Boehma04e0fa2016-11-22 11:53:18 -0800486 private static Locale sLocaleForMaps = null;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700487
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700488 /**
489 * Activity to use for looking up buttons.
490 */
491 private static Activity mActivity;
Hans Boehm013969e2015-04-13 20:29:47 -0700492
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700493 /**
494 * Set acttivity used for looking up button labels.
495 * Call only from UI thread.
496 */
Hans Boehm013969e2015-04-13 20:29:47 -0700497 public static void setActivity(Activity a) {
498 mActivity = a;
499 }
500
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700501 /**
502 * Return the button id corresponding to the supplied character or return NO_ID.
503 * Called only by UI thread.
504 */
Hans Boehm013969e2015-04-13 20:29:47 -0700505 public static int keyForChar(char c) {
506 validateMaps();
Hans Boehm84614952014-11-25 18:46:17 -0800507 if (Character.isDigit(c)) {
508 int i = Character.digit(c, 10);
509 return KeyMaps.keyForDigVal(i);
510 }
Hans Boehm84614952014-11-25 18:46:17 -0800511 switch (c) {
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700512 case '.':
513 case ',':
514 return R.id.dec_point;
515 case '-':
Hans Boehm0b9806f2015-06-29 16:07:15 -0700516 case MINUS_SIGN:
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700517 return R.id.op_sub;
518 case '+':
519 return R.id.op_add;
520 case '*':
Hans Boehm0b9806f2015-06-29 16:07:15 -0700521 case '\u00D7': // MULTIPLICATION SIGN
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700522 return R.id.op_mul;
523 case '/':
Hans Boehm0b9806f2015-06-29 16:07:15 -0700524 case '\u00F7': // DIVISION SIGN
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700525 return R.id.op_div;
526 // We no longer localize function names, so they can't start with an 'e' or 'p'.
527 case 'e':
528 case 'E':
529 return R.id.const_e;
530 case 'p':
531 case 'P':
532 return R.id.const_pi;
533 case '^':
534 return R.id.op_pow;
535 case '!':
536 return R.id.op_fact;
Justin Klaassene2711cb2015-05-28 11:13:17 -0700537 case '%':
538 return R.id.op_pct;
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700539 case '(':
540 return R.id.lparen;
541 case ')':
542 return R.id.rparen;
543 default:
544 if (c == mDecimalPt) return R.id.dec_point;
545 if (c == mPiChar) return R.id.const_pi;
546 // pi is not translated, but it might be typable on a Greek keyboard,
Hans Boehm0b9806f2015-06-29 16:07:15 -0700547 // or pasted in, so we check ...
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700548 return View.NO_ID;
Hans Boehm84614952014-11-25 18:46:17 -0800549 }
550 }
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700551
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700552 /**
553 * Add information corresponding to the given button id to sKeyValForFun, to be used
554 * when mapping keyboard input to button ids.
555 */
Hans Boehm013969e2015-04-13 20:29:47 -0700556 static void addButtonToFunMap(int button_id) {
557 Button button = (Button)mActivity.findViewById(button_id);
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700558 sKeyValForFun.put(button.getText().toString(), button_id);
559 }
560
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700561 /**
562 * Add information corresponding to the given button to sOutputForResultChar, to be used
563 * when translating numbers on output.
564 */
Hans Boehm013969e2015-04-13 20:29:47 -0700565 static void addButtonToOutputMap(char c, int button_id) {
566 Button button = (Button)mActivity.findViewById(button_id);
567 sOutputForResultChar.put(c, button.getText().toString());
568 }
569
Hans Boehma04e0fa2016-11-22 11:53:18 -0800570 /**
571 * Ensure that the preceding map and character constants correspond to the current locale.
572 * Called only by UI thread.
573 */
Hans Boehm013969e2015-04-13 20:29:47 -0700574 static void validateMaps() {
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700575 Locale locale = Locale.getDefault();
Hans Boehma04e0fa2016-11-22 11:53:18 -0800576 if (!locale.equals(sLocaleForMaps)) {
577 Log.v ("Calculator", "Setting locale to: " + locale.toLanguageTag());
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700578 sKeyValForFun = new HashMap<String, Integer>();
579 sKeyValForFun.put("sin", R.id.fun_sin);
580 sKeyValForFun.put("cos", R.id.fun_cos);
581 sKeyValForFun.put("tan", R.id.fun_tan);
582 sKeyValForFun.put("arcsin", R.id.fun_arcsin);
583 sKeyValForFun.put("arccos", R.id.fun_arccos);
584 sKeyValForFun.put("arctan", R.id.fun_arctan);
585 sKeyValForFun.put("asin", R.id.fun_arcsin);
586 sKeyValForFun.put("acos", R.id.fun_arccos);
587 sKeyValForFun.put("atan", R.id.fun_arctan);
588 sKeyValForFun.put("ln", R.id.fun_ln);
589 sKeyValForFun.put("log", R.id.fun_log);
590 sKeyValForFun.put("sqrt", R.id.op_sqrt); // special treatment
Hans Boehm013969e2015-04-13 20:29:47 -0700591 addButtonToFunMap(R.id.fun_sin);
592 addButtonToFunMap(R.id.fun_cos);
593 addButtonToFunMap(R.id.fun_tan);
594 addButtonToFunMap(R.id.fun_arcsin);
595 addButtonToFunMap(R.id.fun_arccos);
596 addButtonToFunMap(R.id.fun_arctan);
597 addButtonToFunMap(R.id.fun_ln);
598 addButtonToFunMap(R.id.fun_log);
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700599
600 // Set locale-dependent character "constants"
Hans Boehm013969e2015-04-13 20:29:47 -0700601 mDecimalPt =
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700602 DecimalFormatSymbols.getInstance().getDecimalSeparator();
Hans Boehm425ed0a2015-05-19 18:27:31 -0700603 // We recognize this in keyboard input, even if we use
604 // a different character.
Hans Boehm013969e2015-04-13 20:29:47 -0700605 Resources res = mActivity.getResources();
Hans Boehm425ed0a2015-05-19 18:27:31 -0700606 mPiChar = 0;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700607 String piString = res.getString(R.string.const_pi);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700608 if (piString.length() == 1) {
609 mPiChar = piString.charAt(0);
610 }
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700611
Hans Boehm013969e2015-04-13 20:29:47 -0700612 sOutputForResultChar = new HashMap<Character, String>();
613 sOutputForResultChar.put('e', "E");
614 sOutputForResultChar.put('E', "E");
Hans Boehmffda5282015-05-18 15:00:12 -0700615 sOutputForResultChar.put(' ', String.valueOf(CHAR_DIGIT_UNKNOWN));
Hans Boehm08e8f322015-04-21 13:18:38 -0700616 sOutputForResultChar.put(ELLIPSIS.charAt(0), ELLIPSIS);
Hans Boehm995e5eb2016-02-08 11:03:01 -0800617 // Translate numbers for fraction display, but not the separating slash, which appears
618 // to be universal. We also do not translate the ln, sqrt, pi
Hans Boehm013969e2015-04-13 20:29:47 -0700619 sOutputForResultChar.put('/', "/");
Hans Boehm995e5eb2016-02-08 11:03:01 -0800620 sOutputForResultChar.put('(', "(");
621 sOutputForResultChar.put(')', ")");
622 sOutputForResultChar.put('l', "l");
623 sOutputForResultChar.put('n', "n");
Hans Boehm24c91ed2016-06-30 18:53:44 -0700624 sOutputForResultChar.put(',',
625 String.valueOf(DecimalFormatSymbols.getInstance().getGroupingSeparator()));
Hans Boehm995e5eb2016-02-08 11:03:01 -0800626 sOutputForResultChar.put('\u221A', "\u221A"); // SQUARE ROOT
627 sOutputForResultChar.put('\u03C0', "\u03C0"); // GREEK SMALL LETTER PI
Hans Boehm013969e2015-04-13 20:29:47 -0700628 addButtonToOutputMap('-', R.id.op_sub);
Hans Boehm425ed0a2015-05-19 18:27:31 -0700629 addButtonToOutputMap('.', R.id.dec_point);
Hans Boehm013969e2015-04-13 20:29:47 -0700630 for (int i = 0; i <= 9; ++i) {
631 addButtonToOutputMap((char)('0' + i), keyForDigVal(i));
632 }
633
Hans Boehma04e0fa2016-11-22 11:53:18 -0800634 sLocaleForMaps = locale;
Hans Boehm013969e2015-04-13 20:29:47 -0700635
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700636 }
637 }
638
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700639 /**
640 * Return function button id for the substring of s starting at pos and ending with
641 * the next "(". Return NO_ID if there is none.
642 * We currently check for both (possibly localized) button labels, and standard
643 * English names. (They should currently be the same, and hence this is currently redundant.)
644 * Callable only from UI thread.
645 */
Hans Boehm013969e2015-04-13 20:29:47 -0700646 public static int funForString(String s, int pos) {
647 validateMaps();
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700648 int parenPos = s.indexOf('(', pos);
649 if (parenPos != -1) {
650 String funString = s.substring(pos, parenPos);
651 Integer keyValue = sKeyValForFun.get(funString);
652 if (keyValue == null) return View.NO_ID;
653 return keyValue;
654 }
655 return View.NO_ID;
656 }
Hans Boehm013969e2015-04-13 20:29:47 -0700657
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700658 /**
659 * Return the localization of the string s representing a numeric answer.
660 * Callable only from UI thread.
Hans Boehm995e5eb2016-02-08 11:03:01 -0800661 * A trailing e is treated as the mathematical constant, not an exponent.
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700662 */
Hans Boehm013969e2015-04-13 20:29:47 -0700663 public static String translateResult(String s) {
664 StringBuilder result = new StringBuilder();
665 int len = s.length();
666 validateMaps();
667 for (int i = 0; i < len; ++i) {
668 char c = s.charAt(i);
Hans Boehm995e5eb2016-02-08 11:03:01 -0800669 if (i < len - 1 || c != 'e') {
670 String translation = sOutputForResultChar.get(c);
671 if (translation == null) {
672 // Should not get here. Report if we do.
673 Log.v("Calculator", "Bad character:" + c);
674 result.append(String.valueOf(c));
675 } else {
676 result.append(translation);
677 }
Hans Boehm013969e2015-04-13 20:29:47 -0700678 }
679 }
680 return result.toString();
681 }
682
Hans Boehm84614952014-11-25 18:46:17 -0800683}