diff options
Diffstat (limited to 'src/com/android')
-rw-r--r-- | src/com/android/calculator2/Calculator.java | 27 | ||||
-rw-r--r-- | src/com/android/calculator2/CalculatorExpr.java | 45 | ||||
-rw-r--r-- | src/com/android/calculator2/CalculatorText.java | 19 | ||||
-rw-r--r-- | src/com/android/calculator2/KeyMaps.java | 51 |
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. */ |