Commit fbcbd3bc authored by Tony Mak's avatar Tony Mak Committed by Commit Bot

Call onSelectionEvent to log instead of using reflection

1. Removed reflection and instead use SelectionEvent introduced in P
   That means we won't have logging from O device anymore.
   However, P-R logs should be enough for us to conduct A/B
   experiments.
2. By calling onSelectionEvent, events would now be routed to a
   TextClassifierService if exists. Previously, TCS won't get the
   event.
3. This will help migrate to the new android metrics logger.

Bug: b/120032995

Change-Id: I4d39600cb0d029f2b311d66fb97ec6bb7a1c7a2b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1868882Reviewed-by: default avatarShimi Zhang <ctzsm@chromium.org>
Reviewed-by: default avatarBo <boliu@chromium.org>
Commit-Queue: Tony Mak <tonymak@google.com>
Auto-Submit: Tony Mak <tonymak@google.com>
Cr-Commit-Position: refs/heads/master@{#708964}
parent f501d023
......@@ -229,7 +229,6 @@ android_library("content_java") {
"java/src/org/chromium/content/browser/selection/MagnifierWrapper.java",
"java/src/org/chromium/content/browser/selection/MagnifierWrapperImpl.java",
"java/src/org/chromium/content/browser/selection/PastePopupMenu.java",
"java/src/org/chromium/content/browser/selection/SelectionEventProxyImpl.java",
"java/src/org/chromium/content/browser/selection/SelectionIndicesConverter.java",
"java/src/org/chromium/content/browser/selection/SelectionInsertionHandleObserver.java",
"java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java",
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.content.browser.selection;
import android.annotation.TargetApi;
import android.os.Build;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextSelection;
import androidx.annotation.NonNull;
import org.chromium.base.Log;
import java.lang.reflect.Method;
/**
* A wrapper class for Android SmartSelectionEventTracker.SelectionEvent.
* SmartSelectionEventTracker.SelectionEvent is a hidden class, need reflection to access to it.
*/
@TargetApi(Build.VERSION_CODES.O)
public class SelectionEventProxyImpl implements SmartSelectionMetricsLogger.SelectionEventProxy {
private static final String TAG = "SmartSelectionLogger";
private static final boolean DEBUG = false;
private static final String SELECTION_EVENT_CLASS =
"android.view.textclassifier.logging.SmartSelectionEventTracker$SelectionEvent";
// Reflection class and methods.
private static Class<?> sSelectionEventClass;
private static Method sSelectionStartedMethod;
private static Method sSelectionModifiedMethod;
private static Method sSelectionModifiedClassificationMethod;
private static Method sSelectionModifiedSelectionMethod;
private static Method sSelectionActionMethod;
private static Method sSelectionActionClassificationMethod;
private static boolean sReflectionFailed;
public static SelectionEventProxyImpl create() {
if (sReflectionFailed) return null;
// TODO(ctzsm): Remove reflection after SDK updates.
if (sSelectionEventClass == null) {
try {
sSelectionEventClass = Class.forName(SELECTION_EVENT_CLASS);
sSelectionStartedMethod =
sSelectionEventClass.getMethod("selectionStarted", Integer.TYPE);
sSelectionModifiedMethod = sSelectionEventClass.getMethod(
"selectionModified", Integer.TYPE, Integer.TYPE);
sSelectionModifiedClassificationMethod = sSelectionEventClass.getMethod(
"selectionModified", Integer.TYPE, Integer.TYPE, TextClassification.class);
sSelectionModifiedSelectionMethod = sSelectionEventClass.getMethod(
"selectionModified", Integer.TYPE, Integer.TYPE, TextSelection.class);
sSelectionActionMethod = sSelectionEventClass.getMethod(
"selectionAction", Integer.TYPE, Integer.TYPE, Integer.TYPE);
sSelectionActionClassificationMethod =
sSelectionEventClass.getMethod("selectionAction", Integer.TYPE,
Integer.TYPE, Integer.TYPE, TextClassification.class);
} catch (ClassNotFoundException | NoSuchMethodException e) {
if (DEBUG) Log.d(TAG, "Reflection failure", e);
sReflectionFailed = true;
return null;
}
}
return new SelectionEventProxyImpl();
}
private SelectionEventProxyImpl() {}
// Reflection wrapper of SelectionEvent#selectionStarted(int)
@Override
public Object createSelectionStarted(int start) {
try {
return sSelectionStartedMethod.invoke(null, start);
} catch (ReflectiveOperationException e) {
// Avoid crashes due to logging.
if (DEBUG) Log.d(TAG, "Reflection failure", e);
}
return null;
}
// Reflection wrapper of SelectionEvent#selectionModified(int, int)
@Override
public Object createSelectionModified(int start, int end) {
try {
return sSelectionModifiedMethod.invoke(null, start, end);
} catch (ReflectiveOperationException e) {
// Avoid crashes due to logging.
if (DEBUG) Log.d(TAG, "Reflection failure", e);
}
return null;
}
// Reflection wrapper of SelectionEvent#selectionModified(int, int, TextClassification)
@Override
public Object createSelectionModifiedClassification(
int start, int end, @NonNull Object classification) {
try {
return sSelectionModifiedClassificationMethod.invoke(
null, start, end, (TextClassification) classification);
} catch (ReflectiveOperationException e) {
// Avoid crashes due to logging.
if (DEBUG) Log.d(TAG, "Reflection failure", e);
}
return null;
}
// Reflection wrapper of SelectionEvent#selectionModified(int, int, TextSelection)
@Override
public Object createSelectionModifiedSelection(int start, int end, @NonNull Object selection) {
try {
return sSelectionModifiedSelectionMethod.invoke(
null, start, end, (TextSelection) selection);
} catch (ReflectiveOperationException e) {
// Avoid crashes due to logging.
if (DEBUG) Log.d(TAG, "Reflection failure", e);
}
return null;
}
// Reflection wrapper of SelectionEvent#selectionAction(int, int, int)
@Override
public Object createSelectionAction(int start, int end, int actionType) {
try {
return sSelectionActionMethod.invoke(null, start, end, actionType);
} catch (ReflectiveOperationException e) {
// Avoid crashes due to logging.
if (DEBUG) Log.d(TAG, "Reflection failure", e);
}
return null;
}
// Reflection wrapper of SelectionEvent#selectionAction(int, int, int, TextClassification)
@Override
public Object createSelectionAction(
int start, int end, int actionType, @NonNull Object classification) {
try {
return sSelectionActionClassificationMethod.invoke(
null, start, end, actionType, (TextClassification) classification);
} catch (ReflectiveOperationException e) {
// Avoid crashes due to logging.
if (DEBUG) Log.d(TAG, "Reflection failure", e);
}
return null;
}
}
......@@ -31,6 +31,7 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.textclassifier.SelectionEvent;
import android.view.textclassifier.TextClassifier;
import androidx.annotation.Nullable;
......@@ -374,7 +375,7 @@ public class SelectionPopupControllerImpl extends ActionModeCallbackHelper
break;
case MenuSourceType.MENU_SOURCE_ADJUST_SELECTION_RESET:
mSelectionMetricsLogger.logSelectionAction(mLastSelectedText,
mLastSelectionOffset, SmartSelectionMetricsLogger.ActionType.RESET,
mLastSelectionOffset, SelectionEvent.ACTION_RESET,
/* SelectionClient.Result = */ null);
break;
case MenuSourceType.MENU_SOURCE_TOUCH_HANDLE:
......@@ -971,28 +972,28 @@ public class SelectionPopupControllerImpl extends ActionModeCallbackHelper
private int getActionType(int menuItemId, int menuItemGroupId) {
if (menuItemGroupId == android.R.id.textAssist) {
return SmartSelectionMetricsLogger.ActionType.SMART_SHARE;
return SelectionEvent.ACTION_SMART_SHARE;
}
if (menuItemId == R.id.select_action_menu_select_all) {
return SmartSelectionMetricsLogger.ActionType.SELECT_ALL;
return SelectionEvent.ACTION_SELECT_ALL;
}
if (menuItemId == R.id.select_action_menu_cut) {
return SmartSelectionMetricsLogger.ActionType.CUT;
return SelectionEvent.ACTION_CUT;
}
if (menuItemId == R.id.select_action_menu_copy) {
return SmartSelectionMetricsLogger.ActionType.COPY;
return SelectionEvent.ACTION_COPY;
}
if (menuItemId == R.id.select_action_menu_paste
|| menuItemId == R.id.select_action_menu_paste_as_plain_text) {
return SmartSelectionMetricsLogger.ActionType.PASTE;
return SelectionEvent.ACTION_PASTE;
}
if (menuItemId == R.id.select_action_menu_share) {
return SmartSelectionMetricsLogger.ActionType.SHARE;
return SelectionEvent.ACTION_SHARE;
}
if (menuItemId == android.R.id.textAssist) {
return SmartSelectionMetricsLogger.ActionType.SMART_SHARE;
return SelectionEvent.ACTION_SMART_SHARE;
}
return SmartSelectionMetricsLogger.ActionType.OTHER;
return SelectionEvent.ACTION_OTHER;
}
/**
......@@ -1396,7 +1397,7 @@ public class SelectionPopupControllerImpl extends ActionModeCallbackHelper
if (unSelected) {
if (mSelectionMetricsLogger != null) {
mSelectionMetricsLogger.logSelectionAction(mLastSelectedText, mLastSelectionOffset,
SmartSelectionMetricsLogger.ActionType.ABANDON,
SelectionEvent.ACTION_ABANDON,
/* SelectionClient.Result = */ null);
}
destroyActionModeAndKeepSelection();
......
......@@ -73,8 +73,16 @@ public class SmartSelectionProvider {
}
}
@TargetApi(Build.VERSION_CODES.O)
public void setTextClassifier(TextClassifier textClassifier) {
mTextClassifier = textClassifier;
Context context = mWindowAndroid.getContext().get();
if (context == null) {
return;
}
((TextClassificationManager) context.getSystemService(Context.TEXT_CLASSIFICATION_SERVICE))
.setTextClassifier(textClassifier);
}
// TODO(wnwen): Remove this suppression once the constant is added to lint.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment