184614957604253d51296e06c97daced699a0a9deHans Boehm/* 24a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm * Copyright (C) 2015 The Android Open Source Project 384614957604253d51296e06c97daced699a0a9deHans Boehm * 484614957604253d51296e06c97daced699a0a9deHans Boehm * Licensed under the Apache License, Version 2.0 (the "License"); 584614957604253d51296e06c97daced699a0a9deHans Boehm * you may not use this file except in compliance with the License. 684614957604253d51296e06c97daced699a0a9deHans Boehm * You may obtain a copy of the License at 784614957604253d51296e06c97daced699a0a9deHans Boehm * 884614957604253d51296e06c97daced699a0a9deHans Boehm * http://www.apache.org/licenses/LICENSE-2.0 984614957604253d51296e06c97daced699a0a9deHans Boehm * 1084614957604253d51296e06c97daced699a0a9deHans Boehm * Unless required by applicable law or agreed to in writing, software 1184614957604253d51296e06c97daced699a0a9deHans Boehm * distributed under the License is distributed on an "AS IS" BASIS, 1284614957604253d51296e06c97daced699a0a9deHans Boehm * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1384614957604253d51296e06c97daced699a0a9deHans Boehm * See the License for the specific language governing permissions and 1484614957604253d51296e06c97daced699a0a9deHans Boehm * limitations under the License. 1584614957604253d51296e06c97daced699a0a9deHans Boehm */ 1684614957604253d51296e06c97daced699a0a9deHans Boehm 1784614957604253d51296e06c97daced699a0a9deHans Boehmpackage com.android.calculator2; 1884614957604253d51296e06c97daced699a0a9deHans Boehm 194a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehmimport android.content.ClipData; 204a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehmimport android.content.ClipDescription; 214459516a2c116ddf80725d6a96a69186ccddc329Justin Klaassenimport android.content.ClipboardManager; 224a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehmimport android.content.Context; 237f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehmimport android.graphics.Rect; 244459516a2c116ddf80725d6a96a69186ccddc329Justin Klaassenimport android.text.Layout; 257f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehmimport android.text.Spannable; 2684614957604253d51296e06c97daced699a0a9deHans Boehmimport android.text.SpannableString; 271176f23dae4d3740782e46463003e9f36a381c9dHans Boehmimport android.text.Spanned; 284459516a2c116ddf80725d6a96a69186ccddc329Justin Klaassenimport android.text.TextPaint; 297f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehmimport android.text.style.BackgroundColorSpan; 3084614957604253d51296e06c97daced699a0a9deHans Boehmimport android.text.style.ForegroundColorSpan; 314a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehmimport android.util.AttributeSet; 324a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehmimport android.view.ActionMode; 334a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehmimport android.view.GestureDetector; 344a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehmimport android.view.Menu; 354a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehmimport android.view.MenuInflater; 364a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehmimport android.view.MenuItem; 374a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehmimport android.view.MotionEvent; 384a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehmimport android.view.View; 394459516a2c116ddf80725d6a96a69186ccddc329Justin Klaassenimport android.widget.OverScroller; 404a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehmimport android.widget.Toast; 4184614957604253d51296e06c97daced699a0a9deHans Boehm 4284614957604253d51296e06c97daced699a0a9deHans Boehm// A text widget that is "infinitely" scrollable to the right, 4384614957604253d51296e06c97daced699a0a9deHans Boehm// and obtains the text to display via a callback to Logic. 444459516a2c116ddf80725d6a96a69186ccddc329Justin Klaassenpublic class CalculatorResult extends AlignedTextView { 4561568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm static final int MAX_RIGHT_SCROLL = 10000000; 4608e8f322b0d93e06aaa2a15acc869dfd70791461Hans Boehm static final int INVALID = MAX_RIGHT_SCROLL + 10000; 4784614957604253d51296e06c97daced699a0a9deHans Boehm // A larger value is unlikely to avoid running out of space 4884614957604253d51296e06c97daced699a0a9deHans Boehm final OverScroller mScroller; 4984614957604253d51296e06c97daced699a0a9deHans Boehm final GestureDetector mGestureDetector; 5084614957604253d51296e06c97daced699a0a9deHans Boehm class MyTouchListener implements View.OnTouchListener { 5184614957604253d51296e06c97daced699a0a9deHans Boehm @Override 5284614957604253d51296e06c97daced699a0a9deHans Boehm public boolean onTouch(View v, MotionEvent event) { 534459516a2c116ddf80725d6a96a69186ccddc329Justin Klaassen return mGestureDetector.onTouchEvent(event); 5484614957604253d51296e06c97daced699a0a9deHans Boehm } 5584614957604253d51296e06c97daced699a0a9deHans Boehm } 5684614957604253d51296e06c97daced699a0a9deHans Boehm final MyTouchListener mTouchListener = new MyTouchListener(); 5784614957604253d51296e06c97daced699a0a9deHans Boehm private Evaluator mEvaluator; 5884614957604253d51296e06c97daced699a0a9deHans Boehm private boolean mScrollable = false; 5984614957604253d51296e06c97daced699a0a9deHans Boehm // A scrollable result is currently displayed. 60760a9dc6573e35bcbf9097dece06cd90c4abb551Hans Boehm private boolean mValid = false; 61c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // The result holds something valid; either a a number or an error 62c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // message. 635e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm // A suffix of "Pos" denotes a pixel offset. Zero represents a scroll position 645e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm // in which the decimal point is just barely visible on the right of the display. 65c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm private int mCurrentPos;// Position of right of display relative to decimal point, in pixels. 66c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // Large positive values mean the decimal point is scrolled off the 67c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // left of the display. Zero means decimal point is barely displayed 68c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // on the right. 6961568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm private int mLastPos; // Position already reflected in display. Pixels. 7061568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm private int mMinPos; // Minimum position before all digits disappear off the right. Pixels. 7161568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm private int mMaxPos; // Maximum position before we start displaying the infinite 7261568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm // sequence of trailing zeroes on the right. Pixels. 735e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm // In the following, we use a suffix of Offset to denote a character position in a numeric 745e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm // string relative to the decimal point. Positive is to the right and negative is to 755e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm // the left. 1 = tenths position, -1 = units. Integer.MAX_VALUE is sometimes used 765e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm // for the offset of the last digit in an a nonterminating decimal expansion. 775e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm // We use the suffix "Index" to denote a zero-based index into a string representing a 785e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm // result. 795e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm // TODO: Apply the same convention to other classes. 805e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm private int mMaxCharOffset; // Character offset from decimal point of rightmost digit 815e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm // that should be displayed. Essentially the same as 825e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm private int mLsdOffset; // Position of least-significant digit in result 835e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm private int mLastDisplayedOffset; // Offset of last digit actually displayed after adding 84f6dae114d7e7c5a2d2d5a6b0f2c8d1fc6cf2937fHans Boehm // exponent. 854459516a2c116ddf80725d6a96a69186ccddc329Justin Klaassen private final Object mWidthLock = new Object(); 864a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm // Protects the next two fields. 874a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm private int mWidthConstraint = -1; 88a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // Our total width in pixels minus space for ellipsis. 894459516a2c116ddf80725d6a96a69186ccddc329Justin Klaassen private float mCharWidth = 1; 90c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // Maximum character width. For now we pretend that all characters 914a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm // have this width. 92c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // TODO: We're not really using a fixed width font. But it appears 93c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // to be close enough for the characters we use that the difference 94c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // is not noticeable. 954a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm private static final int MAX_WIDTH = 100; 964a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm // Maximum number of digits displayed 9750ed320c1cdd1b3624f73956a80eb2f2c2f5a01dHans Boehm public static final int MAX_LEADING_ZEROES = 6; 98a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // Maximum number of leading zeroes after decimal point before we 99a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // switch to scientific notation with negative exponent. 10050ed320c1cdd1b3624f73956a80eb2f2c2f5a01dHans Boehm public static final int MAX_TRAILING_ZEROES = 6; 101a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // Maximum number of trailing zeroes before the decimal point before 102a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // we switch to scientific notation with positive exponent. 103a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm private static final int SCI_NOTATION_EXTRA = 1; 104a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // Extra digits for standard scientific notation. In this case we 10580018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm // have a decimal point and no ellipsis. 10680018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm // We assume that we do not drop digits to make room for the decimal 10780018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm // point in ordinary scientific notation. Thus >= 1. 1081176f23dae4d3740782e46463003e9f36a381c9dHans Boehm private ActionMode mActionMode; 1091176f23dae4d3740782e46463003e9f36a381c9dHans Boehm private final ForegroundColorSpan mExponentColorSpan; 11084614957604253d51296e06c97daced699a0a9deHans Boehm 11184614957604253d51296e06c97daced699a0a9deHans Boehm public CalculatorResult(Context context, AttributeSet attrs) { 11284614957604253d51296e06c97daced699a0a9deHans Boehm super(context, attrs); 11384614957604253d51296e06c97daced699a0a9deHans Boehm mScroller = new OverScroller(context); 11484614957604253d51296e06c97daced699a0a9deHans Boehm mGestureDetector = new GestureDetector(context, 11584614957604253d51296e06c97daced699a0a9deHans Boehm new GestureDetector.SimpleOnGestureListener() { 11684614957604253d51296e06c97daced699a0a9deHans Boehm @Override 117d48b756434bda6a5f66740a8ea603aca1f536544Justin Klaassen public boolean onDown(MotionEvent e) { 118d48b756434bda6a5f66740a8ea603aca1f536544Justin Klaassen return true; 119d48b756434bda6a5f66740a8ea603aca1f536544Justin Klaassen } 120d48b756434bda6a5f66740a8ea603aca1f536544Justin Klaassen @Override 12184614957604253d51296e06c97daced699a0a9deHans Boehm public boolean onFling(MotionEvent e1, MotionEvent e2, 12284614957604253d51296e06c97daced699a0a9deHans Boehm float velocityX, float velocityY) { 12384614957604253d51296e06c97daced699a0a9deHans Boehm if (!mScroller.isFinished()) { 12484614957604253d51296e06c97daced699a0a9deHans Boehm mCurrentPos = mScroller.getFinalX(); 12584614957604253d51296e06c97daced699a0a9deHans Boehm } 12684614957604253d51296e06c97daced699a0a9deHans Boehm mScroller.forceFinished(true); 1271176f23dae4d3740782e46463003e9f36a381c9dHans Boehm stopActionMode(); 128fbcef7005de4436682072927f83000b502928d25Hans Boehm CalculatorResult.this.cancelLongPress(); 129fbcef7005de4436682072927f83000b502928d25Hans Boehm // Ignore scrolls of error string, etc. 130fbcef7005de4436682072927f83000b502928d25Hans Boehm if (!mScrollable) return true; 131c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm mScroller.fling(mCurrentPos, 0, - (int) velocityX, 0 /* horizontal only */, 13261568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm mMinPos, mMaxPos, 0, 0); 1334459516a2c116ddf80725d6a96a69186ccddc329Justin Klaassen postInvalidateOnAnimation(); 13484614957604253d51296e06c97daced699a0a9deHans Boehm return true; 13584614957604253d51296e06c97daced699a0a9deHans Boehm } 13684614957604253d51296e06c97daced699a0a9deHans Boehm @Override 13784614957604253d51296e06c97daced699a0a9deHans Boehm public boolean onScroll(MotionEvent e1, MotionEvent e2, 13884614957604253d51296e06c97daced699a0a9deHans Boehm float distanceX, float distanceY) { 13961568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm int distance = (int)distanceX; 14084614957604253d51296e06c97daced699a0a9deHans Boehm if (!mScroller.isFinished()) { 14184614957604253d51296e06c97daced699a0a9deHans Boehm mCurrentPos = mScroller.getFinalX(); 14284614957604253d51296e06c97daced699a0a9deHans Boehm } 14384614957604253d51296e06c97daced699a0a9deHans Boehm mScroller.forceFinished(true); 1441176f23dae4d3740782e46463003e9f36a381c9dHans Boehm stopActionMode(); 14584614957604253d51296e06c97daced699a0a9deHans Boehm CalculatorResult.this.cancelLongPress(); 14684614957604253d51296e06c97daced699a0a9deHans Boehm if (!mScrollable) return true; 14761568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm if (mCurrentPos + distance < mMinPos) { 14861568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm distance = mMinPos - mCurrentPos; 14961568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm } else if (mCurrentPos + distance > mMaxPos) { 15061568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm distance = mMaxPos - mCurrentPos; 15161568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm } 15284614957604253d51296e06c97daced699a0a9deHans Boehm int duration = (int)(e2.getEventTime() - e1.getEventTime()); 15384614957604253d51296e06c97daced699a0a9deHans Boehm if (duration < 1 || duration > 100) duration = 10; 15461568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm mScroller.startScroll(mCurrentPos, 0, distance, 0, (int)duration); 1554459516a2c116ddf80725d6a96a69186ccddc329Justin Klaassen postInvalidateOnAnimation(); 15684614957604253d51296e06c97daced699a0a9deHans Boehm return true; 15784614957604253d51296e06c97daced699a0a9deHans Boehm } 1584a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm @Override 1594a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm public void onLongPress(MotionEvent e) { 1601176f23dae4d3740782e46463003e9f36a381c9dHans Boehm if (mValid) { 1611176f23dae4d3740782e46463003e9f36a381c9dHans Boehm mActionMode = startActionMode(mCopyActionModeCallback, 1621176f23dae4d3740782e46463003e9f36a381c9dHans Boehm ActionMode.TYPE_FLOATING); 1631176f23dae4d3740782e46463003e9f36a381c9dHans Boehm } 1644a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm } 16584614957604253d51296e06c97daced699a0a9deHans Boehm }); 16684614957604253d51296e06c97daced699a0a9deHans Boehm setOnTouchListener(mTouchListener); 16784614957604253d51296e06c97daced699a0a9deHans Boehm setHorizontallyScrolling(false); // do it ourselves 16884614957604253d51296e06c97daced699a0a9deHans Boehm setCursorVisible(false); 1691176f23dae4d3740782e46463003e9f36a381c9dHans Boehm mExponentColorSpan = new ForegroundColorSpan( 1701176f23dae4d3740782e46463003e9f36a381c9dHans Boehm context.getColor(R.color.display_result_exponent_text_color)); 1714a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm 1724a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm // Copy ActionMode is triggered explicitly, not through 1734a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm // setCustomSelectionActionModeCallback. 17484614957604253d51296e06c97daced699a0a9deHans Boehm } 17584614957604253d51296e06c97daced699a0a9deHans Boehm 17684614957604253d51296e06c97daced699a0a9deHans Boehm void setEvaluator(Evaluator evaluator) { 17784614957604253d51296e06c97daced699a0a9deHans Boehm mEvaluator = evaluator; 17884614957604253d51296e06c97daced699a0a9deHans Boehm } 17984614957604253d51296e06c97daced699a0a9deHans Boehm 1804a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm @Override 1814a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 1824a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm super.onMeasure(widthMeasureSpec, heightMeasureSpec); 1834a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm 1844459516a2c116ddf80725d6a96a69186ccddc329Justin Klaassen final TextPaint paint = getPaint(); 18580018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm final Context context = getContext(); 1864459516a2c116ddf80725d6a96a69186ccddc329Justin Klaassen final float newCharWidth = Layout.getDesiredWidth("\u2007", paint); 18780018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm // Digits are presumed to have no more than newCharWidth. 18880018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm // We sometimes replace a character by an ellipsis or, due to SCI_NOTATION_EXTRA, add 18980018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm // an extra decimal separator beyond the maximum number of characters we normally allow. 19080018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm // Empirically, our minus sign is also slightly wider than a digit, so we have to 19180018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm // account for that. We never have both an ellipsis and two minus signs, and 19280018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm // we assume an ellipsis is no narrower than a minus sign. 19380018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm final float decimalSeparatorWidth = Layout.getDesiredWidth( 19480018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm context.getString(R.string.dec_point), paint); 19580018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm final float minusExtraWidth = Layout.getDesiredWidth( 19680018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm context.getString(R.string.op_sub), paint) - newCharWidth; 19780018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm final float ellipsisExtraWidth = Layout.getDesiredWidth(KeyMaps.ELLIPSIS, paint) 19880018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm - newCharWidth; 19980018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm final int extraWidth = (int) (Math.ceil(Math.max(decimalSeparatorWidth + minusExtraWidth, 20080018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm ellipsisExtraWidth)) + Math.max(minusExtraWidth, 0.0f)); 20180018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm final int newWidthConstraint = MeasureSpec.getSize(widthMeasureSpec) 20280018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm - (getPaddingLeft() + getPaddingRight()) - extraWidth; 2034a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm synchronized(mWidthLock) { 204013969e98ce9e3eb4f87ec6159b06a74d07b2592Hans Boehm mWidthConstraint = newWidthConstraint; 205013969e98ce9e3eb4f87ec6159b06a74d07b2592Hans Boehm mCharWidth = newCharWidth; 2064a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm } 2074a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm } 2084a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm 209a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // Return the length of the exponent representation for the given exponent, in 210a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // characters. 211a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm private final int expLen(int exp) { 212a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm if (exp == 0) return 0; 2135e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm final int abs_exp_digits = (int) Math.ceil(Math.log10(Math.abs((double)exp)) 2145e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm + 0.0000000001d /* Round whole numbers to next integer */); 2155e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm return abs_exp_digits + (exp >= 0 ? 1 : 2); 216a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm } 217a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm 218a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm /** 219a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm * Initiate display of a new result. 220a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm * The parameters specify various properties of the result. 221a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm * @param initPrec Initial display precision computed by evaluator. (1 = tenths digit) 222a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm * @param msd Position of most significant digit. Offset from left of string. 223a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm Evaluator.INVALID_MSD if unknown. 224a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm * @param leastDigPos Position of least significant digit (1 = tenths digit) 225a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm * or Integer.MAX_VALUE. 226a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm * @param truncatedWholePart Result up to but not including decimal point. 227a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm Currently we only use the length. 228a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm */ 229a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm void displayResult(int initPrec, int msd, int leastDigPos, String truncatedWholePart) { 230a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm initPositions(initPrec, msd, leastDigPos, truncatedWholePart); 231a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm redisplay(); 23261568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm } 23361568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm 234a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm /** 2355e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm * Set up scroll bounds (mMinPos, mMaxPos, etc.) and determine whether the result is 2365e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm * scrollable, based on the supplied information about the result. 237a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm * This is unfortunately complicated because we need to predict whether trailing digits 238a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm * will eventually be replaced by an exponent. 239a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm * Just appending the exponent during formatting would be simpler, but would produce 240a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm * jumpier results during transitions. 241a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm */ 2425e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm private void initPositions(int initPrecOffset, int msdIndex, int lsdOffset, 2435e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm String truncatedWholePart) { 244a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm float charWidth; 245a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm int maxChars = getMaxChars(); 24684614957604253d51296e06c97daced699a0a9deHans Boehm mLastPos = INVALID; 2475e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm mLsdOffset = lsdOffset; 248013969e98ce9e3eb4f87ec6159b06a74d07b2592Hans Boehm synchronized(mWidthLock) { 249a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm charWidth = mCharWidth; 250a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm } 2515e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm mCurrentPos = mMinPos = (int) Math.round(initPrecOffset * charWidth); 252a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // Prevent scrolling past initial position, which is calculated to show leading digits. 2535e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm if (msdIndex == Evaluator.INVALID_MSD) { 254a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // Possible zero value 2555e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm if (lsdOffset == Integer.MIN_VALUE) { 256a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // Definite zero value. 257a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm mMaxPos = mMinPos; 2585e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm mMaxCharOffset = (int) Math.round(mMaxPos/charWidth); 259a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm mScrollable = false; 260a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm } else { 261a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // May be very small nonzero value. Allow user to find out. 2625e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm mMaxPos = mMaxCharOffset = MAX_RIGHT_SCROLL; 2635e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm mMinPos -= charWidth; // Allow for future minus sign. 264a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm mScrollable = true; 265a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm } 266a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm return; 267013969e98ce9e3eb4f87ec6159b06a74d07b2592Hans Boehm } 268a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm int wholeLen = truncatedWholePart.length(); 269a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm int negative = truncatedWholePart.charAt(0) == '-' ? 1 : 0; 2705e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm if (msdIndex > wholeLen && msdIndex <= wholeLen + 3) { 2715e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm // Avoid tiny negative exponent; pretend msdIndex is just to the right of decimal point. 2725e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm msdIndex = wholeLen - 1; 273a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm } 2745e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm int minCharOffset = msdIndex - wholeLen; 275a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // Position of leftmost significant digit relative to dec. point. 276a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // Usually negative. 2775e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm mMaxCharOffset = MAX_RIGHT_SCROLL; // How far does it make sense to scroll right? 278a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // If msd is left of decimal point should logically be 27961568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm // mMinPos = - (int) Math.ceil(getPaint().measureText(truncatedWholePart)), but 280a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // we eventually translate to a character position by dividing by mCharWidth. 28161568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm // To avoid rounding issues, we use the analogous computation here. 2825e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm if (minCharOffset > -1 && minCharOffset < MAX_LEADING_ZEROES + 2) { 283a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // Small number of leading zeroes, avoid scientific notation. 2845e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm minCharOffset = -1; 285a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm } 2865e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm if (lsdOffset < MAX_RIGHT_SCROLL) { 2875e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm mMaxCharOffset = lsdOffset; 2885e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm if (mMaxCharOffset < -1 && mMaxCharOffset > -(MAX_TRAILING_ZEROES + 2)) { 2895e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm mMaxCharOffset = -1; 290a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm } 2915e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm // lsdOffset is positive or negative, never 0. 2925e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm int currentExpLen = 0; // Length of required standard scientific notation exponent. 2935e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm if (mMaxCharOffset < -1) { 2945e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm currentExpLen = expLen(-minCharOffset - 1); 2955e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm } else if (minCharOffset > -1 || mMaxCharOffset >= maxChars) { 296a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // Number either entirely to the right of decimal point, or decimal point not 297a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // visible when scrolled to the right. 2985e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm currentExpLen = expLen(-minCharOffset); 299a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm } 3005e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm mScrollable = (mMaxCharOffset + currentExpLen - minCharOffset + negative >= maxChars); 3015e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm int newMaxCharOffset; 3025e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm if (currentExpLen > 0) { 3035e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm if (mScrollable) { 3045e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm // We'll use exponent corresponding to leastDigPos when scrolled to right. 3055e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm newMaxCharOffset = mMaxCharOffset + expLen(-lsdOffset); 3065e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm } else { 3075e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm newMaxCharOffset = mMaxCharOffset + currentExpLen; 3085e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm } 3095e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm if (mMaxCharOffset <= -1 && newMaxCharOffset > -1) { 3105e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm // Very unlikely; just drop exponent. 3115e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm mMaxCharOffset = -1; 3125e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm } else { 3135e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm mMaxCharOffset = newMaxCharOffset; 314a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm } 315a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm } 3165e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm mMaxPos = Math.min((int) Math.round(mMaxCharOffset * charWidth), MAX_RIGHT_SCROLL); 317a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm if (!mScrollable) { 318a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // Position the number consistently with our assumptions to make sure it 319a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // actually fits. 320a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm mCurrentPos = mMaxPos; 321a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm } 32261568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm } else { 3235e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm mMaxPos = mMaxCharOffset = MAX_RIGHT_SCROLL; 324a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm mScrollable = true; 32561568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm } 32684614957604253d51296e06c97daced699a0a9deHans Boehm } 32784614957604253d51296e06c97daced699a0a9deHans Boehm 32884614957604253d51296e06c97daced699a0a9deHans Boehm void displayError(int resourceId) { 329760a9dc6573e35bcbf9097dece06cd90c4abb551Hans Boehm mValid = true; 33084614957604253d51296e06c97daced699a0a9deHans Boehm mScrollable = false; 33184614957604253d51296e06c97daced699a0a9deHans Boehm setText(resourceId); 33284614957604253d51296e06c97daced699a0a9deHans Boehm } 33384614957604253d51296e06c97daced699a0a9deHans Boehm 334013969e98ce9e3eb4f87ec6159b06a74d07b2592Hans Boehm private final int MAX_COPY_SIZE = 1000000; 335013969e98ce9e3eb4f87ec6159b06a74d07b2592Hans Boehm 336a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm /* 337a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm * Return the most significant digit position in the given string or Evaluator.INVALID_MSD. 338b13daf1050757fe3c69c2f0246de33e7e69b5fa9Hans Boehm * Unlike Evaluator.getMsdIndexOf, we treat a final 1 as significant. 339a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm */ 340b13daf1050757fe3c69c2f0246de33e7e69b5fa9Hans Boehm public static int getNaiveMsdIndexOf(String s) { 341a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm int len = s.length(); 342a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm for (int i = 0; i < len; ++i) { 343a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm char c = s.charAt(i); 344a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm if (c != '-' && c != '.' && c != '0') { 345a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm return i; 346a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm } 347a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm } 348a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm return Evaluator.INVALID_MSD; 349a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm } 350a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm 351c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // Format a result returned by Evaluator.getString() into a single line containing ellipses 352b13daf1050757fe3c69c2f0246de33e7e69b5fa9Hans Boehm // (if appropriate) and an exponent (if appropriate). precOffset is the value that was passed 353b13daf1050757fe3c69c2f0246de33e7e69b5fa9Hans Boehm // to getString and thus identifies the significance of the rightmost digit. 354a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // A value of 1 means the rightmost digits corresponds to tenths. 355a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // maxDigs is the maximum number of characters in the result. 3565e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm // We set lastDisplayedOffset[0] to the offset of the last digit actually appearing in 357f6dae114d7e7c5a2d2d5a6b0f2c8d1fc6cf2937fHans Boehm // the display. 358f6dae114d7e7c5a2d2d5a6b0f2c8d1fc6cf2937fHans Boehm // If forcePrecision is true, we make sure that the last displayed digit corresponds to 359b13daf1050757fe3c69c2f0246de33e7e69b5fa9Hans Boehm // precOffset, and allow maxDigs to be exceeded in assing the exponent. 36008e8f322b0d93e06aaa2a15acc869dfd70791461Hans Boehm // We add two distinct kinds of exponents: 3615e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm // (1) If the final result contains the leading digit we use standard scientific notation. 3625e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm // (2) If not, we add an exponent corresponding to an interpretation of the final result as 3635e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm // an integer. 36408e8f322b0d93e06aaa2a15acc869dfd70791461Hans Boehm // We add an ellipsis on the left if the result was truncated. 365c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // We add ellipses and exponents in a way that leaves most digits in the position they 366c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // would have been in had we not done so. 367c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // This minimizes jumps as a result of scrolling. Result is NOT internationalized, 3680b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm // uses "E" for exponent. 3695e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm public String formatResult(String in, int precOffset, int maxDigs, boolean truncated, 3705e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm boolean negative, int lastDisplayedOffset[], boolean forcePrecision) { 3715e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm final int minusSpace = negative ? 1 : 0; 372b13daf1050757fe3c69c2f0246de33e7e69b5fa9Hans Boehm final int msdIndex = truncated ? -1 : getNaiveMsdIndexOf(in); // INVALID_MSD is OK. 3735e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm String result = in; 374ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm if (truncated || (negative && result.charAt(0) != '-')) { 375ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm result = KeyMaps.ELLIPSIS + result.substring(1, result.length()); 376ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm // Ellipsis may be removed again in the type(1) scientific notation case. 377ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm } 378ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm final int decIndex = result.indexOf('.'); 3795e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm lastDisplayedOffset[0] = precOffset; 3805e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm if ((decIndex == -1 || msdIndex != Evaluator.INVALID_MSD 3815e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm && msdIndex - decIndex > MAX_LEADING_ZEROES + 1) && precOffset != -1) { 382a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // No decimal point displayed, and it's not just to the right of the last digit, 383a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // or we should suppress leading zeroes. 384c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // Add an exponent to let the user track which digits are currently displayed. 3855e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm // Start with type (2) exponent if we dropped no digits. -1 accounts for decimal point. 3865e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm final int initExponent = precOffset > 0 ? -precOffset : -precOffset - 1; 3875e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm int exponent = initExponent; 38808e8f322b0d93e06aaa2a15acc869dfd70791461Hans Boehm boolean hasPoint = false; 3895e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm if (!truncated && msdIndex < maxDigs - 1 3905e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm && result.length() - msdIndex + 1 + minusSpace 3915e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm <= maxDigs + SCI_NOTATION_EXTRA) { 3925e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm // Type (1) exponent computation and transformation: 393c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // Leading digit is in display window. Use standard calculator scientific notation 394c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // with one digit to the left of the decimal point. Insert decimal point and 395c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // delete leading zeroes. 396a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm // We try to keep leading digits roughly in position, and never 397f6dae114d7e7c5a2d2d5a6b0f2c8d1fc6cf2937fHans Boehm // lengthen the result by more than SCI_NOTATION_EXTRA. 3985e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm final int resLen = result.length(); 3995e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm String fraction = result.substring(msdIndex + 1, resLen); 4005e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm result = (negative ? "-" : "") + result.substring(msdIndex, msdIndex + 1) 4015e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm + "." + fraction; 402c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // Original exp was correct for decimal point at right of fraction. 403c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // Adjust by length of fraction. 4045e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm exponent = initExponent + resLen - msdIndex - 1; 40508e8f322b0d93e06aaa2a15acc869dfd70791461Hans Boehm hasPoint = true; 40608e8f322b0d93e06aaa2a15acc869dfd70791461Hans Boehm } 407ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm // Exponent can't be zero. 408ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm // Actually add the exponent of either type: 409ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm if (!forcePrecision) { 410ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm int dropDigits; // Digits to drop to make room for exponent. 411ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm if (hasPoint) { 412ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm // Type (1) exponent. 413ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm // Drop digits even if there is room. Otherwise the scrolling gets jumpy. 414ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm dropDigits = expLen(exponent); 415ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm if (dropDigits >= result.length() - 1) { 416ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm // Jumpy is better than no mantissa. Probably impossible anyway. 417ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm dropDigits = Math.max(result.length() - 2, 0); 418ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm } 419ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm } else { 420ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm // Type (2) exponent. 421ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm // Exponent depends on the number of digits we drop, which depends on 422ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm // exponent ... 423ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm for (dropDigits = 2; expLen(initExponent + dropDigits) > dropDigits; 424ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm ++dropDigits) {} 425ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm exponent = initExponent + dropDigits; 426ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm if (precOffset - dropDigits > mLsdOffset) { 427ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm // This can happen if e.g. result = 10^40 + 10^10 428ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm // It turns out we would otherwise display ...10e9 because it takes 429ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm // the same amount of space as ...1e10 but shows one more digit. 430ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm // But we don't want to display a trailing zero, even if it's free. 431ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm ++dropDigits; 432ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm ++exponent; 433a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm } 43408e8f322b0d93e06aaa2a15acc869dfd70791461Hans Boehm } 435ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm result = result.substring(0, result.length() - dropDigits); 436ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm lastDisplayedOffset[0] -= dropDigits; 437ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm } 438ec7517f38758f67233f2b06bc7bab17a8ffeccbaHans Boehm result = result + "E" + Integer.toString(exponent); 4395e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm } 4405e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm return result; 44108e8f322b0d93e06aaa2a15acc869dfd70791461Hans Boehm } 44208e8f322b0d93e06aaa2a15acc869dfd70791461Hans Boehm 443f6dae114d7e7c5a2d2d5a6b0f2c8d1fc6cf2937fHans Boehm /** 444f6dae114d7e7c5a2d2d5a6b0f2c8d1fc6cf2937fHans Boehm * Get formatted, but not internationalized, result from mEvaluator. 4455e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm * @param precOffset requested position (1 = tenths) of last included digit. 446f6dae114d7e7c5a2d2d5a6b0f2c8d1fc6cf2937fHans Boehm * @param maxSize Maximum number of characters (more or less) in result. 4475e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm * @param lastDisplayedOffset Zeroth entry is set to actual offset of last included digit, 4485e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm * after adjusting for exponent, etc. 449f6dae114d7e7c5a2d2d5a6b0f2c8d1fc6cf2937fHans Boehm * @param forcePrecision Ensure that last included digit is at pos, at the expense 450f6dae114d7e7c5a2d2d5a6b0f2c8d1fc6cf2937fHans Boehm * of treating maxSize as a soft limit. 451f6dae114d7e7c5a2d2d5a6b0f2c8d1fc6cf2937fHans Boehm */ 4525e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm private String getFormattedResult(int precOffset, int maxSize, int lastDisplayedOffset[], 453f6dae114d7e7c5a2d2d5a6b0f2c8d1fc6cf2937fHans Boehm boolean forcePrecision) { 45408e8f322b0d93e06aaa2a15acc869dfd70791461Hans Boehm final boolean truncated[] = new boolean[1]; 45508e8f322b0d93e06aaa2a15acc869dfd70791461Hans Boehm final boolean negative[] = new boolean[1]; 4565e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm final int requestedPrecOffset[] = {precOffset}; 4575e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm final String rawResult = mEvaluator.getString(requestedPrecOffset, mMaxCharOffset, 458a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm maxSize, truncated, negative); 4595e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm return formatResult(rawResult, requestedPrecOffset[0], maxSize, truncated[0], negative[0], 4605e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm lastDisplayedOffset, forcePrecision); 46108e8f322b0d93e06aaa2a15acc869dfd70791461Hans Boehm } 46208e8f322b0d93e06aaa2a15acc869dfd70791461Hans Boehm 46384614957604253d51296e06c97daced699a0a9deHans Boehm // Return entire result (within reason) up to current displayed precision. 4644a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm public String getFullText() { 465760a9dc6573e35bcbf9097dece06cd90c4abb551Hans Boehm if (!mValid) return ""; 4664a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm if (!mScrollable) return getText().toString(); 4675e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm int currentCharOffset = getCurrentCharOffset(); 468f6dae114d7e7c5a2d2d5a6b0f2c8d1fc6cf2937fHans Boehm int unused[] = new int[1]; 4695e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm return KeyMaps.translateResult(getFormattedResult(mLastDisplayedOffset, MAX_COPY_SIZE, 470f6dae114d7e7c5a2d2d5a6b0f2c8d1fc6cf2937fHans Boehm unused, true)); 47184614957604253d51296e06c97daced699a0a9deHans Boehm } 47284614957604253d51296e06c97daced699a0a9deHans Boehm 4734a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm public boolean fullTextIsExact() { 474f6dae114d7e7c5a2d2d5a6b0f2c8d1fc6cf2937fHans Boehm return !mScrollable 4755e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm || mMaxCharOffset == getCurrentCharOffset() && mMaxCharOffset != MAX_RIGHT_SCROLL; 4764a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm } 4774a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm 47861568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm /** 47961568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm * Return the maximum number of characters that will fit in the result display. 48061568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm * May be called asynchronously from non-UI thread. 48161568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm */ 48284614957604253d51296e06c97daced699a0a9deHans Boehm int getMaxChars() { 4834a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm int result; 4844a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm synchronized(mWidthLock) { 4854459516a2c116ddf80725d6a96a69186ccddc329Justin Klaassen result = (int) Math.floor(mWidthConstraint / mCharWidth); 486c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // We can apparently finish evaluating before onMeasure in CalculatorText has been 487c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // called, in which case we get 0 or -1 as the width constraint. 4884a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm } 48984614957604253d51296e06c97daced699a0a9deHans Boehm if (result <= 0) { 490c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // Return something conservatively big, to force sufficient evaluation. 4914a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm return MAX_WIDTH; 49284614957604253d51296e06c97daced699a0a9deHans Boehm } else { 49380018c885a8f8c2ae32b05c4b2bcd7ae164b04feHans Boehm return result; 49484614957604253d51296e06c97daced699a0a9deHans Boehm } 49584614957604253d51296e06c97daced699a0a9deHans Boehm } 49684614957604253d51296e06c97daced699a0a9deHans Boehm 49761568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm /** 4984459516a2c116ddf80725d6a96a69186ccddc329Justin Klaassen * @return {@code true} if the currently displayed result is scrollable 49961568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm */ 5004459516a2c116ddf80725d6a96a69186ccddc329Justin Klaassen public boolean isScrollable() { 5014459516a2c116ddf80725d6a96a69186ccddc329Justin Klaassen return mScrollable; 50261568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm } 50361568a15c8d88d86aba14a7800d0bfb46f22c8baHans Boehm 5045e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm int getCurrentCharOffset() { 505013969e98ce9e3eb4f87ec6159b06a74d07b2592Hans Boehm synchronized(mWidthLock) { 506a0e45f306463394d9eeeb887b42ae18c72d69136Hans Boehm return (int) Math.round(mCurrentPos / mCharWidth); 507013969e98ce9e3eb4f87ec6159b06a74d07b2592Hans Boehm } 508013969e98ce9e3eb4f87ec6159b06a74d07b2592Hans Boehm } 509013969e98ce9e3eb4f87ec6159b06a74d07b2592Hans Boehm 51084614957604253d51296e06c97daced699a0a9deHans Boehm void clear() { 511760a9dc6573e35bcbf9097dece06cd90c4abb551Hans Boehm mValid = false; 5121176f23dae4d3740782e46463003e9f36a381c9dHans Boehm mScrollable = false; 51384614957604253d51296e06c97daced699a0a9deHans Boehm setText(""); 51484614957604253d51296e06c97daced699a0a9deHans Boehm } 51584614957604253d51296e06c97daced699a0a9deHans Boehm 51684614957604253d51296e06c97daced699a0a9deHans Boehm void redisplay() { 5175e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm int currentCharOffset = getCurrentCharOffset(); 51884614957604253d51296e06c97daced699a0a9deHans Boehm int maxChars = getMaxChars(); 5195e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm int lastDisplayedOffset[] = new int[1]; 5205e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm String result = getFormattedResult(currentCharOffset, maxChars, lastDisplayedOffset, false); 5210b9806f624f25e7e0302da4cf55eda21f8c28163Hans Boehm int expIndex = result.indexOf('E'); 522013969e98ce9e3eb4f87ec6159b06a74d07b2592Hans Boehm result = KeyMaps.translateResult(result); 5235e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm if (expIndex > 0 && result.indexOf('.') == -1) { 52484614957604253d51296e06c97daced699a0a9deHans Boehm // Gray out exponent if used as position indicator 52584614957604253d51296e06c97daced699a0a9deHans Boehm SpannableString formattedResult = new SpannableString(result); 5265e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm formattedResult.setSpan(mExponentColorSpan, expIndex, result.length(), 52784614957604253d51296e06c97daced699a0a9deHans Boehm Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 52884614957604253d51296e06c97daced699a0a9deHans Boehm setText(formattedResult); 52984614957604253d51296e06c97daced699a0a9deHans Boehm } else { 53084614957604253d51296e06c97daced699a0a9deHans Boehm setText(result); 53184614957604253d51296e06c97daced699a0a9deHans Boehm } 5325e802f30a0f18df4e2ecbf030e4aebb4ee70e8e8Hans Boehm mLastDisplayedOffset = lastDisplayedOffset[0]; 533760a9dc6573e35bcbf9097dece06cd90c4abb551Hans Boehm mValid = true; 53484614957604253d51296e06c97daced699a0a9deHans Boehm } 53584614957604253d51296e06c97daced699a0a9deHans Boehm 53684614957604253d51296e06c97daced699a0a9deHans Boehm @Override 53784614957604253d51296e06c97daced699a0a9deHans Boehm public void computeScroll() { 53884614957604253d51296e06c97daced699a0a9deHans Boehm if (!mScrollable) return; 53984614957604253d51296e06c97daced699a0a9deHans Boehm if (mScroller.computeScrollOffset()) { 54084614957604253d51296e06c97daced699a0a9deHans Boehm mCurrentPos = mScroller.getCurrX(); 54184614957604253d51296e06c97daced699a0a9deHans Boehm if (mCurrentPos != mLastPos) { 54284614957604253d51296e06c97daced699a0a9deHans Boehm mLastPos = mCurrentPos; 54384614957604253d51296e06c97daced699a0a9deHans Boehm redisplay(); 54484614957604253d51296e06c97daced699a0a9deHans Boehm } 54584614957604253d51296e06c97daced699a0a9deHans Boehm if (!mScroller.isFinished()) { 5464459516a2c116ddf80725d6a96a69186ccddc329Justin Klaassen postInvalidateOnAnimation(); 54784614957604253d51296e06c97daced699a0a9deHans Boehm } 54884614957604253d51296e06c97daced699a0a9deHans Boehm } 54984614957604253d51296e06c97daced699a0a9deHans Boehm } 55084614957604253d51296e06c97daced699a0a9deHans Boehm 5514a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm // Copy support: 5524a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm 5537f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm private ActionMode.Callback2 mCopyActionModeCallback = new ActionMode.Callback2() { 5547f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm 5557f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm private BackgroundColorSpan mHighlightSpan; 5567f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm 5577f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm private void highlightResult() { 5587f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm final Spannable text = (Spannable) getText(); 5597f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm mHighlightSpan = new BackgroundColorSpan(getHighlightColor()); 5607f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm text.setSpan(mHighlightSpan, 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 5617f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm } 5627f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm 5637f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm private void unhighlightResult() { 5647f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm final Spannable text = (Spannable) getText(); 5657f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm text.removeSpan(mHighlightSpan); 5667f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm } 5677f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm 5684a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm @Override 5694a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm public boolean onCreateActionMode(ActionMode mode, Menu menu) { 5704a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm MenuInflater inflater = mode.getMenuInflater(); 5714a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm inflater.inflate(R.menu.copy, menu); 5727f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm highlightResult(); 5734a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm return true; 5744a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm } 5754a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm 5764a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm @Override 5774a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 5784a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm return false; // Return false if nothing is done 5794a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm } 5804a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm 5814a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm @Override 5824a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 5834a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm switch (item.getItemId()) { 5844a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm case R.id.menu_copy: 5854a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm copyContent(); 5864a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm mode.finish(); 5874a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm return true; 5884a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm default: 5894a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm return false; 5904a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm } 5914a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm } 5924a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm 5934a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm @Override 5944a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm public void onDestroyActionMode(ActionMode mode) { 5957f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm unhighlightResult(); 5961176f23dae4d3740782e46463003e9f36a381c9dHans Boehm mActionMode = null; 5974a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm } 5987f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm 5997f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm @Override 6007f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm public void onGetContentRect(ActionMode mode, View view, Rect outRect) { 6017f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm super.onGetContentRect(mode, view, outRect); 6027f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm outRect.left += getPaddingLeft(); 6037f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm outRect.top += getPaddingTop(); 6047f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm outRect.right -= getPaddingRight(); 6057f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm outRect.bottom -= getPaddingBottom(); 6067f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm final int width = (int) Layout.getDesiredWidth(getText(), getPaint()); 6077f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm if (width < outRect.width()) { 6087f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm outRect.left = outRect.right - width; 6097f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm } 6107f83e36b7a1cb358b8dd44da842a3897b65bfddeHans Boehm } 6114a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm }; 6124a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm 6131176f23dae4d3740782e46463003e9f36a381c9dHans Boehm public boolean stopActionMode() { 6141176f23dae4d3740782e46463003e9f36a381c9dHans Boehm if (mActionMode != null) { 6151176f23dae4d3740782e46463003e9f36a381c9dHans Boehm mActionMode.finish(); 6161176f23dae4d3740782e46463003e9f36a381c9dHans Boehm return true; 6171176f23dae4d3740782e46463003e9f36a381c9dHans Boehm } 6181176f23dae4d3740782e46463003e9f36a381c9dHans Boehm return false; 6191176f23dae4d3740782e46463003e9f36a381c9dHans Boehm } 6201176f23dae4d3740782e46463003e9f36a381c9dHans Boehm 6214a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm private void setPrimaryClip(ClipData clip) { 6224a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm ClipboardManager clipboard = (ClipboardManager) getContext(). 623c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm getSystemService(Context.CLIPBOARD_SERVICE); 6244a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm clipboard.setPrimaryClip(clip); 6254a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm } 6264a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm 6274a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm private void copyContent() { 6284a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm final CharSequence text = getFullText(); 6294a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm ClipboardManager clipboard = 630c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); 631c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // We include a tag URI, to allow us to recognize our own results and handle them 632c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm // specially. 633c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm ClipData.Item newItem = new ClipData.Item(text, null, mEvaluator.capture()); 634c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm String[] mimeTypes = new String[] {ClipDescription.MIMETYPE_TEXT_PLAIN}; 635c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm ClipData cd = new ClipData("calculator result", mimeTypes, newItem); 6364a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm clipboard.setPrimaryClip(cd); 637c01cd7f21c3f74d83ba0f9284deb930a298efbd6Hans Boehm Toast.makeText(getContext(), R.string.text_copied_toast, Toast.LENGTH_SHORT).show(); 6384a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm } 6394a6b7cb235c305761af5d7f40e74d4704e5058c8Hans Boehm 64084614957604253d51296e06c97daced699a0a9deHans Boehm} 641