Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5e6a0ca2 authored by Hans Boehm's avatar Hans Boehm
Browse files

Cleanup of timeout handling and message

Bug: 21470513

Prevent timeout message from disappearing on rotation.
Have long timeout setting survive rotation.  Following Justin's
suggestion, associate it with a given expression and reset when
expression is cleared.

This does mean that when you rotate a device displaying an expensive-
-to-compute result, the device will initially display the formula for
several seconds, before it redisplays the result.  Previously you
had to reenable the long timeout.  Neither is 100% ideal.

Change-Id: Ibf8e151dd37ebadf1e86adee4718e8fa8f66b975
parent e57f3ef9
Loading
Loading
Loading
Loading
+47 −7
Original line number Diff line number Diff line
@@ -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 */);
    }
@@ -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);
        }
    }
}
+14 −3
Original line number Diff line number Diff line
@@ -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;
@@ -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.
@@ -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);
@@ -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() {
+70 −33
Original line number Diff line number Diff line
@@ -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.
@@ -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() {
@@ -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 {
@@ -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.
@@ -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);
@@ -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);
@@ -959,6 +991,9 @@ class Evaluator {
    public void delete() {
        mChangedValue = true;
        mExpr.delete();
        if (mExpr.isEmpty()) {
            mLongTimeout = false;
        }
    }

    void setDegreeMode(boolean degreeMode) {
@@ -993,7 +1028,7 @@ class Evaluator {
     */
    public void collapse() {
        final CalculatorExpr abbrvExpr = getResultExpr();
        clear();
        clearPreservingTimeout();
        mExpr.append(abbrvExpr);
        mChangedValue = true;
    }
@@ -1009,6 +1044,7 @@ class Evaluator {
        final CalculatorExpr abbrvExpr = getResultExpr();
        mSaved.clear();
        mSaved.append(abbrvExpr);
        mLongSavedTimeout = mLongTimeout;
        return true;
    }

@@ -1042,6 +1078,7 @@ class Evaluator {

    public void appendSaved() {
        mChangedValue = true;
        mLongTimeout |= mLongSavedTimeout;
        mExpr.append(mSaved);
    }