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

Commit b3513a13 authored by Jan Althaus's avatar Jan Althaus
Browse files

Fixing session abandon logging bug

Previously, making two selections quickly one after the other could lead
to the first session missing the terminal event, and the second one getting
terminated prematurely - getting marked incorrectly as abandoned.

Bug: 64914512
Test: Manually tested that logs are correct.
Change-Id: Icd75dcabe707b591f30629b9b9b42c5459ed7dda
parent 3f1ba67f
Loading
Loading
Loading
Loading
+40 −6
Original line number Diff line number Diff line
@@ -49,6 +49,8 @@ import java.util.regex.Pattern;
@UiThread
final class SelectionActionModeHelper {

    private static final String LOG_TAG = "SelectActionModeHelper";

    /**
     * Maximum time (in milliseconds) to wait for a result before timing out.
     */
@@ -216,6 +218,7 @@ final class SelectionActionModeHelper {
        private int mSelectionStart;
        private int mSelectionEnd;
        private boolean mAllowReset;
        private final LogAbandonRunnable mDelayedLogAbandon = new LogAbandonRunnable();

        SelectionTracker(TextView textView) {
            mTextView = Preconditions.checkNotNull(textView);
@@ -227,6 +230,10 @@ final class SelectionActionModeHelper {
         */
        public void onOriginalSelection(
                CharSequence text, int selectionStart, int selectionEnd, boolean editableText) {
            // If we abandoned a selection and created a new one very shortly after, we may still
            // have a pending request to log ABANDON, which we flush here.
            mDelayedLogAbandon.flush();

            mOriginalStart = mSelectionStart = selectionStart;
            mOriginalEnd = mSelectionEnd = selectionEnd;
            mAllowReset = false;
@@ -267,12 +274,7 @@ final class SelectionActionModeHelper {
        public void onSelectionDestroyed() {
            mAllowReset = false;
            // Wait a few ms to see if the selection was destroyed because of a text change event.
            mTextView.postDelayed(() -> {
                mLogger.logSelectionAction(
                        mSelectionStart, mSelectionEnd,
                        SelectionEvent.ActionType.ABANDON, null /* classification */);
                mSelectionStart = mSelectionEnd = -1;
            }, 100 /* ms */);
            mDelayedLogAbandon.schedule(100 /* ms */);
        }

        /**
@@ -329,6 +331,38 @@ final class SelectionActionModeHelper {
        private boolean isSelectionStarted() {
            return mSelectionStart >= 0 && mSelectionEnd >= 0 && mSelectionStart != mSelectionEnd;
        }

        /** A helper for keeping track of pending abandon logging requests. */
        private final class LogAbandonRunnable implements Runnable {
            private boolean mIsPending;

            /** Schedules an abandon to be logged with the given delay. Flush if necessary. */
            void schedule(int delayMillis) {
                if (mIsPending) {
                    Log.e(LOG_TAG, "Force flushing abandon due to new scheduling request");
                    flush();
                }
                mIsPending = true;
                mTextView.postDelayed(this, delayMillis);
            }

            /** If there is a pending log request, execute it now. */
            void flush() {
                mTextView.removeCallbacks(this);
                run();
            }

            @Override
            public void run() {
                if (mIsPending) {
                    mLogger.logSelectionAction(
                            mSelectionStart, mSelectionEnd,
                            SelectionEvent.ActionType.ABANDON, null /* classification */);
                    mSelectionStart = mSelectionEnd = -1;
                    mIsPending = false;
                }
            }
        }
    }

    // TODO: Write tests