diff options
| author | Jan Althaus <jalt@google.com> | 2017-09-22 18:26:06 +0200 |
|---|---|---|
| committer | Jan Althaus <jalt@google.com> | 2017-09-25 10:37:24 +0200 |
| commit | b3513a130d93b07387c05b29aff5a3c205a43f9f (patch) | |
| tree | 1108ec6c8474061ecee866a1ffa37429c38de679 /core/java/android/widget/SelectionActionModeHelper.java | |
| parent | 3f1ba67f1e3f5a2bbde662213e41d2437ba9a2ab (diff) | |
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
Diffstat (limited to 'core/java/android/widget/SelectionActionModeHelper.java')
| -rw-r--r-- | core/java/android/widget/SelectionActionModeHelper.java | 46 |
1 files changed, 40 insertions, 6 deletions
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index 4ebb3cfea266..fceefaf8a28c 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -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 |
