Loading src/com/android/calculator2/AlertDialogFragment.java +47 −7 Original line number Diff line number Diff line Loading @@ -21,22 +21,50 @@ import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.widget.TextView; public class AlertDialogFragment extends DialogFragment { /** * Display a message with a dismiss putton, and optionally a second button. */ public class AlertDialogFragment extends DialogFragment implements DialogInterface.OnClickListener { public interface OnClickListener { /** * This method will be invoked when a button in the dialog is clicked. * * @param fragment the AlertDialogFragment that received the click * @param which the button that was clicked (e.g. * {@link DialogInterface#BUTTON_POSITIVE}) or the position * of the item clicked */ public void onClick(AlertDialogFragment fragment, int which); } private static final String NAME = AlertDialogFragment.class.getName(); private static final String KEY_MESSAGE = NAME + "_message"; private static final String KEY_BUTTON_NEGATIVE = NAME + "_button_negative"; private static final String KEY_BUTTON_POSITIVE = NAME + "_button_positive"; public static void showMessageDialog(Activity activity, CharSequence message) { /** * Create and show a DialogFragment with the given message. * @param activity originating Activity * @param message displayed message * @param positiveButtonLabel label for second button, if any. If non-null, activity must * implement AlertDialogFragment.OnClickListener to respond. */ public static void showMessageDialog(Activity activity, CharSequence message, @Nullable CharSequence positiveButtonLabel) { final AlertDialogFragment dialogFragment = new AlertDialogFragment(); final Bundle args = new Bundle(); args.putCharSequence(KEY_MESSAGE, message); args.putCharSequence(KEY_BUTTON_NEGATIVE, activity.getString(R.string.dismiss)); final AlertDialogFragment dialogFragment = new AlertDialogFragment(); if (positiveButtonLabel != null) { args.putCharSequence(KEY_BUTTON_POSITIVE, positiveButtonLabel); } dialogFragment.setArguments(args); dialogFragment.show(activity.getFragmentManager(), null /* tag */); } Loading @@ -53,9 +81,21 @@ public class AlertDialogFragment extends DialogFragment { final TextView textView = (TextView) inflater.inflate(R.layout.dialog_message, null /* root */); textView.setText(args.getCharSequence(KEY_MESSAGE)); return new AlertDialog.Builder(context) final AlertDialog.Builder builder = new AlertDialog.Builder(context) .setView(textView) .setNegativeButton(args.getCharSequence(KEY_BUTTON_NEGATIVE), null /* listener */) .create(); .setNegativeButton(args.getCharSequence(KEY_BUTTON_NEGATIVE), null /* listener */); final CharSequence positiveButtonLabel = args.getCharSequence(KEY_BUTTON_POSITIVE); if (positiveButtonLabel != null) { builder.setPositiveButton(positiveButtonLabel, this); } return builder.create(); } @Override public void onClick(DialogInterface dialog, int which) { final Activity activity = getActivity(); if (activity instanceof AlertDialogFragment.OnClickListener /* always true */) { ((AlertDialogFragment.OnClickListener) activity).onClick(this, which); } } } src/com/android/calculator2/Calculator.java +14 −3 Original line number Diff line number Diff line Loading @@ -32,7 +32,9 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.app.Activity; import android.app.AlertDialog; import android.content.ClipData; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Resources; import android.graphics.Color; Loading Loading @@ -71,7 +73,8 @@ import java.io.ObjectOutput; import java.io.ObjectOutputStream; public class Calculator extends Activity implements OnTextSizeChangeListener, OnLongClickListener, CalculatorText.OnPasteListener { implements OnTextSizeChangeListener, OnLongClickListener, CalculatorText.OnPasteListener, AlertDialogFragment.OnClickListener { /** * Constant for an invalid resource id. Loading Loading @@ -849,6 +852,14 @@ public class Calculator extends Activity mFormulaText.requestFocus(); } @Override public void onClick(AlertDialogFragment fragment, int which) { if (which == DialogInterface.BUTTON_POSITIVE) { // Timeout extension request. mEvaluator.setLongTimeOut(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); Loading Loading @@ -889,7 +900,7 @@ public class Calculator extends Activity } private void displayMessage(String s) { AlertDialogFragment.showMessageDialog(this, s); AlertDialogFragment.showMessageDialog(this, s, null); } private void displayFraction() { Loading src/com/android/calculator2/Evaluator.java +70 −33 Original line number Diff line number Diff line Loading @@ -234,43 +234,65 @@ class Evaluator { .show(); } // Maximum timeout for background computations. Exceeding a few tens of seconds // increases the risk of running out of memory and impacting the rest of the system. private final long MAX_TIMEOUT = 15000; // Timeout handling. // Expressions are evaluated with a sort timeout or a long timeout. // Each implies different maxima on both computation time and bit length. // We recheck bit length separetly to avoid wasting time on decimal conversions that are // destined to fail. // Timeout for requested evaluations, in milliseconds. This is currently not saved and // restored with the state; we reset the timeout when the calculator is restarted. We'll call // that a feature; others might argue it's a bug. private long mTimeout = 2000; /** * Is a long timeout in effect for the main expression? */ private boolean mLongTimeout = false; /** * Is a long timeout in effect for the saved expression? */ private boolean mLongSavedTimeout = false; /** * Return the timeout in milliseconds. * @param longTimeout a long timeout is in effect */ private long getTimeout(boolean longTimeout) { return longTimeout ? 15000 : 2000; // Exceeding a few tens of seconds increases the risk of running out of memory // and impacting the rest of the system. } // Timeout for unrequested, speculative evaluations, in milliseconds. /** * Return the maximum number of bits in the result. Longer results are assumed to time out. * @param longTimeout a long timeout is in effect */ private int getMaxResultBits(boolean longTimeout) { return longTimeout ? 350000 : 120000; } /** * Timeout for unrequested, speculative evaluations, in milliseconds. */ private final long QUICK_TIMEOUT = 1000; private int mMaxResultBits = 120000; // Don't try to display a larger result. private final int MAX_MAX_RESULT_BITS = 350000; // Long timeout version. private final int QUICK_MAX_RESULT_BITS = 50000; // Instant result version. /** * Maximum result bit length for unrequested, speculative evaluations. */ private final int QUICK_MAX_RESULT_BITS = 50000; private void displayTimeoutMessage() { final AlertDialog.Builder builder = new AlertDialog.Builder(mCalculator) .setMessage(R.string.timeout) .setNegativeButton(R.string.dismiss, null /* listener */); if (mTimeout != MAX_TIMEOUT) { builder.setPositiveButton(R.string.ok_remove_timeout, new DialogInterface.OnClickListener() { public void onClick(DialogInterface d, int which) { mTimeout = MAX_TIMEOUT; mMaxResultBits = MAX_MAX_RESULT_BITS; AlertDialogFragment.showMessageDialog(mCalculator, mCalculator.getString(R.string.timeout), (mLongTimeout ? null : mCalculator.getString(R.string.ok_remove_timeout))); } }); } builder.show(); public void setLongTimeOut() { mLongTimeout = true; } // Compute initial cache contents and result when we're good and ready. // We leave the expression display up, with scrolling // disabled, until this computation completes. // Can result in an error display if something goes wrong. // By default we set a timeout to catch runaway computations. /** * Compute initial cache contents and result when we're good and ready. * We leave the expression display up, with scrolling disabled, until this computation * completes. Can result in an error display if something goes wrong. By default we set a * timeout to catch runaway computations. */ class AsyncEvaluator extends AsyncTask<Void, Void, InitialResult> { private boolean mDm; // degrees private boolean mRequired; // Result was requested by user. Loading Loading @@ -299,7 +321,7 @@ class Evaluator { } @Override protected void onPreExecute() { long timeout = mRequired ? mTimeout : QUICK_TIMEOUT; long timeout = mRequired ? getTimeout(mLongTimeout) : QUICK_TIMEOUT; mTimeoutRunnable = new Runnable() { @Override public void run() { Loading @@ -312,7 +334,7 @@ class Evaluator { * Is a computed result too big for decimal conversion? */ private boolean isTooBig(CalculatorExpr.EvalResult res) { int maxBits = mRequired ? mMaxResultBits : QUICK_MAX_RESULT_BITS; int maxBits = mRequired ? getMaxResultBits(mLongTimeout) : QUICK_MAX_RESULT_BITS; if (res.ratVal != null) { return res.ratVal.wholeNumberBits() > maxBits; } else { Loading Loading @@ -821,11 +843,17 @@ class Evaluator { mMsdIndex = INVALID_MSD; } public void clear() { private void clearPreservingTimeout() { mExpr.clear(); clearCache(); } public void clear() { clearPreservingTimeout(); mLongTimeout = false; } /** * Start asynchronous result evaluation of formula. * Will result in display on completion. Loading Loading @@ -916,6 +944,8 @@ class Evaluator { try { CalculatorExpr.initExprInput(); mDegreeMode = in.readBoolean(); mLongTimeout = in.readBoolean(); mLongSavedTimeout = in.readBoolean(); mExpr = new CalculatorExpr(in); mSavedName = in.readUTF(); mSaved = new CalculatorExpr(in); Loading @@ -931,6 +961,8 @@ class Evaluator { try { CalculatorExpr.initExprOutput(); out.writeBoolean(mDegreeMode); out.writeBoolean(mLongTimeout); out.writeBoolean(mLongSavedTimeout); mExpr.write(out); out.writeUTF(mSavedName); mSaved.write(out); Loading Loading @@ -959,6 +991,9 @@ class Evaluator { public void delete() { mChangedValue = true; mExpr.delete(); if (mExpr.isEmpty()) { mLongTimeout = false; } } void setDegreeMode(boolean degreeMode) { Loading Loading @@ -993,7 +1028,7 @@ class Evaluator { */ public void collapse() { final CalculatorExpr abbrvExpr = getResultExpr(); clear(); clearPreservingTimeout(); mExpr.append(abbrvExpr); mChangedValue = true; } Loading @@ -1009,6 +1044,7 @@ class Evaluator { final CalculatorExpr abbrvExpr = getResultExpr(); mSaved.clear(); mSaved.append(abbrvExpr); mLongSavedTimeout = mLongTimeout; return true; } Loading Loading @@ -1042,6 +1078,7 @@ class Evaluator { public void appendSaved() { mChangedValue = true; mLongTimeout |= mLongSavedTimeout; mExpr.append(mSaved); } Loading Loading
src/com/android/calculator2/AlertDialogFragment.java +47 −7 Original line number Diff line number Diff line Loading @@ -21,22 +21,50 @@ import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.widget.TextView; public class AlertDialogFragment extends DialogFragment { /** * Display a message with a dismiss putton, and optionally a second button. */ public class AlertDialogFragment extends DialogFragment implements DialogInterface.OnClickListener { public interface OnClickListener { /** * This method will be invoked when a button in the dialog is clicked. * * @param fragment the AlertDialogFragment that received the click * @param which the button that was clicked (e.g. * {@link DialogInterface#BUTTON_POSITIVE}) or the position * of the item clicked */ public void onClick(AlertDialogFragment fragment, int which); } private static final String NAME = AlertDialogFragment.class.getName(); private static final String KEY_MESSAGE = NAME + "_message"; private static final String KEY_BUTTON_NEGATIVE = NAME + "_button_negative"; private static final String KEY_BUTTON_POSITIVE = NAME + "_button_positive"; public static void showMessageDialog(Activity activity, CharSequence message) { /** * Create and show a DialogFragment with the given message. * @param activity originating Activity * @param message displayed message * @param positiveButtonLabel label for second button, if any. If non-null, activity must * implement AlertDialogFragment.OnClickListener to respond. */ public static void showMessageDialog(Activity activity, CharSequence message, @Nullable CharSequence positiveButtonLabel) { final AlertDialogFragment dialogFragment = new AlertDialogFragment(); final Bundle args = new Bundle(); args.putCharSequence(KEY_MESSAGE, message); args.putCharSequence(KEY_BUTTON_NEGATIVE, activity.getString(R.string.dismiss)); final AlertDialogFragment dialogFragment = new AlertDialogFragment(); if (positiveButtonLabel != null) { args.putCharSequence(KEY_BUTTON_POSITIVE, positiveButtonLabel); } dialogFragment.setArguments(args); dialogFragment.show(activity.getFragmentManager(), null /* tag */); } Loading @@ -53,9 +81,21 @@ public class AlertDialogFragment extends DialogFragment { final TextView textView = (TextView) inflater.inflate(R.layout.dialog_message, null /* root */); textView.setText(args.getCharSequence(KEY_MESSAGE)); return new AlertDialog.Builder(context) final AlertDialog.Builder builder = new AlertDialog.Builder(context) .setView(textView) .setNegativeButton(args.getCharSequence(KEY_BUTTON_NEGATIVE), null /* listener */) .create(); .setNegativeButton(args.getCharSequence(KEY_BUTTON_NEGATIVE), null /* listener */); final CharSequence positiveButtonLabel = args.getCharSequence(KEY_BUTTON_POSITIVE); if (positiveButtonLabel != null) { builder.setPositiveButton(positiveButtonLabel, this); } return builder.create(); } @Override public void onClick(DialogInterface dialog, int which) { final Activity activity = getActivity(); if (activity instanceof AlertDialogFragment.OnClickListener /* always true */) { ((AlertDialogFragment.OnClickListener) activity).onClick(this, which); } } }
src/com/android/calculator2/Calculator.java +14 −3 Original line number Diff line number Diff line Loading @@ -32,7 +32,9 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.app.Activity; import android.app.AlertDialog; import android.content.ClipData; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Resources; import android.graphics.Color; Loading Loading @@ -71,7 +73,8 @@ import java.io.ObjectOutput; import java.io.ObjectOutputStream; public class Calculator extends Activity implements OnTextSizeChangeListener, OnLongClickListener, CalculatorText.OnPasteListener { implements OnTextSizeChangeListener, OnLongClickListener, CalculatorText.OnPasteListener, AlertDialogFragment.OnClickListener { /** * Constant for an invalid resource id. Loading Loading @@ -849,6 +852,14 @@ public class Calculator extends Activity mFormulaText.requestFocus(); } @Override public void onClick(AlertDialogFragment fragment, int which) { if (which == DialogInterface.BUTTON_POSITIVE) { // Timeout extension request. mEvaluator.setLongTimeOut(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); Loading Loading @@ -889,7 +900,7 @@ public class Calculator extends Activity } private void displayMessage(String s) { AlertDialogFragment.showMessageDialog(this, s); AlertDialogFragment.showMessageDialog(this, s, null); } private void displayFraction() { Loading
src/com/android/calculator2/Evaluator.java +70 −33 Original line number Diff line number Diff line Loading @@ -234,43 +234,65 @@ class Evaluator { .show(); } // Maximum timeout for background computations. Exceeding a few tens of seconds // increases the risk of running out of memory and impacting the rest of the system. private final long MAX_TIMEOUT = 15000; // Timeout handling. // Expressions are evaluated with a sort timeout or a long timeout. // Each implies different maxima on both computation time and bit length. // We recheck bit length separetly to avoid wasting time on decimal conversions that are // destined to fail. // Timeout for requested evaluations, in milliseconds. This is currently not saved and // restored with the state; we reset the timeout when the calculator is restarted. We'll call // that a feature; others might argue it's a bug. private long mTimeout = 2000; /** * Is a long timeout in effect for the main expression? */ private boolean mLongTimeout = false; /** * Is a long timeout in effect for the saved expression? */ private boolean mLongSavedTimeout = false; /** * Return the timeout in milliseconds. * @param longTimeout a long timeout is in effect */ private long getTimeout(boolean longTimeout) { return longTimeout ? 15000 : 2000; // Exceeding a few tens of seconds increases the risk of running out of memory // and impacting the rest of the system. } // Timeout for unrequested, speculative evaluations, in milliseconds. /** * Return the maximum number of bits in the result. Longer results are assumed to time out. * @param longTimeout a long timeout is in effect */ private int getMaxResultBits(boolean longTimeout) { return longTimeout ? 350000 : 120000; } /** * Timeout for unrequested, speculative evaluations, in milliseconds. */ private final long QUICK_TIMEOUT = 1000; private int mMaxResultBits = 120000; // Don't try to display a larger result. private final int MAX_MAX_RESULT_BITS = 350000; // Long timeout version. private final int QUICK_MAX_RESULT_BITS = 50000; // Instant result version. /** * Maximum result bit length for unrequested, speculative evaluations. */ private final int QUICK_MAX_RESULT_BITS = 50000; private void displayTimeoutMessage() { final AlertDialog.Builder builder = new AlertDialog.Builder(mCalculator) .setMessage(R.string.timeout) .setNegativeButton(R.string.dismiss, null /* listener */); if (mTimeout != MAX_TIMEOUT) { builder.setPositiveButton(R.string.ok_remove_timeout, new DialogInterface.OnClickListener() { public void onClick(DialogInterface d, int which) { mTimeout = MAX_TIMEOUT; mMaxResultBits = MAX_MAX_RESULT_BITS; AlertDialogFragment.showMessageDialog(mCalculator, mCalculator.getString(R.string.timeout), (mLongTimeout ? null : mCalculator.getString(R.string.ok_remove_timeout))); } }); } builder.show(); public void setLongTimeOut() { mLongTimeout = true; } // Compute initial cache contents and result when we're good and ready. // We leave the expression display up, with scrolling // disabled, until this computation completes. // Can result in an error display if something goes wrong. // By default we set a timeout to catch runaway computations. /** * Compute initial cache contents and result when we're good and ready. * We leave the expression display up, with scrolling disabled, until this computation * completes. Can result in an error display if something goes wrong. By default we set a * timeout to catch runaway computations. */ class AsyncEvaluator extends AsyncTask<Void, Void, InitialResult> { private boolean mDm; // degrees private boolean mRequired; // Result was requested by user. Loading Loading @@ -299,7 +321,7 @@ class Evaluator { } @Override protected void onPreExecute() { long timeout = mRequired ? mTimeout : QUICK_TIMEOUT; long timeout = mRequired ? getTimeout(mLongTimeout) : QUICK_TIMEOUT; mTimeoutRunnable = new Runnable() { @Override public void run() { Loading @@ -312,7 +334,7 @@ class Evaluator { * Is a computed result too big for decimal conversion? */ private boolean isTooBig(CalculatorExpr.EvalResult res) { int maxBits = mRequired ? mMaxResultBits : QUICK_MAX_RESULT_BITS; int maxBits = mRequired ? getMaxResultBits(mLongTimeout) : QUICK_MAX_RESULT_BITS; if (res.ratVal != null) { return res.ratVal.wholeNumberBits() > maxBits; } else { Loading Loading @@ -821,11 +843,17 @@ class Evaluator { mMsdIndex = INVALID_MSD; } public void clear() { private void clearPreservingTimeout() { mExpr.clear(); clearCache(); } public void clear() { clearPreservingTimeout(); mLongTimeout = false; } /** * Start asynchronous result evaluation of formula. * Will result in display on completion. Loading Loading @@ -916,6 +944,8 @@ class Evaluator { try { CalculatorExpr.initExprInput(); mDegreeMode = in.readBoolean(); mLongTimeout = in.readBoolean(); mLongSavedTimeout = in.readBoolean(); mExpr = new CalculatorExpr(in); mSavedName = in.readUTF(); mSaved = new CalculatorExpr(in); Loading @@ -931,6 +961,8 @@ class Evaluator { try { CalculatorExpr.initExprOutput(); out.writeBoolean(mDegreeMode); out.writeBoolean(mLongTimeout); out.writeBoolean(mLongSavedTimeout); mExpr.write(out); out.writeUTF(mSavedName); mSaved.write(out); Loading Loading @@ -959,6 +991,9 @@ class Evaluator { public void delete() { mChangedValue = true; mExpr.delete(); if (mExpr.isEmpty()) { mLongTimeout = false; } } void setDegreeMode(boolean degreeMode) { Loading Loading @@ -993,7 +1028,7 @@ class Evaluator { */ public void collapse() { final CalculatorExpr abbrvExpr = getResultExpr(); clear(); clearPreservingTimeout(); mExpr.append(abbrvExpr); mChangedValue = true; } Loading @@ -1009,6 +1044,7 @@ class Evaluator { final CalculatorExpr abbrvExpr = getResultExpr(); mSaved.clear(); mSaved.append(abbrvExpr); mLongSavedTimeout = mLongTimeout; return true; } Loading Loading @@ -1042,6 +1078,7 @@ class Evaluator { public void appendSaved() { mChangedValue = true; mLongTimeout |= mLongSavedTimeout; mExpr.append(mSaved); } Loading