blob: d14135c77502e5de5bda4476f242ebc0d624b1c3 [file] [log] [blame]
Hans Boehm84614952014-11-25 18:46:17 -08001/*
Hans Boehm4a6b7cb2015-04-03 18:41:52 -07002 * Copyright (C) 2015 The Android Open Source Project
Hans Boehm84614952014-11-25 18:46:17 -08003 *
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
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070019import android.content.ClipboardManager;
20import android.content.ClipData;
21import android.content.ClipDescription;
22import android.content.Context;
Hans Boehm84614952014-11-25 18:46:17 -080023import android.graphics.Typeface;
24import android.graphics.Paint;
25import android.graphics.Rect;
26import android.graphics.Color;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070027import android.net.Uri;
28import android.widget.TextView;
Hans Boehm84614952014-11-25 18:46:17 -080029import android.widget.OverScroller;
Hans Boehm84614952014-11-25 18:46:17 -080030import android.text.Editable;
Hans Boehm84614952014-11-25 18:46:17 -080031import android.text.SpannableString;
Hans Boehm1176f232015-05-11 16:26:03 -070032import android.text.Spanned;
Hans Boehm84614952014-11-25 18:46:17 -080033import android.text.style.ForegroundColorSpan;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070034import android.util.AttributeSet;
35import android.util.Log;
36import android.view.ActionMode;
37import android.view.GestureDetector;
38import android.view.Menu;
39import android.view.MenuInflater;
40import android.view.MenuItem;
41import android.view.MotionEvent;
42import android.view.View;
43import android.widget.Toast;
Hans Boehm84614952014-11-25 18:46:17 -080044
45import android.support.v4.view.ViewCompat;
46
47
48// A text widget that is "infinitely" scrollable to the right,
49// and obtains the text to display via a callback to Logic.
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070050public class CalculatorResult extends TextView {
Hans Boehm08e8f322015-04-21 13:18:38 -070051 static final int MAX_RIGHT_SCROLL = 100000000;
52 static final int INVALID = MAX_RIGHT_SCROLL + 10000;
Hans Boehm84614952014-11-25 18:46:17 -080053 // A larger value is unlikely to avoid running out of space
54 final OverScroller mScroller;
55 final GestureDetector mGestureDetector;
56 class MyTouchListener implements View.OnTouchListener {
57 @Override
58 public boolean onTouch(View v, MotionEvent event) {
59 boolean res = mGestureDetector.onTouchEvent(event);
60 return res;
61 }
62 }
63 final MyTouchListener mTouchListener = new MyTouchListener();
64 private Evaluator mEvaluator;
65 private boolean mScrollable = false;
66 // A scrollable result is currently displayed.
Hans Boehm760a9dc2015-04-20 10:27:12 -070067 private boolean mValid = false;
Hans Boehmc01cd7f2015-05-12 18:32:19 -070068 // The result holds something valid; either a a number or an error
69 // message.
70 private int mCurrentPos;// Position of right of display relative to decimal point, in pixels.
71 // Large positive values mean the decimal point is scrolled off the
72 // left of the display. Zero means decimal point is barely displayed
73 // on the right.
Hans Boehm84614952014-11-25 18:46:17 -080074 private int mLastPos; // Position already reflected in display.
Hans Boehmc01cd7f2015-05-12 18:32:19 -070075 private int mMinPos; // Maximum position before all digits disappear of the right.
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070076 private Object mWidthLock = new Object();
77 // Protects the next two fields.
78 private int mWidthConstraint = -1;
79 // Our total width in pixels.
80 private int mCharWidth = 1;
Hans Boehmc01cd7f2015-05-12 18:32:19 -070081 // Maximum character width. For now we pretend that all characters
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070082 // have this width.
Hans Boehmc01cd7f2015-05-12 18:32:19 -070083 // TODO: We're not really using a fixed width font. But it appears
84 // to be close enough for the characters we use that the difference
85 // is not noticeable.
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070086 private static final int MAX_WIDTH = 100;
87 // Maximum number of digits displayed
Hans Boehm1176f232015-05-11 16:26:03 -070088 private ActionMode mActionMode;
89 private final ForegroundColorSpan mExponentColorSpan;
Hans Boehm84614952014-11-25 18:46:17 -080090
91 public CalculatorResult(Context context, AttributeSet attrs) {
92 super(context, attrs);
93 mScroller = new OverScroller(context);
94 mGestureDetector = new GestureDetector(context,
95 new GestureDetector.SimpleOnGestureListener() {
96 @Override
Justin Klaassend48b7562015-04-16 16:51:38 -070097 public boolean onDown(MotionEvent e) {
98 return true;
99 }
100 @Override
Hans Boehm84614952014-11-25 18:46:17 -0800101 public boolean onFling(MotionEvent e1, MotionEvent e2,
102 float velocityX, float velocityY) {
103 if (!mScroller.isFinished()) {
104 mCurrentPos = mScroller.getFinalX();
105 }
106 mScroller.forceFinished(true);
Hans Boehm1176f232015-05-11 16:26:03 -0700107 stopActionMode();
Hans Boehmfbcef702015-04-27 18:07:47 -0700108 CalculatorResult.this.cancelLongPress();
109 // Ignore scrolls of error string, etc.
110 if (!mScrollable) return true;
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700111 mScroller.fling(mCurrentPos, 0, - (int) velocityX, 0 /* horizontal only */,
112 mMinPos, MAX_RIGHT_SCROLL, 0, 0);
Hans Boehm84614952014-11-25 18:46:17 -0800113 ViewCompat.postInvalidateOnAnimation(CalculatorResult.this);
114 return true;
115 }
116 @Override
117 public boolean onScroll(MotionEvent e1, MotionEvent e2,
118 float distanceX, float distanceY) {
119 // TODO: Should we be dealing with any edge effects here?
120 if (!mScroller.isFinished()) {
121 mCurrentPos = mScroller.getFinalX();
122 }
123 mScroller.forceFinished(true);
Hans Boehm1176f232015-05-11 16:26:03 -0700124 stopActionMode();
Hans Boehm84614952014-11-25 18:46:17 -0800125 CalculatorResult.this.cancelLongPress();
126 if (!mScrollable) return true;
127 int duration = (int)(e2.getEventTime() - e1.getEventTime());
128 if (duration < 1 || duration > 100) duration = 10;
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700129 mScroller.startScroll(mCurrentPos, 0, (int)distanceX, 0, (int)duration);
Hans Boehm84614952014-11-25 18:46:17 -0800130 ViewCompat.postInvalidateOnAnimation(CalculatorResult.this);
131 return true;
132 }
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700133 @Override
134 public void onLongPress(MotionEvent e) {
Hans Boehm1176f232015-05-11 16:26:03 -0700135 if (mValid) {
136 mActionMode = startActionMode(mCopyActionModeCallback,
137 ActionMode.TYPE_FLOATING);
138 }
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700139 }
Hans Boehm84614952014-11-25 18:46:17 -0800140 });
141 setOnTouchListener(mTouchListener);
142 setHorizontallyScrolling(false); // do it ourselves
143 setCursorVisible(false);
Hans Boehm1176f232015-05-11 16:26:03 -0700144 mExponentColorSpan = new ForegroundColorSpan(
145 context.getColor(R.color.display_result_exponent_text_color));
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700146
147 // Copy ActionMode is triggered explicitly, not through
148 // setCustomSelectionActionModeCallback.
Hans Boehm84614952014-11-25 18:46:17 -0800149 }
150
151 void setEvaluator(Evaluator evaluator) {
152 mEvaluator = evaluator;
153 }
154
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700155 @Override
156 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
157 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
158
Hans Boehm013969e2015-04-13 20:29:47 -0700159 char testChar = KeyMaps.translateResult("5").charAt(0);
160 // TODO: Redo on Locale change? Doesn't seem to matter?
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700161 // We try to determine the maximal size of a digit plus corresponding inter-character
162 // space. We assume that "5" has maximal width. Since any string includes one fewer
163 // inter-character space than characters, me measure one that's longer than any real
164 // display string, and then divide by the number of characters. This should bound
165 // the per-character space we need for any real string.
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700166 StringBuilder sb = new StringBuilder(MAX_WIDTH);
167 for (int i = 0; i < MAX_WIDTH; ++i) {
Hans Boehm013969e2015-04-13 20:29:47 -0700168 sb.append(testChar);
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700169 }
Hans Boehm013969e2015-04-13 20:29:47 -0700170 final int newWidthConstraint =
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700171 MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
Hans Boehm013969e2015-04-13 20:29:47 -0700172 final int newCharWidth =
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700173 (int)Math.ceil(getPaint().measureText(sb.toString()) / MAX_WIDTH);
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700174 synchronized(mWidthLock) {
Hans Boehm013969e2015-04-13 20:29:47 -0700175 mWidthConstraint = newWidthConstraint;
176 mCharWidth = newCharWidth;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700177 }
178 }
179
Hans Boehm84614952014-11-25 18:46:17 -0800180 // Display a new result, given initial displayed
181 // precision and the string representing the whole part of
182 // the number to be displayed.
183 // We pass the string, instead of just the length, so we have
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700184 // one less place to fix in case we ever decide to
185 // correctly use a variable width font.
Hans Boehm84614952014-11-25 18:46:17 -0800186 void displayResult(int initPrec, String truncatedWholePart) {
187 mLastPos = INVALID;
Hans Boehm013969e2015-04-13 20:29:47 -0700188 synchronized(mWidthLock) {
189 mCurrentPos = initPrec * mCharWidth;
190 }
Hans Boehmc5e6e152015-04-22 12:06:29 -0700191 mMinPos = - (int) Math.ceil(getPaint().measureText(truncatedWholePart));
Hans Boehm84614952014-11-25 18:46:17 -0800192 redisplay();
193 }
194
Hans Boehm84614952014-11-25 18:46:17 -0800195 void displayError(int resourceId) {
Hans Boehm760a9dc2015-04-20 10:27:12 -0700196 mValid = true;
Hans Boehm84614952014-11-25 18:46:17 -0800197 mScrollable = false;
198 setText(resourceId);
199 }
200
Hans Boehm013969e2015-04-13 20:29:47 -0700201 private final int MAX_COPY_SIZE = 1000000;
202
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700203 // Format a result returned by Evaluator.getString() into a single line containing ellipses
204 // (if appropriate) and an exponent (if appropriate). digs is the value that was passed to
205 // getString and thus identifies the significance of the rightmost digit.
Hans Boehm08e8f322015-04-21 13:18:38 -0700206 // We add two distinct kinds of exponents:
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700207 // 1) If the final result contains the leading digit we use standard scientific notation.
208 // 2) If not, we add an exponent corresponding to an interpretation of the final result as
209 // an integer.
Hans Boehm08e8f322015-04-21 13:18:38 -0700210 // We add an ellipsis on the left if the result was truncated.
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700211 // We add ellipses and exponents in a way that leaves most digits in the position they
212 // would have been in had we not done so.
213 // This minimizes jumps as a result of scrolling. Result is NOT internationalized,
214 // uses "e" for exponent.
215 // last_included[0] is set to the position of the last digit we actually include;
216 // thus caller can tell whether result is exact.
Hans Boehm08e8f322015-04-21 13:18:38 -0700217 public String formatResult(String res, int digs,
218 int maxDigs, boolean truncated,
219 boolean negative) {
220 if (truncated) {
221 res = KeyMaps.ELLIPSIS + res.substring(1, res.length());
222 }
223 int decIndex = res.indexOf('.');
224 int resLen = res.length();
225 if (decIndex == -1 && digs != -1) {
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700226 // No decimal point displayed, and it's not just to the right of the last digit.
227 // Add an exponent to let the user track which digits are currently displayed.
228 // This is a bit tricky, since the number of displayed digits affects the displayed
229 // exponent, which can affect the room we have for mantissa digits. We occasionally
230 // display one digit too few. This is sometimes unavoidable, but we could
Hans Boehm08e8f322015-04-21 13:18:38 -0700231 // avoid it in more cases.
232 int exp = digs > 0 ? -digs : -digs - 1;
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700233 // Can be used as TYPE (2) EXPONENT. -1 accounts for decimal point.
234 int msd; // Position of most significant digit in res or indication its outside res.
Hans Boehm08e8f322015-04-21 13:18:38 -0700235 boolean hasPoint = false;
236 if (truncated) {
237 msd = -1;
238 } else {
239 msd = Evaluator.getMsdPos(res); // INVALID_MSD is OK
240 }
241 if (msd < maxDigs - 1 && msd >= 0) {
242 // TYPE (1) EXPONENT computation and transformation:
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700243 // Leading digit is in display window. Use standard calculator scientific notation
244 // with one digit to the left of the decimal point. Insert decimal point and
245 // delete leading zeroes.
246 String fraction = res.substring(msd + 1, resLen);
247 res = (negative ? "-" : "") + res.substring(msd, msd+1) + "." + fraction;
Hans Boehm08e8f322015-04-21 13:18:38 -0700248 exp += resLen - msd - 1;
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700249 // Original exp was correct for decimal point at right of fraction.
250 // Adjust by length of fraction.
Hans Boehm08e8f322015-04-21 13:18:38 -0700251 resLen = res.length();
252 hasPoint = true;
253 }
254 if (exp != 0 || truncated) {
255 // Actually add the exponent of either type:
256 String expAsString = Integer.toString(exp);
257 int expDigits = expAsString.length();
258 int dropDigits = expDigits + 1;
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700259 // Drop digits even if there is room. Otherwise the scrolling gets jumpy.
Hans Boehm08e8f322015-04-21 13:18:38 -0700260 if (dropDigits >= resLen - 1) {
261 dropDigits = Math.max(resLen - 2, 0);
262 // Jumpy is better than no mantissa.
263 }
264 if (!hasPoint) {
265 // Special handling for TYPE(2) EXPONENT:
266 exp += dropDigits;
267 expAsString = Integer.toString(exp);
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700268 // Adjust for digits we are about to drop to drop to make room for exponent.
269 // This can affect the room we have for the mantissa. We adjust only for
270 // positive exponents, when it could otherwise result in a truncated
Hans Boehm08e8f322015-04-21 13:18:38 -0700271 // displayed result.
272 if (exp > 0 && expAsString.length() > expDigits) {
273 // ++expDigits; (dead code)
274 ++dropDigits;
275 ++exp;
276 // This cannot increase the length a second time.
277 }
278 }
279 res = res.substring(0, resLen - dropDigits);
280 res = res + "e" + expAsString;
281 } // else don't add zero exponent
282 }
283 return res;
284 }
285
286 // Get formatted, but not internationalized, result from
287 // mEvaluator.
288 private String getFormattedResult(int pos, int maxSize) {
289 final boolean truncated[] = new boolean[1];
290 final boolean negative[] = new boolean[1];
291 final int requested_prec[] = {pos};
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700292 final String raw_res = mEvaluator.getString(requested_prec, maxSize, truncated, negative);
293 return formatResult(raw_res, requested_prec[0], maxSize, truncated[0], negative[0]);
Hans Boehm08e8f322015-04-21 13:18:38 -0700294 }
295
Hans Boehm84614952014-11-25 18:46:17 -0800296 // Return entire result (within reason) up to current displayed precision.
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700297 public String getFullText() {
Hans Boehm760a9dc2015-04-20 10:27:12 -0700298 if (!mValid) return "";
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700299 if (!mScrollable) return getText().toString();
Hans Boehm013969e2015-04-13 20:29:47 -0700300 int currentCharPos = getCurrentCharPos();
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700301 return KeyMaps.translateResult(getFormattedResult(currentCharPos, MAX_COPY_SIZE));
Hans Boehm84614952014-11-25 18:46:17 -0800302 }
303
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700304 public boolean fullTextIsExact() {
305 BoundedRational rat = mEvaluator.getRational();
Hans Boehm013969e2015-04-13 20:29:47 -0700306 int currentCharPos = getCurrentCharPos();
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700307 if (currentCharPos == -1) {
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700308 // Suppressing decimal point; still showing all integral digits.
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700309 currentCharPos = 0;
310 }
311 // TODO: Could handle scientific notation cases better;
312 // We currently treat those conservatively as approximate.
313 return (currentCharPos >= BoundedRational.digitsRequired(rat));
314 }
315
316 // May be called asynchronously from non-UI thread.
Hans Boehm84614952014-11-25 18:46:17 -0800317 int getMaxChars() {
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700318 // We only use 2/3 of the available space, since the left 1/3 of the result is not
319 // visible when it is shown in large size.
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700320 int result;
321 synchronized(mWidthLock) {
322 result = 2 * mWidthConstraint / (3 * mCharWidth);
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700323 // We can apparently finish evaluating before onMeasure in CalculatorText has been
324 // called, in which case we get 0 or -1 as the width constraint.
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700325 }
Hans Boehm84614952014-11-25 18:46:17 -0800326 if (result <= 0) {
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700327 // Return something conservatively big, to force sufficient evaluation.
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700328 return MAX_WIDTH;
Hans Boehm84614952014-11-25 18:46:17 -0800329 } else {
330 return result;
331 }
332 }
333
Hans Boehm013969e2015-04-13 20:29:47 -0700334 int getCurrentCharPos() {
335 synchronized(mWidthLock) {
336 return mCurrentPos/mCharWidth;
337 }
338 }
339
Hans Boehm84614952014-11-25 18:46:17 -0800340 void clear() {
Hans Boehm760a9dc2015-04-20 10:27:12 -0700341 mValid = false;
Hans Boehm1176f232015-05-11 16:26:03 -0700342 mScrollable = false;
Hans Boehm84614952014-11-25 18:46:17 -0800343 setText("");
344 }
345
346 void redisplay() {
Hans Boehm013969e2015-04-13 20:29:47 -0700347 int currentCharPos = getCurrentCharPos();
Hans Boehm84614952014-11-25 18:46:17 -0800348 int maxChars = getMaxChars();
Hans Boehm08e8f322015-04-21 13:18:38 -0700349 String result = getFormattedResult(currentCharPos, maxChars);
Hans Boehm84614952014-11-25 18:46:17 -0800350 int epos = result.indexOf('e');
Hans Boehm013969e2015-04-13 20:29:47 -0700351 result = KeyMaps.translateResult(result);
Hans Boehm84614952014-11-25 18:46:17 -0800352 if (epos > 0 && result.indexOf('.') == -1) {
353 // Gray out exponent if used as position indicator
354 SpannableString formattedResult = new SpannableString(result);
Hans Boehm1176f232015-05-11 16:26:03 -0700355 formattedResult.setSpan(mExponentColorSpan, epos, result.length(),
Hans Boehm84614952014-11-25 18:46:17 -0800356 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
357 setText(formattedResult);
358 } else {
359 setText(result);
360 }
Hans Boehm760a9dc2015-04-20 10:27:12 -0700361 mValid = true;
Hans Boehm84614952014-11-25 18:46:17 -0800362 mScrollable = true;
363 }
364
365 @Override
366 public void computeScroll() {
367 if (!mScrollable) return;
368 if (mScroller.computeScrollOffset()) {
369 mCurrentPos = mScroller.getCurrX();
370 if (mCurrentPos != mLastPos) {
371 mLastPos = mCurrentPos;
372 redisplay();
373 }
374 if (!mScroller.isFinished()) {
375 ViewCompat.postInvalidateOnAnimation(this);
376 }
377 }
378 }
379
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700380 // Copy support:
381
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700382 private ActionMode.Callback mCopyActionModeCallback = new ActionMode.Callback() {
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700383 @Override
384 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
385 MenuInflater inflater = mode.getMenuInflater();
386 inflater.inflate(R.menu.copy, menu);
387 return true;
388 }
389
390 @Override
391 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
392 return false; // Return false if nothing is done
393 }
394
395 @Override
396 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
397 switch (item.getItemId()) {
398 case R.id.menu_copy:
399 copyContent();
400 mode.finish();
401 return true;
402 default:
403 return false;
404 }
405 }
406
407 @Override
408 public void onDestroyActionMode(ActionMode mode) {
Hans Boehm1176f232015-05-11 16:26:03 -0700409 mActionMode = null;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700410 }
411 };
412
Hans Boehm1176f232015-05-11 16:26:03 -0700413 public boolean stopActionMode() {
414 if (mActionMode != null) {
415 mActionMode.finish();
416 return true;
417 }
418 return false;
419 }
420
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700421 private void setPrimaryClip(ClipData clip) {
422 ClipboardManager clipboard = (ClipboardManager) getContext().
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700423 getSystemService(Context.CLIPBOARD_SERVICE);
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700424 clipboard.setPrimaryClip(clip);
425 }
426
427 private void copyContent() {
428 final CharSequence text = getFullText();
429 ClipboardManager clipboard =
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700430 (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
431 // We include a tag URI, to allow us to recognize our own results and handle them
432 // specially.
433 ClipData.Item newItem = new ClipData.Item(text, null, mEvaluator.capture());
434 String[] mimeTypes = new String[] {ClipDescription.MIMETYPE_TEXT_PLAIN};
435 ClipData cd = new ClipData("calculator result", mimeTypes, newItem);
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700436 clipboard.setPrimaryClip(cd);
Hans Boehmc01cd7f2015-05-12 18:32:19 -0700437 Toast.makeText(getContext(), R.string.text_copied_toast, Toast.LENGTH_SHORT).show();
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700438 }
439
Hans Boehm84614952014-11-25 18:46:17 -0800440}