summaryrefslogtreecommitdiff
path: root/core/java/android/widget/SelectionActionModeHelper.java
diff options
context:
space:
mode:
authorJan Althaus <jalt@google.com>2017-09-22 18:26:06 +0200
committerJan Althaus <jalt@google.com>2017-09-25 10:37:24 +0200
commitb3513a130d93b07387c05b29aff5a3c205a43f9f (patch)
tree1108ec6c8474061ecee866a1ffa37429c38de679 /core/java/android/widget/SelectionActionModeHelper.java
parent3f1ba67f1e3f5a2bbde662213e41d2437ba9a2ab (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.java46
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