diff options
Diffstat (limited to 'android/view/textclassifier')
-rw-r--r-- | android/view/textclassifier/GenerateLinksLogger.java | 10 | ||||
-rw-r--r-- | android/view/textclassifier/Logger.java | 397 | ||||
-rw-r--r-- | android/view/textclassifier/SelectionEvent.java | 17 | ||||
-rw-r--r-- | android/view/textclassifier/SelectionSessionLogger.java (renamed from android/view/textclassifier/DefaultLogger.java) | 58 | ||||
-rw-r--r-- | android/view/textclassifier/SystemTextClassifier.java | 29 | ||||
-rw-r--r-- | android/view/textclassifier/TextClassification.java | 73 | ||||
-rw-r--r-- | android/view/textclassifier/TextClassificationSession.java | 4 | ||||
-rw-r--r-- | android/view/textclassifier/TextClassifier.java | 105 | ||||
-rw-r--r-- | android/view/textclassifier/TextClassifierImpl.java | 28 | ||||
-rw-r--r-- | android/view/textclassifier/TextLinks.java | 124 | ||||
-rw-r--r-- | android/view/textclassifier/TextSelection.java | 52 |
11 files changed, 369 insertions, 528 deletions
diff --git a/android/view/textclassifier/GenerateLinksLogger.java b/android/view/textclassifier/GenerateLinksLogger.java index 73cf43b8..067513f1 100644 --- a/android/view/textclassifier/GenerateLinksLogger.java +++ b/android/view/textclassifier/GenerateLinksLogger.java @@ -19,13 +19,13 @@ package android.view.textclassifier; import android.annotation.Nullable; import android.metrics.LogMaker; import android.util.ArrayMap; -import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.Preconditions; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Random; @@ -39,6 +39,7 @@ import java.util.UUID; public final class GenerateLinksLogger { private static final String LOG_TAG = "GenerateLinksLogger"; + private static final boolean DEBUG_LOG_ENABLED = false; private static final String ZERO = "0"; private final MetricsLogger mMetricsLogger; @@ -127,7 +128,7 @@ public final class GenerateLinksLogger { } private static void debugLog(LogMaker log) { - if (!Logger.DEBUG_LOG_ENABLED) return; + if (!DEBUG_LOG_ENABLED) return; final String callId = Objects.toString( log.getTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID), ""); @@ -142,8 +143,9 @@ public final class GenerateLinksLogger { final int latencyMs = Integer.parseInt( Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_LATENCY), ZERO)); - Log.d(LOG_TAG, String.format("%s:%s %d links (%d/%d chars) %dms %s", callId, entityType, - numLinks, linkLength, textLength, latencyMs, log.getPackageName())); + Log.d(LOG_TAG, + String.format(Locale.US, "%s:%s %d links (%d/%d chars) %dms %s", callId, entityType, + numLinks, linkLength, textLength, latencyMs, log.getPackageName())); } /** Helper class for storing per-entity type statistics. */ diff --git a/android/view/textclassifier/Logger.java b/android/view/textclassifier/Logger.java deleted file mode 100644 index f03906a0..00000000 --- a/android/view/textclassifier/Logger.java +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.view.textclassifier; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; - -import com.android.internal.util.Preconditions; - -import java.text.BreakIterator; -import java.util.Locale; -import java.util.Objects; - -/** - * A helper for logging TextClassifier related events. - * @hide - */ -public abstract class Logger { - - private static final String LOG_TAG = "Logger"; - /* package */ static final boolean DEBUG_LOG_ENABLED = true; - - private @SelectionEvent.InvocationMethod int mInvocationMethod; - private SelectionEvent mPrevEvent; - private SelectionEvent mSmartEvent; - private SelectionEvent mStartEvent; - - /** - * Logger that does not log anything. - * @hide - */ - public static final Logger DISABLED = new Logger() { - @Override - public void writeEvent(SelectionEvent event) {} - }; - - @Nullable - private final Config mConfig; - - public Logger(Config config) { - mConfig = Preconditions.checkNotNull(config); - } - - private Logger() { - mConfig = null; - } - - /** - * Writes the selection event to a log. - */ - public abstract void writeEvent(@NonNull SelectionEvent event); - - /** - * Returns true if the resultId matches that of a smart selection event (i.e. - * {@link SelectionEvent#EVENT_SMART_SELECTION_SINGLE} or - * {@link SelectionEvent#EVENT_SMART_SELECTION_MULTI}). - * Returns false otherwise. - */ - public boolean isSmartSelection(@NonNull String resultId) { - return false; - } - - /** - * Returns a token iterator for tokenizing text for logging purposes. - */ - public BreakIterator getTokenIterator(@NonNull Locale locale) { - return BreakIterator.getWordInstance(Preconditions.checkNotNull(locale)); - } - - /** - * Logs a "selection started" event. - * - * @param invocationMethod the way the selection was triggered - * @param start the token index of the selected token - */ - public final void logSelectionStartedEvent( - @SelectionEvent.InvocationMethod int invocationMethod, int start) { - if (mConfig == null) { - return; - } - - mInvocationMethod = invocationMethod; - logEvent(new SelectionEvent( - start, start + 1, SelectionEvent.EVENT_SELECTION_STARTED, - TextClassifier.TYPE_UNKNOWN, mInvocationMethod, null, mConfig)); - } - - /** - * Logs a "selection modified" event. - * Use when the user modifies the selection. - * - * @param start the start token (inclusive) index of the selection - * @param end the end token (exclusive) index of the selection - */ - public final void logSelectionModifiedEvent(int start, int end) { - Preconditions.checkArgument(end >= start, "end cannot be less than start"); - - if (mConfig == null) { - return; - } - - logEvent(new SelectionEvent( - start, end, SelectionEvent.EVENT_SELECTION_MODIFIED, - TextClassifier.TYPE_UNKNOWN, mInvocationMethod, null, mConfig)); - } - - /** - * Logs a "selection modified" event. - * Use when the user modifies the selection and the selection's entity type is known. - * - * @param start the start token (inclusive) index of the selection - * @param end the end token (exclusive) index of the selection - * @param classification the TextClassification object returned by the TextClassifier that - * classified the selected text - */ - public final void logSelectionModifiedEvent( - int start, int end, @NonNull TextClassification classification) { - Preconditions.checkArgument(end >= start, "end cannot be less than start"); - Preconditions.checkNotNull(classification); - - if (mConfig == null) { - return; - } - - final String entityType = classification.getEntityCount() > 0 - ? classification.getEntity(0) - : TextClassifier.TYPE_UNKNOWN; - logEvent(new SelectionEvent( - start, end, SelectionEvent.EVENT_SELECTION_MODIFIED, - entityType, mInvocationMethod, classification.getId(), mConfig)); - } - - /** - * Logs a "selection modified" event. - * Use when a TextClassifier modifies the selection. - * - * @param start the start token (inclusive) index of the selection - * @param end the end token (exclusive) index of the selection - * @param selection the TextSelection object returned by the TextClassifier for the - * specified selection - */ - public final void logSelectionModifiedEvent( - int start, int end, @NonNull TextSelection selection) { - Preconditions.checkArgument(end >= start, "end cannot be less than start"); - Preconditions.checkNotNull(selection); - - if (mConfig == null) { - return; - } - - final int eventType; - if (isSmartSelection(selection.getId())) { - eventType = end - start > 1 - ? SelectionEvent.EVENT_SMART_SELECTION_MULTI - : SelectionEvent.EVENT_SMART_SELECTION_SINGLE; - - } else { - eventType = SelectionEvent.EVENT_AUTO_SELECTION; - } - final String entityType = selection.getEntityCount() > 0 - ? selection.getEntity(0) - : TextClassifier.TYPE_UNKNOWN; - logEvent(new SelectionEvent(start, end, eventType, entityType, mInvocationMethod, - selection.getId(), mConfig)); - } - - /** - * Logs an event specifying an action taken on a selection. - * Use when the user clicks on an action to act on the selected text. - * - * @param start the start token (inclusive) index of the selection - * @param end the end token (exclusive) index of the selection - * @param actionType the action that was performed on the selection - */ - public final void logSelectionActionEvent( - int start, int end, @SelectionEvent.ActionType int actionType) { - Preconditions.checkArgument(end >= start, "end cannot be less than start"); - checkActionType(actionType); - - if (mConfig == null) { - return; - } - - logEvent(new SelectionEvent( - start, end, actionType, TextClassifier.TYPE_UNKNOWN, mInvocationMethod, - null, mConfig)); - } - - /** - * Logs an event specifying an action taken on a selection. - * Use when the user clicks on an action to act on the selected text and the selection's - * entity type is known. - * - * @param start the start token (inclusive) index of the selection - * @param end the end token (exclusive) index of the selection - * @param actionType the action that was performed on the selection - * @param classification the TextClassification object returned by the TextClassifier that - * classified the selected text - * - * @throws IllegalArgumentException If actionType is not a valid SelectionEvent actionType - */ - public final void logSelectionActionEvent( - int start, int end, @SelectionEvent.ActionType int actionType, - @NonNull TextClassification classification) { - Preconditions.checkArgument(end >= start, "end cannot be less than start"); - Preconditions.checkNotNull(classification); - checkActionType(actionType); - - if (mConfig == null) { - return; - } - - final String entityType = classification.getEntityCount() > 0 - ? classification.getEntity(0) - : TextClassifier.TYPE_UNKNOWN; - logEvent(new SelectionEvent(start, end, actionType, entityType, mInvocationMethod, - classification.getId(), mConfig)); - } - - private void logEvent(@NonNull SelectionEvent event) { - Preconditions.checkNotNull(event); - - if (event.getEventType() != SelectionEvent.EVENT_SELECTION_STARTED - && mStartEvent == null) { - if (DEBUG_LOG_ENABLED) { - Log.d(LOG_TAG, "Selection session not yet started. Ignoring event"); - } - return; - } - - final long now = System.currentTimeMillis(); - switch (event.getEventType()) { - case SelectionEvent.EVENT_SELECTION_STARTED: - Preconditions.checkArgument(event.getAbsoluteEnd() == event.getAbsoluteStart() + 1); - event.setSessionId(startNewSession()); - mStartEvent = event; - break; - case SelectionEvent.EVENT_SMART_SELECTION_SINGLE: // fall through - case SelectionEvent.EVENT_SMART_SELECTION_MULTI: - mSmartEvent = event; - break; - case SelectionEvent.EVENT_SELECTION_MODIFIED: // fall through - case SelectionEvent.EVENT_AUTO_SELECTION: - if (mPrevEvent != null - && mPrevEvent.getAbsoluteStart() == event.getAbsoluteStart() - && mPrevEvent.getAbsoluteEnd() == event.getAbsoluteEnd()) { - // Selection did not change. Ignore event. - return; - } - break; - default: - // do nothing. - } - - event.setEventTime(now); - if (mStartEvent != null) { - event.setSessionId(mStartEvent.getSessionId()) - .setDurationSinceSessionStart(now - mStartEvent.getEventTime()) - .setStart(event.getAbsoluteStart() - mStartEvent.getAbsoluteStart()) - .setEnd(event.getAbsoluteEnd() - mStartEvent.getAbsoluteStart()); - } - if (mSmartEvent != null) { - event.setResultId(mSmartEvent.getResultId()) - .setSmartStart(mSmartEvent.getAbsoluteStart() - mStartEvent.getAbsoluteStart()) - .setSmartEnd(mSmartEvent.getAbsoluteEnd() - mStartEvent.getAbsoluteStart()); - } - if (mPrevEvent != null) { - event.setDurationSincePreviousEvent(now - mPrevEvent.getEventTime()) - .setEventIndex(mPrevEvent.getEventIndex() + 1); - } - writeEvent(event); - mPrevEvent = event; - - if (event.isTerminal()) { - endSession(); - } - } - - private TextClassificationSessionId startNewSession() { - endSession(); - return new TextClassificationSessionId(); - } - - private void endSession() { - mPrevEvent = null; - mSmartEvent = null; - mStartEvent = null; - } - - /** - * @throws IllegalArgumentException If eventType is not an {@link SelectionEvent.ActionType} - */ - private static void checkActionType(@SelectionEvent.EventType int eventType) - throws IllegalArgumentException { - switch (eventType) { - case SelectionEvent.ACTION_OVERTYPE: // fall through - case SelectionEvent.ACTION_COPY: // fall through - case SelectionEvent.ACTION_PASTE: // fall through - case SelectionEvent.ACTION_CUT: // fall through - case SelectionEvent.ACTION_SHARE: // fall through - case SelectionEvent.ACTION_SMART_SHARE: // fall through - case SelectionEvent.ACTION_DRAG: // fall through - case SelectionEvent.ACTION_ABANDON: // fall through - case SelectionEvent.ACTION_SELECT_ALL: // fall through - case SelectionEvent.ACTION_RESET: // fall through - return; - default: - throw new IllegalArgumentException( - String.format(Locale.US, "%d is not an eventType", eventType)); - } - } - - - /** - * A Logger config. - */ - public static final class Config { - - private final String mPackageName; - private final String mWidgetType; - @Nullable private final String mWidgetVersion; - - /** - * @param context Context of the widget the logger logs for - * @param widgetType a name for the widget being logged for. e.g. - * {@link TextClassifier#WIDGET_TYPE_TEXTVIEW} - * @param widgetVersion a string version info for the widget the logger logs for - */ - public Config( - @NonNull Context context, - @TextClassifier.WidgetType String widgetType, - @Nullable String widgetVersion) { - mPackageName = Preconditions.checkNotNull(context).getPackageName(); - mWidgetType = widgetType; - mWidgetVersion = widgetVersion; - } - - /** - * Returns the package name of the application the logger logs for. - */ - public String getPackageName() { - return mPackageName; - } - - /** - * Returns the name for the widget being logged for. e.g. - * {@link TextClassifier#WIDGET_TYPE_TEXTVIEW}. - */ - public String getWidgetType() { - return mWidgetType; - } - - /** - * Returns string version info for the logger. This is specific to the text classifier. - */ - @Nullable - public String getWidgetVersion() { - return mWidgetVersion; - } - - @Override - public int hashCode() { - return Objects.hash(mPackageName, mWidgetType, mWidgetVersion); - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - - if (!(obj instanceof Config)) { - return false; - } - - final Config other = (Config) obj; - return Objects.equals(mPackageName, other.mPackageName) - && Objects.equals(mWidgetType, other.mWidgetType) - && Objects.equals(mWidgetVersion, other.mWidgetType); - } - } -} diff --git a/android/view/textclassifier/SelectionEvent.java b/android/view/textclassifier/SelectionEvent.java index 1e978ccf..b0735969 100644 --- a/android/view/textclassifier/SelectionEvent.java +++ b/android/view/textclassifier/SelectionEvent.java @@ -150,20 +150,6 @@ public final class SelectionEvent implements Parcelable { mInvocationMethod = invocationMethod; } - SelectionEvent( - int start, int end, - @EventType int eventType, @EntityType String entityType, - @InvocationMethod int invocationMethod, @Nullable String resultId, - Logger.Config config) { - this(start, end, eventType, entityType, invocationMethod, resultId); - Preconditions.checkNotNull(config); - setTextClassificationSessionContext( - new TextClassificationContext.Builder( - config.getPackageName(), config.getWidgetType()) - .setWidgetVersion(config.getWidgetVersion()) - .build()); - } - private SelectionEvent(Parcel in) { mAbsoluteStart = in.readInt(); mAbsoluteEnd = in.readInt(); @@ -362,6 +348,7 @@ public final class SelectionEvent implements Parcelable { case SelectionEvent.ACTION_ABANDON: // fall through case SelectionEvent.ACTION_SELECT_ALL: // fall through case SelectionEvent.ACTION_RESET: // fall through + case SelectionEvent.ACTION_OTHER: // fall through return; default: throw new IllegalArgumentException( @@ -667,4 +654,4 @@ public final class SelectionEvent implements Parcelable { return new SelectionEvent[size]; } }; -}
\ No newline at end of file +} diff --git a/android/view/textclassifier/DefaultLogger.java b/android/view/textclassifier/SelectionSessionLogger.java index 203ca560..f2fb63eb 100644 --- a/android/view/textclassifier/DefaultLogger.java +++ b/android/view/textclassifier/SelectionSessionLogger.java @@ -17,28 +17,29 @@ package android.view.textclassifier; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.metrics.LogMaker; -import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.Preconditions; +import java.text.BreakIterator; import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.StringJoiner; /** - * Default Logger. - * Used internally by TextClassifierImpl. + * A helper for logging selection session events. * @hide */ -public final class DefaultLogger extends Logger { +public final class SelectionSessionLogger { - private static final String LOG_TAG = "DefaultLogger"; + private static final String LOG_TAG = "SelectionSessionLogger"; + private static final boolean DEBUG_LOG_ENABLED = false; static final String CLASSIFIER_ID = "androidtc"; private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START; @@ -59,23 +60,16 @@ public final class DefaultLogger extends Logger { private final MetricsLogger mMetricsLogger; - public DefaultLogger(@NonNull Config config) { - super(config); + public SelectionSessionLogger() { mMetricsLogger = new MetricsLogger(); } @VisibleForTesting - public DefaultLogger(@NonNull Config config, @NonNull MetricsLogger metricsLogger) { - super(config); + public SelectionSessionLogger(@NonNull MetricsLogger metricsLogger) { mMetricsLogger = Preconditions.checkNotNull(metricsLogger); } - @Override - public boolean isSmartSelection(@NonNull String signature) { - return CLASSIFIER_ID.equals(SignatureParser.getClassifierId(signature)); - } - - @Override + /** Emits a selection event to the logs. */ public void writeEvent(@NonNull SelectionEvent event) { Preconditions.checkNotNull(event); final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION) @@ -93,7 +87,7 @@ public final class DefaultLogger extends Logger { .addTaggedData(SMART_END, event.getSmartEnd()) .addTaggedData(EVENT_START, event.getStart()) .addTaggedData(EVENT_END, event.getEnd()) - .addTaggedData(SESSION_ID, event.getSessionId()); + .addTaggedData(SESSION_ID, event.getSessionId().flattenToString()); mMetricsLogger.write(log); debugLog(log); } @@ -225,9 +219,17 @@ public final class DefaultLogger extends Logger { final int eventEnd = Integer.parseInt( Objects.toString(log.getTaggedData(EVENT_END), ZERO)); - Log.d(LOG_TAG, String.format("%2d: %s/%s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)", - index, type, subType, entity, eventStart, eventEnd, smartStart, smartEnd, widget, - model)); + Log.d(LOG_TAG, + String.format(Locale.US, "%2d: %s/%s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)", + index, type, subType, entity, eventStart, eventEnd, smartStart, smartEnd, + widget, model)); + } + + /** + * Returns a token iterator for tokenizing text for logging purposes. + */ + public static BreakIterator getTokenIterator(@NonNull Locale locale) { + return BreakIterator.getWordInstance(Preconditions.checkNotNull(locale)); } /** @@ -260,8 +262,10 @@ public final class DefaultLogger extends Logger { return String.format(Locale.US, "%s|%s|%d", classifierId, modelName, hash); } - static String getClassifierId(String signature) { - Preconditions.checkNotNull(signature); + static String getClassifierId(@Nullable String signature) { + if (signature == null) { + return ""; + } final int end = signature.indexOf("|"); if (end >= 0) { return signature.substring(0, end); @@ -269,8 +273,10 @@ public final class DefaultLogger extends Logger { return ""; } - static String getModelName(String signature) { - Preconditions.checkNotNull(signature); + static String getModelName(@Nullable String signature) { + if (signature == null) { + return ""; + } final int start = signature.indexOf("|") + 1; final int end = signature.indexOf("|", start); if (start >= 1 && end >= start) { @@ -279,8 +285,10 @@ public final class DefaultLogger extends Logger { return ""; } - static int getHash(String signature) { - Preconditions.checkNotNull(signature); + static int getHash(@Nullable String signature) { + if (signature == null) { + return 0; + } final int index1 = signature.indexOf("|"); final int index2 = signature.indexOf("|", index1); if (index2 > 0) { diff --git a/android/view/textclassifier/SystemTextClassifier.java b/android/view/textclassifier/SystemTextClassifier.java index 45fd6bfb..490c3890 100644 --- a/android/view/textclassifier/SystemTextClassifier.java +++ b/android/view/textclassifier/SystemTextClassifier.java @@ -28,7 +28,6 @@ import android.service.textclassifier.ITextClassifierService; import android.service.textclassifier.ITextLinksCallback; import android.service.textclassifier.ITextSelectionCallback; -import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.util.Preconditions; @@ -49,13 +48,6 @@ public final class SystemTextClassifier implements TextClassifier { private final TextClassificationConstants mSettings; private final TextClassifier mFallback; private final String mPackageName; - - private final Object mLoggerLock = new Object(); - @GuardedBy("mLoggerLock") - private Logger.Config mLoggerConfig; - @GuardedBy("mLoggerLock") - private Logger mLogger; - @GuardedBy("mLoggerLock") private TextClassificationSessionId mSessionId; public SystemTextClassifier(Context context, TextClassificationConstants settings) @@ -147,27 +139,6 @@ public final class SystemTextClassifier implements TextClassifier { } @Override - public Logger getLogger(@NonNull Logger.Config config) { - Preconditions.checkNotNull(config); - synchronized (mLoggerLock) { - if (mLogger == null || !config.equals(mLoggerConfig)) { - mLoggerConfig = config; - mLogger = new Logger(config) { - @Override - public void writeEvent(SelectionEvent event) { - try { - mManagerService.onSelectionEvent(mSessionId, event); - } catch (RemoteException e) { - Log.e(LOG_TAG, "Error reporting selection event.", e); - } - } - }; - } - } - return mLogger; - } - - @Override public void destroy() { try { if (mSessionId != null) { diff --git a/android/view/textclassifier/TextClassification.java b/android/view/textclassifier/TextClassification.java index 37a5d9a1..96016b44 100644 --- a/android/view/textclassifier/TextClassification.java +++ b/android/view/textclassifier/TextClassification.java @@ -375,13 +375,13 @@ public final class TextClassification implements Parcelable { */ public static final class Builder { - @NonNull private String mText; @NonNull private List<RemoteAction> mActions = new ArrayList<>(); @NonNull private final Map<String, Float> mEntityConfidence = new ArrayMap<>(); - @Nullable Drawable mLegacyIcon; - @Nullable String mLegacyLabel; - @Nullable Intent mLegacyIntent; - @Nullable OnClickListener mLegacyOnClickListener; + @Nullable private String mText; + @Nullable private Drawable mLegacyIcon; + @Nullable private String mLegacyLabel; + @Nullable private Intent mLegacyIntent; + @Nullable private OnClickListener mLegacyOnClickListener; @Nullable private String mId; /** @@ -721,4 +721,67 @@ public final class TextClassification implements Parcelable { mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in); mId = in.readString(); } + + // TODO: Remove once apps can build against the latest sdk. + /** + * Optional input parameters for generating TextClassification. + * @hide + */ + public static final class Options { + + @Nullable private final TextClassificationSessionId mSessionId; + @Nullable private final Request mRequest; + @Nullable private LocaleList mDefaultLocales; + @Nullable private ZonedDateTime mReferenceTime; + + public Options() { + this(null, null); + } + + private Options( + @Nullable TextClassificationSessionId sessionId, @Nullable Request request) { + mSessionId = sessionId; + mRequest = request; + } + + /** Helper to create Options from a Request. */ + public static Options from(TextClassificationSessionId sessionId, Request request) { + final Options options = new Options(sessionId, request); + options.setDefaultLocales(request.getDefaultLocales()); + options.setReferenceTime(request.getReferenceTime()); + return options; + } + + /** @param defaultLocales ordered list of locale preferences. */ + public Options setDefaultLocales(@Nullable LocaleList defaultLocales) { + mDefaultLocales = defaultLocales; + return this; + } + + /** @param referenceTime refrence time used for interpreting relatives dates */ + public Options setReferenceTime(@Nullable ZonedDateTime referenceTime) { + mReferenceTime = referenceTime; + return this; + } + + @Nullable + public LocaleList getDefaultLocales() { + return mDefaultLocales; + } + + @Nullable + public ZonedDateTime getReferenceTime() { + return mReferenceTime; + } + + @Nullable + public Request getRequest() { + return mRequest; + } + + @Nullable + public TextClassificationSessionId getSessionId() { + return mSessionId; + } + } } diff --git a/android/view/textclassifier/TextClassificationSession.java b/android/view/textclassifier/TextClassificationSession.java index e8e300a9..4c641985 100644 --- a/android/view/textclassifier/TextClassificationSession.java +++ b/android/view/textclassifier/TextClassificationSession.java @@ -17,7 +17,6 @@ package android.view.textclassifier; import android.annotation.WorkerThread; -import android.view.textclassifier.DefaultLogger.SignatureParser; import android.view.textclassifier.SelectionEvent.InvocationMethod; import com.android.internal.util.Preconditions; @@ -222,7 +221,8 @@ final class TextClassificationSession implements TextClassifier { } private static boolean isPlatformLocalTextClassifierSmartSelection(String signature) { - return DefaultLogger.CLASSIFIER_ID.equals(SignatureParser.getClassifierId(signature)); + return SelectionSessionLogger.CLASSIFIER_ID.equals( + SelectionSessionLogger.SignatureParser.getClassifierId(signature)); } } } diff --git a/android/view/textclassifier/TextClassifier.java b/android/view/textclassifier/TextClassifier.java index 54261be3..da47bcb1 100644 --- a/android/view/textclassifier/TextClassifier.java +++ b/android/view/textclassifier/TextClassifier.java @@ -41,8 +41,9 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.List; +import java.util.HashSet; import java.util.Map; +import java.util.Set; /** * Interface for providing text classification related features. @@ -208,6 +209,26 @@ public interface TextClassifier { return suggestSelection(request); } + // TODO: Remove once apps can build against the latest sdk. + /** @hide */ + default TextSelection suggestSelection( + @NonNull CharSequence text, + @IntRange(from = 0) int selectionStartIndex, + @IntRange(from = 0) int selectionEndIndex, + @Nullable TextSelection.Options options) { + if (options == null) { + return suggestSelection(new TextSelection.Request.Builder( + text, selectionStartIndex, selectionEndIndex).build()); + } else if (options.getRequest() != null) { + return suggestSelection(options.getRequest()); + } else { + return suggestSelection( + new TextSelection.Request.Builder(text, selectionStartIndex, selectionEndIndex) + .setDefaultLocales(options.getDefaultLocales()) + .build()); + } + } + /** * Classifies the specified text and returns a {@link TextClassification} object that can be * used to generate a widget for handling the classified text. @@ -267,6 +288,26 @@ public interface TextClassifier { return classifyText(request); } + // TODO: Remove once apps can build against the latest sdk. + /** @hide */ + default TextClassification classifyText( + @NonNull CharSequence text, + @IntRange(from = 0) int startIndex, + @IntRange(from = 0) int endIndex, + @Nullable TextClassification.Options options) { + if (options == null) { + return classifyText( + new TextClassification.Request.Builder(text, startIndex, endIndex).build()); + } else if (options.getRequest() != null) { + return classifyText(options.getRequest()); + } else { + return classifyText(new TextClassification.Request.Builder(text, startIndex, endIndex) + .setDefaultLocales(options.getDefaultLocales()) + .setReferenceTime(options.getReferenceTime()) + .build()); + } + } + /** * Generates and returns a {@link TextLinks} that may be applied to the text to annotate it with * links information. @@ -288,6 +329,22 @@ public interface TextClassifier { return new TextLinks.Builder(request.getText().toString()).build(); } + // TODO: Remove once apps can build against the latest sdk. + /** @hide */ + default TextLinks generateLinks( + @NonNull CharSequence text, @Nullable TextLinks.Options options) { + if (options == null) { + return generateLinks(new TextLinks.Request.Builder(text).build()); + } else if (options.getRequest() != null) { + return generateLinks(options.getRequest()); + } else { + return generateLinks(new TextLinks.Request.Builder(text) + .setDefaultLocales(options.getDefaultLocales()) + .setEntityConfig(options.getEntityConfig()) + .build()); + } + } + /** * Returns the maximal length of text that can be processed by generateLinks. * @@ -302,18 +359,6 @@ public interface TextClassifier { } /** - * Returns a helper for logging TextClassifier related events. - * - * @param config logger configuration - * @hide - */ - @WorkerThread - default Logger getLogger(@NonNull Logger.Config config) { - Preconditions.checkNotNull(config); - return Logger.DISABLED; - } - - /** * Reports a selection event. * * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should @@ -377,6 +422,12 @@ public interface TextClassifier { /* includedEntityTypes */null, /* excludedEntityTypes */ null); } + // TODO: Remove once apps can build against the latest sdk. + /** @hide */ + public static EntityConfig create(@Nullable Collection<String> hints) { + return createWithHints(hints); + } + /** * Creates an EntityConfig. * @@ -406,6 +457,12 @@ public interface TextClassifier { /* includedEntityTypes */ entityTypes, /* excludedEntityTypes */ null); } + // TODO: Remove once apps can build against the latest sdk. + /** @hide */ + public static EntityConfig createWithEntityList(@Nullable Collection<String> entityTypes) { + return createWithExplicitEntityList(entityTypes); + } + /** * Returns a list of the final set of entities to find. * @@ -413,21 +470,15 @@ public interface TextClassifier { * * This method is intended for use by TextClassifier implementations. */ - public List<String> resolveEntityListModifications(@NonNull Collection<String> entities) { - final ArrayList<String> finalList = new ArrayList<>(); + public Collection<String> resolveEntityListModifications( + @NonNull Collection<String> entities) { + final Set<String> finalSet = new HashSet(); if (mUseHints) { - for (String entity : entities) { - if (!mExcludedEntityTypes.contains(entity)) { - finalList.add(entity); - } - } - } - for (String entity : mIncludedEntityTypes) { - if (!mExcludedEntityTypes.contains(entity) && !finalList.contains(entity)) { - finalList.add(entity); - } + finalSet.addAll(entities); } - return finalList; + finalSet.addAll(mIncludedEntityTypes); + finalSet.removeAll(mExcludedEntityTypes); + return finalSet; } /** @@ -508,7 +559,7 @@ public interface TextClassifier { final String string = request.getText().toString(); final TextLinks.Builder links = new TextLinks.Builder(string); - final List<String> entities = request.getEntityConfig() + final Collection<String> entities = request.getEntityConfig() .resolveEntityListModifications(Collections.emptyList()); if (entities.contains(TextClassifier.TYPE_URL)) { addLinks(links, string, TextClassifier.TYPE_URL); diff --git a/android/view/textclassifier/TextClassifierImpl.java b/android/view/textclassifier/TextClassifierImpl.java index 7e3748ae..22133558 100644 --- a/android/view/textclassifier/TextClassifierImpl.java +++ b/android/view/textclassifier/TextClassifierImpl.java @@ -94,11 +94,7 @@ public final class TextClassifierImpl implements TextClassifier { private final Object mLoggerLock = new Object(); @GuardedBy("mLoggerLock") // Do not access outside this lock. - private Logger.Config mLoggerConfig; - @GuardedBy("mLoggerLock") // Do not access outside this lock. - private Logger mLogger; - @GuardedBy("mLoggerLock") // Do not access outside this lock. - private Logger mLogger2; // This is the new logger. Will replace mLogger. + private SelectionSessionLogger mSessionLogger; private final TextClassificationConstants mSettings; @@ -283,28 +279,14 @@ public final class TextClassifierImpl implements TextClassifier { } } - /** @inheritDoc */ - @Override - public Logger getLogger(@NonNull Logger.Config config) { - Preconditions.checkNotNull(config); - synchronized (mLoggerLock) { - if (mLogger == null || !config.equals(mLoggerConfig)) { - mLoggerConfig = config; - mLogger = new DefaultLogger(config); - } - } - return mLogger; - } - @Override public void onSelectionEvent(SelectionEvent event) { Preconditions.checkNotNull(event); synchronized (mLoggerLock) { - if (mLogger2 == null) { - mLogger2 = new DefaultLogger( - new Logger.Config(mContext, WIDGET_TYPE_UNKNOWN, null)); + if (mSessionLogger == null) { + mSessionLogger = new SelectionSessionLogger(); } - mLogger2.writeEvent(event); + mSessionLogger.writeEvent(event); } } @@ -331,7 +313,7 @@ public final class TextClassifierImpl implements TextClassifier { private String createId(String text, int start, int end) { synchronized (mLock) { - return DefaultLogger.createId(text, start, end, mContext, mModel.getVersion(), + return SelectionSessionLogger.createId(text, start, end, mContext, mModel.getVersion(), mModel.getSupportedLocales()); } } diff --git a/android/view/textclassifier/TextLinks.java b/android/view/textclassifier/TextLinks.java index 17c7b13c..851b2c9b 100644 --- a/android/view/textclassifier/TextLinks.java +++ b/android/view/textclassifier/TextLinks.java @@ -28,6 +28,8 @@ import android.text.Spannable; import android.text.method.MovementMethod; import android.text.style.ClickableSpan; import android.text.style.URLSpan; +import android.text.util.Linkify; +import android.text.util.Linkify.LinkifyMask; import android.view.View; import android.view.textclassifier.TextClassifier.EntityType; import android.widget.TextView; @@ -337,7 +339,7 @@ public final class TextLinks implements Parcelable { /** * @return The config representing the set of entities to look for - * @see #setEntityConfig(TextClassifier.EntityConfig) + * @see Builder#setEntityConfig(TextClassifier.EntityConfig) */ @Nullable public TextClassifier.EntityConfig getEntityConfig() { @@ -607,4 +609,124 @@ public final class TextLinks implements Parcelable { return new TextLinks(mFullText, mLinks); } } + + // TODO: Remove once apps can build against the latest sdk. + /** + * Optional input parameters for generating TextLinks. + * @hide + */ + public static final class Options { + + @Nullable private final TextClassificationSessionId mSessionId; + @Nullable private final Request mRequest; + @Nullable private LocaleList mDefaultLocales; + @Nullable private TextClassifier.EntityConfig mEntityConfig; + private boolean mLegacyFallback; + + private @ApplyStrategy int mApplyStrategy; + private Function<TextLink, TextLinkSpan> mSpanFactory; + + private String mCallingPackageName; + + public Options() { + this(null, null); + } + + private Options( + @Nullable TextClassificationSessionId sessionId, @Nullable Request request) { + mSessionId = sessionId; + mRequest = request; + } + + /** Helper to create Options from a Request. */ + public static Options from(TextClassificationSessionId sessionId, Request request) { + final Options options = new Options(sessionId, request); + options.setDefaultLocales(request.getDefaultLocales()); + options.setEntityConfig(request.getEntityConfig()); + return options; + } + + /** Returns a new options object based on the specified link mask. */ + public static Options fromLinkMask(@LinkifyMask int mask) { + final List<String> entitiesToFind = new ArrayList<>(); + + if ((mask & Linkify.WEB_URLS) != 0) { + entitiesToFind.add(TextClassifier.TYPE_URL); + } + if ((mask & Linkify.EMAIL_ADDRESSES) != 0) { + entitiesToFind.add(TextClassifier.TYPE_EMAIL); + } + if ((mask & Linkify.PHONE_NUMBERS) != 0) { + entitiesToFind.add(TextClassifier.TYPE_PHONE); + } + if ((mask & Linkify.MAP_ADDRESSES) != 0) { + entitiesToFind.add(TextClassifier.TYPE_ADDRESS); + } + + return new Options().setEntityConfig( + TextClassifier.EntityConfig.createWithEntityList(entitiesToFind)); + } + + /** @param defaultLocales ordered list of locale preferences. */ + public Options setDefaultLocales(@Nullable LocaleList defaultLocales) { + mDefaultLocales = defaultLocales; + return this; + } + + /** @param entityConfig definition of which entity types to look for. */ + public Options setEntityConfig(@Nullable TextClassifier.EntityConfig entityConfig) { + mEntityConfig = entityConfig; + return this; + } + + /** @param applyStrategy strategy to use when resolving conflicts. */ + public Options setApplyStrategy(@ApplyStrategy int applyStrategy) { + checkValidApplyStrategy(applyStrategy); + mApplyStrategy = applyStrategy; + return this; + } + + /** @param spanFactory factory for converting TextLink to TextLinkSpan. */ + public Options setSpanFactory(@Nullable Function<TextLink, TextLinkSpan> spanFactory) { + mSpanFactory = spanFactory; + return this; + } + + @Nullable + public LocaleList getDefaultLocales() { + return mDefaultLocales; + } + + @Nullable + public TextClassifier.EntityConfig getEntityConfig() { + return mEntityConfig; + } + + @ApplyStrategy + public int getApplyStrategy() { + return mApplyStrategy; + } + + @Nullable + public Function<TextLink, TextLinkSpan> getSpanFactory() { + return mSpanFactory; + } + + @Nullable + public Request getRequest() { + return mRequest; + } + + @Nullable + public TextClassificationSessionId getSessionId() { + return mSessionId; + } + + private static void checkValidApplyStrategy(int applyStrategy) { + if (applyStrategy != APPLY_STRATEGY_IGNORE && applyStrategy != APPLY_STRATEGY_REPLACE) { + throw new IllegalArgumentException( + "Invalid apply strategy. See TextLinks.ApplyStrategy for options."); + } + } + } } diff --git a/android/view/textclassifier/TextSelection.java b/android/view/textclassifier/TextSelection.java index 939e7176..17687c9e 100644 --- a/android/view/textclassifier/TextSelection.java +++ b/android/view/textclassifier/TextSelection.java @@ -375,4 +375,56 @@ public final class TextSelection implements Parcelable { mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in); mId = in.readString(); } + + + // TODO: Remove once apps can build against the latest sdk. + /** + * Optional input parameters for generating TextSelection. + * @hide + */ + public static final class Options { + + @Nullable private final TextClassificationSessionId mSessionId; + @Nullable private final Request mRequest; + @Nullable private LocaleList mDefaultLocales; + private boolean mDarkLaunchAllowed; + + public Options() { + this(null, null); + } + + private Options( + @Nullable TextClassificationSessionId sessionId, @Nullable Request request) { + mSessionId = sessionId; + mRequest = request; + } + + /** Helper to create Options from a Request. */ + public static Options from(TextClassificationSessionId sessionId, Request request) { + final Options options = new Options(sessionId, request); + options.setDefaultLocales(request.getDefaultLocales()); + return options; + } + + /** @param defaultLocales ordered list of locale preferences. */ + public Options setDefaultLocales(@Nullable LocaleList defaultLocales) { + mDefaultLocales = defaultLocales; + return this; + } + + @Nullable + public LocaleList getDefaultLocales() { + return mDefaultLocales; + } + + @Nullable + public Request getRequest() { + return mRequest; + } + + @Nullable + public TextClassificationSessionId getSessionId() { + return mSessionId; + } + } } |