summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Boehm <hboehm@google.com>2015-07-21 20:46:52 +0000
committerAndroid Git Automerger <android-git-automerger@android.com>2015-07-21 20:46:52 +0000
commit70f49ecd24447f735e78143358abd716482f783e (patch)
tree19084b803f720bdf009411729be023e0f06c8023
parent432f67c4a0f77880f6def1150ea803b54367a175 (diff)
parent9d75c1ac50abcd44ab5870a56e112196dc8c87c1 (diff)
downloadandroid_packages_apps_ExactCalculator-70f49ecd24447f735e78143358abd716482f783e.tar.gz
android_packages_apps_ExactCalculator-70f49ecd24447f735e78143358abd716482f783e.tar.bz2
android_packages_apps_ExactCalculator-70f49ecd24447f735e78143358abd716482f783e.zip
am 9d75c1ac: am 8a4f81c5: More correctly pronounce advanced operators in Talkback
* commit '9d75c1ac50abcd44ab5870a56e112196dc8c87c1': More correctly pronounce advanced operators in Talkback
-rw-r--r--src/com/android/calculator2/Calculator.java27
-rw-r--r--src/com/android/calculator2/CalculatorExpr.java45
-rw-r--r--src/com/android/calculator2/CalculatorText.java19
-rw-r--r--src/com/android/calculator2/KeyMaps.java51
4 files changed, 112 insertions, 30 deletions
diff --git a/src/com/android/calculator2/Calculator.java b/src/com/android/calculator2/Calculator.java
index f1c9835..4eecb50 100644
--- a/src/com/android/calculator2/Calculator.java
+++ b/src/com/android/calculator2/Calculator.java
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-// TODO: Better indication of when the result is known to be exact.
-// TODO: Check and possibly fix accessability issues.
// TODO: Copy & more general paste in formula? Note that this requires
// great care: Currently the text version of a displayed formula
// is not directly useful for re-evaluating the formula later, since
@@ -44,8 +42,10 @@ import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.view.ViewPager;
import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
+import android.text.TextUtils;
import android.util.Property;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
@@ -210,9 +210,13 @@ public class Calculator extends Activity
private View mCurrentButton;
private Animator mCurrentAnimator;
- private String mUnprocessedChars = null; // Characters that were recently entered
- // at the end of the display that have not yet
- // been added to the underlying expression.
+ // Characters that were recently entered at the end of the display that have not yet
+ // been added to the underlying expression.
+ private String mUnprocessedChars = null;
+
+ // Color to highlight unprocessed characters from physical keyboard.
+ // TODO: should probably match this to the error color?
+ private ForegroundColorSpan mUnprocessedColorSpan = new ForegroundColorSpan(Color.RED);
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -536,18 +540,13 @@ public class Calculator extends Activity
}
void redisplayFormula() {
- String formula = mEvaluator.getExpr().toString(this);
+ SpannableStringBuilder formula = mEvaluator.getExpr().toSpannableStringBuilder(this);
if (mUnprocessedChars != null) {
// Add and highlight characters we couldn't process.
- SpannableString formatted = new SpannableString(formula + mUnprocessedChars);
- // TODO: should probably match this to the error color.
- formatted.setSpan(new ForegroundColorSpan(Color.RED),
- formula.length(), formatted.length(),
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- mFormulaText.changeTextTo(formatted);
- } else {
- mFormulaText.changeTextTo(formula);
+ formula.append(mUnprocessedChars, mUnprocessedColorSpan,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
+ mFormulaText.changeTextTo(formula);
}
@Override
diff --git a/src/com/android/calculator2/CalculatorExpr.java b/src/com/android/calculator2/CalculatorExpr.java
index 3023b5c..8a008b8 100644
--- a/src/com/android/calculator2/CalculatorExpr.java
+++ b/src/com/android/calculator2/CalculatorExpr.java
@@ -21,6 +21,11 @@ import com.hp.creals.CR;
import com.hp.creals.UnaryCRFunction;
import android.content.Context;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.TtsSpan;
+import android.text.style.TtsSpan.TextBuilder;
import android.util.Log;
import java.math.BigInteger;
@@ -46,11 +51,19 @@ class CalculatorExpr {
private static abstract class Token {
abstract TokenKind kind();
+
+ /**
+ * Write kind as Byte followed by data needed by subclass constructor.
+ */
abstract void write(DataOutput out) throws IOException;
- // Implementation writes kind as Byte followed by
- // data read by constructor.
- abstract String toString(Context context);
- // We need the context to convert button ids to strings.
+
+ /**
+ * Return a textual representation of the token.
+ * The result is suitable for either display as part od the formula or TalkBack use.
+ * It may be a SpannableString that includes added TalkBack information.
+ * @param context context used for converting button ids to strings
+ */
+ abstract CharSequence toCharSequence(Context context);
}
// An operator token
@@ -68,8 +81,16 @@ class CalculatorExpr {
out.writeInt(mId);
}
@Override
- public String toString(Context context) {
- return KeyMaps.toString(context, mId);
+ public CharSequence toCharSequence(Context context) {
+ String desc = KeyMaps.toDescriptiveString(context, mId);
+ if (desc != null) {
+ SpannableString result = new SpannableString(KeyMaps.toString(context, mId));
+ Object descSpan = new TtsSpan.TextBuilder(desc).build();
+ result.setSpan(descSpan, 0, result.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ return result;
+ } else {
+ return KeyMaps.toString(context, mId);
+ }
}
@Override
TokenKind kind() { return TokenKind.OPERATOR; }
@@ -193,7 +214,7 @@ class CalculatorExpr {
}
@Override
- String toString(Context context) {
+ CharSequence toCharSequence(Context context) {
return toString();
}
@@ -323,7 +344,7 @@ class CalculatorExpr {
}
}
@Override
- String toString(Context context) {
+ CharSequence toCharSequence(Context context) {
return KeyMaps.translateResult(mShortRep);
}
@Override
@@ -1019,11 +1040,11 @@ class CalculatorExpr {
}
// Produce a string representation of the expression itself
- String toString(Context context) {
- StringBuilder sb = new StringBuilder();
+ SpannableStringBuilder toSpannableStringBuilder(Context context) {
+ SpannableStringBuilder ssb = new SpannableStringBuilder();
for (Token t: mExpr) {
- sb.append(t.toString(context));
+ ssb.append(t.toCharSequence(context));
}
- return sb.toString();
+ return ssb;
}
}
diff --git a/src/com/android/calculator2/CalculatorText.java b/src/com/android/calculator2/CalculatorText.java
index 4b0b0c9..109c2af 100644
--- a/src/com/android/calculator2/CalculatorText.java
+++ b/src/com/android/calculator2/CalculatorText.java
@@ -224,11 +224,22 @@ public class CalculatorText extends AlignedTextView implements View.OnLongClickL
* Otherwise, e.g. after deletion, announce the entire new text.
*/
public void changeTextTo(CharSequence newText) {
- CharSequence oldText = getText();
+ final CharSequence oldText = getText();
if (startsWith(newText, oldText)) {
- int newLen = newText.length();
- int oldLen = oldText.length();
- if (oldLen != newLen) {
+ final int newLen = newText.length();
+ final int oldLen = oldText.length();
+ if (newLen == oldLen + 1) {
+ // The algorithm for pronouncing a single character doesn't seem
+ // to respect our hints. Don't give it the choice.
+ final char c = newText.charAt(oldLen);
+ final int id = KeyMaps.keyForChar(c);
+ final String descr = KeyMaps.toDescriptiveString(getContext(), id);
+ if (descr != null) {
+ announceForAccessibility(descr);
+ } else {
+ announceForAccessibility(String.valueOf(c));
+ }
+ } else if (newLen > oldLen) {
announceForAccessibility(newText.subSequence(oldLen, newLen));
}
} else {
diff --git a/src/com/android/calculator2/KeyMaps.java b/src/com/android/calculator2/KeyMaps.java
index b4bfdf9..f99850b 100644
--- a/src/com/android/calculator2/KeyMaps.java
+++ b/src/com/android/calculator2/KeyMaps.java
@@ -115,6 +115,57 @@ public class KeyMaps {
}
/**
+ * Map key id to corresponding (internationalized) descriptive string that can be used
+ * to correctly read back a formula.
+ * Only used for operators and individual characters; not used inside constants.
+ * Returns null when we don't need a descriptive string.
+ * Pure function.
+ */
+ public static String toDescriptiveString(Context context, int id) {
+ switch(id) {
+ case R.id.op_fact:
+ return context.getString(R.string.desc_op_fact);
+ case R.id.fun_sin:
+ return context.getString(R.string.desc_fun_sin)
+ + " " + context.getString(R.string.desc_lparen);
+ case R.id.fun_cos:
+ return context.getString(R.string.desc_fun_cos)
+ + " " + context.getString(R.string.desc_lparen);
+ case R.id.fun_tan:
+ return context.getString(R.string.desc_fun_tan)
+ + " " + context.getString(R.string.desc_lparen);
+ case R.id.fun_arcsin:
+ return context.getString(R.string.desc_fun_arcsin)
+ + " " + context.getString(R.string.desc_lparen);
+ case R.id.fun_arccos:
+ return context.getString(R.string.desc_fun_arccos)
+ + " " + context.getString(R.string.desc_lparen);
+ case R.id.fun_arctan:
+ return context.getString(R.string.desc_fun_arctan)
+ + " " + context.getString(R.string.desc_lparen);
+ case R.id.fun_ln:
+ return context.getString(R.string.desc_fun_ln)
+ + " " + context.getString(R.string.desc_lparen);
+ case R.id.fun_log:
+ return context.getString(R.string.desc_fun_log)
+ + " " + context.getString(R.string.desc_lparen);
+ case R.id.fun_exp:
+ return context.getString(R.string.desc_fun_exp)
+ + " " + context.getString(R.string.desc_lparen);
+ case R.id.lparen:
+ return context.getString(R.string.desc_lparen);
+ case R.id.rparen:
+ return context.getString(R.string.desc_rparen);
+ case R.id.op_pow:
+ return context.getString(R.string.desc_op_pow);
+ case R.id.dec_point:
+ return context.getString(R.string.desc_dec_point);
+ default:
+ return null;
+ }
+ }
+
+ /**
* Does a button id correspond to a binary operator?
* Pure function.
*/