Merge changes from topics "tcevent", "tc-ca-id"

* changes:
  Introduce TextClassifierEvent
  Add id to ConversationActions and conversationId to its Request
This commit is contained in:
Abodunrinwa Toki
2018-12-14 12:41:40 +00:00
committed by Android (Google) Code Review
13 changed files with 810 additions and 33 deletions

View File

@@ -52606,9 +52606,10 @@ package android.view.inspector {
package android.view.textclassifier {
public final class ConversationActions implements android.os.Parcelable {
ctor public ConversationActions(java.util.List<android.view.textclassifier.ConversationActions.ConversationAction>);
ctor public ConversationActions(java.util.List<android.view.textclassifier.ConversationActions.ConversationAction>, java.lang.String);
method public int describeContents();
method public java.util.List<android.view.textclassifier.ConversationActions.ConversationAction> getConversationActions();
method public java.lang.String getId();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions> CREATOR;
field public static final java.lang.String HINT_FOR_IN_APP = "in_app";
@@ -52669,6 +52670,7 @@ package android.view.textclassifier {
method public int describeContents();
method public java.lang.String getCallingPackageName();
method public java.util.List<android.view.textclassifier.ConversationActions.Message> getConversation();
method public java.lang.String getConversationId();
method public java.util.List<java.lang.String> getHints();
method public int getMaxSuggestions();
method public android.view.textclassifier.ConversationActions.TypeConfig getTypeConfig();
@@ -52679,6 +52681,7 @@ package android.view.textclassifier {
public static final class ConversationActions.Request.Builder {
ctor public ConversationActions.Request.Builder(java.util.List<android.view.textclassifier.ConversationActions.Message>);
method public android.view.textclassifier.ConversationActions.Request build();
method public android.view.textclassifier.ConversationActions.Request.Builder setConversationId(java.lang.String);
method public android.view.textclassifier.ConversationActions.Request.Builder setHints(java.util.List<java.lang.String>);
method public android.view.textclassifier.ConversationActions.Request.Builder setMaxSuggestions(int);
method public android.view.textclassifier.ConversationActions.Request.Builder setTypeConfig(android.view.textclassifier.ConversationActions.TypeConfig);
@@ -52841,6 +52844,7 @@ package android.view.textclassifier {
method public default int getMaxGenerateLinksTextLength();
method public default boolean isDestroyed();
method public default void onSelectionEvent(android.view.textclassifier.SelectionEvent);
method public default void onTextClassifierEvent(android.view.textclassifier.TextClassifierEvent);
method public default android.view.textclassifier.ConversationActions suggestConversationActions(android.view.textclassifier.ConversationActions.Request);
method public default android.view.textclassifier.TextSelection suggestSelection(android.view.textclassifier.TextSelection.Request);
method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
@@ -52862,6 +52866,7 @@ package android.view.textclassifier {
field public static final java.lang.String WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
field public static final java.lang.String WIDGET_TYPE_EDITTEXT = "edittext";
field public static final java.lang.String WIDGET_TYPE_EDIT_WEBVIEW = "edit-webview";
field public static final java.lang.String WIDGET_TYPE_NOTIFICATION = "notification";
field public static final java.lang.String WIDGET_TYPE_TEXTVIEW = "textview";
field public static final java.lang.String WIDGET_TYPE_UNKNOWN = "unknown";
field public static final java.lang.String WIDGET_TYPE_UNSELECTABLE_TEXTVIEW = "nosel-textview";
@@ -52879,6 +52884,68 @@ package android.view.textclassifier {
field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifier.EntityConfig> CREATOR;
}
public final class TextClassifierEvent implements android.os.Parcelable {
method public int describeContents();
method public int[] getActionIndices();
method public java.lang.String getEntityType();
method public int getEventCategory();
method public android.view.textclassifier.TextClassificationContext getEventContext();
method public int getEventIndex();
method public long getEventTime();
method public int getEventType();
method public android.os.Bundle getExtras();
method public java.lang.String getLanguage();
method public int getRelativeSuggestedWordEndIndex();
method public int getRelativeSuggestedWordStartIndex();
method public int getRelativeWordEndIndex();
method public int getRelativeWordStartIndex();
method public java.lang.String getResultId();
method public void writeToParcel(android.os.Parcel, int);
field public static final int CATEGORY_CONVERSATION_ACTIONS = 3; // 0x3
field public static final int CATEGORY_LANGUAGE_DETECTION = 4; // 0x4
field public static final int CATEGORY_LINKIFY = 2; // 0x2
field public static final int CATEGORY_SELECTION = 1; // 0x1
field public static final int CATEGORY_UNDEFINED = 0; // 0x0
field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifierEvent> CREATOR;
field public static final int TYPE_ACTIONS_SHOWN = 6; // 0x6
field public static final int TYPE_AUTO_SELECTION = 5; // 0x5
field public static final int TYPE_COPY_ACTION = 9; // 0x9
field public static final int TYPE_CUT_ACTION = 11; // 0xb
field public static final int TYPE_LINK_CLICKED = 7; // 0x7
field public static final int TYPE_MANUAL_REPLY = 19; // 0x13
field public static final int TYPE_OTHER_ACTION = 16; // 0x10
field public static final int TYPE_OVERTYPE = 8; // 0x8
field public static final int TYPE_PASTE_ACTION = 10; // 0xa
field public static final int TYPE_SELECTION_DESTROYED = 15; // 0xf
field public static final int TYPE_SELECTION_DRAG = 14; // 0xe
field public static final int TYPE_SELECTION_MODIFIED = 2; // 0x2
field public static final int TYPE_SELECTION_RESET = 18; // 0x12
field public static final int TYPE_SELECTION_STARTED = 1; // 0x1
field public static final int TYPE_SELECT_ALL = 17; // 0x11
field public static final int TYPE_SHARE_ACTION = 12; // 0xc
field public static final int TYPE_SMART_ACTION = 13; // 0xd
field public static final int TYPE_SMART_SELECTION_MULTI = 4; // 0x4
field public static final int TYPE_SMART_SELECTION_SINGLE = 3; // 0x3
field public static final int TYPE_UNDEFINED = 0; // 0x0
}
public static final class TextClassifierEvent.Builder {
ctor public TextClassifierEvent.Builder(int, int);
method public android.view.textclassifier.TextClassifierEvent build();
method public android.view.textclassifier.TextClassifierEvent.Builder setActionIndices(int...);
method public android.view.textclassifier.TextClassifierEvent.Builder setEntityType(java.lang.String);
method public android.view.textclassifier.TextClassifierEvent.Builder setEventContext(android.view.textclassifier.TextClassificationContext);
method public android.view.textclassifier.TextClassifierEvent.Builder setEventIndex(int);
method public android.view.textclassifier.TextClassifierEvent.Builder setEventTime(long);
method public android.view.textclassifier.TextClassifierEvent.Builder setExtras(android.os.Bundle);
method public android.view.textclassifier.TextClassifierEvent.Builder setLanguage(java.lang.String);
method public android.view.textclassifier.TextClassifierEvent.Builder setRelativeSuggestedWordEndIndex(int);
method public android.view.textclassifier.TextClassifierEvent.Builder setRelativeSuggestedWordStartIndex(int);
method public android.view.textclassifier.TextClassifierEvent.Builder setRelativeWordEndIndex(int);
method public android.view.textclassifier.TextClassifierEvent.Builder setRelativeWordStartIndex(int);
method public android.view.textclassifier.TextClassifierEvent.Builder setResultId(java.lang.String);
}
public final class TextLanguage implements android.os.Parcelable {
method public int describeContents();
method public float getConfidenceScore(android.icu.util.ULocale);

View File

@@ -5357,9 +5357,10 @@ package android.service.textclassifier {
method public void onDestroyTextClassificationSession(android.view.textclassifier.TextClassificationSessionId);
method public void onDetectLanguage(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.TextLanguage.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLanguage>);
method public abstract void onGenerateLinks(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.TextLinks.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLinks>);
method public void onSelectionEvent(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.SelectionEvent);
method public deprecated void onSelectionEvent(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.SelectionEvent);
method public void onSuggestConversationActions(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.ConversationActions.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.ConversationActions>);
method public abstract void onSuggestSelection(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.TextSelection.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextSelection>);
method public void onTextClassifierEvent(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.TextClassifierEvent);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.textclassifier.TextClassifierService";
}

View File

@@ -26,6 +26,7 @@ import android.view.textclassifier.SelectionEvent;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationSessionId;
import android.view.textclassifier.TextClassifierEvent;
import android.view.textclassifier.TextLinks;
import android.view.textclassifier.TextLanguage;
import android.view.textclassifier.TextSelection;
@@ -52,10 +53,15 @@ oneway interface ITextClassifierService {
in TextLinks.Request request,
in ITextLinksCallback callback);
// TODO: Remove
void onSelectionEvent(
in TextClassificationSessionId sessionId,
in SelectionEvent event);
void onTextClassifierEvent(
in TextClassificationSessionId sessionId,
in TextClassifierEvent event);
void onCreateTextClassificationSession(
in TextClassificationContext context,
in TextClassificationSessionId sessionId);

View File

@@ -39,6 +39,7 @@ import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassificationSessionId;
import android.view.textclassifier.TextClassifier;
import android.view.textclassifier.TextClassifierEvent;
import android.view.textclassifier.TextLanguage;
import android.view.textclassifier.TextLinks;
import android.view.textclassifier.TextSelection;
@@ -191,6 +192,15 @@ public abstract class TextClassifierService extends Service {
TextClassifierService.this.onSelectionEvent(sessionId, event);
}
/** {@inheritDoc} */
@Override
public void onTextClassifierEvent(
TextClassificationSessionId sessionId,
TextClassifierEvent event) {
Preconditions.checkNotNull(event);
TextClassifierService.this.onTextClassifierEvent(sessionId, event);
}
/** {@inheritDoc} */
@Override
public void onDetectLanguage(
@@ -368,10 +378,27 @@ public abstract class TextClassifierService extends Service {
*
* @param sessionId the session id
* @param event the selection event
* @deprecated
* Use {@link #onTextClassifierEvent(TextClassificationSessionId, TextClassifierEvent)}
* instead
*/
@Deprecated
public void onSelectionEvent(
@Nullable TextClassificationSessionId sessionId, @NonNull SelectionEvent event) {}
/**
* Writes the TextClassifier event.
* This is called when a TextClassifier event occurs. e.g. user changed selection,
* smart selection happened, or a link was clicked.
*
* <p>The default implementation ignores the event.
*
* @param sessionId the session id
* @param event the TextClassifier event
*/
public void onTextClassifierEvent(
@Nullable TextClassificationSessionId sessionId, @NonNull TextClassifierEvent event) {}
/**
* Creates a new text classification session for the specified context.
*

View File

@@ -139,17 +139,21 @@ public final class ConversationActions implements Parcelable {
*/
public static final String HINT_FOR_NOTIFICATION = "notification";
private List<ConversationAction> mConversationActions;
private final List<ConversationAction> mConversationActions;
private final String mId;
/** Constructs a {@link ConversationActions} object. */
public ConversationActions(@NonNull List<ConversationAction> conversationActions) {
public ConversationActions(
@NonNull List<ConversationAction> conversationActions, @Nullable String id) {
mConversationActions =
Collections.unmodifiableList(Preconditions.checkNotNull(conversationActions));
mId = id;
}
private ConversationActions(Parcel in) {
mConversationActions =
Collections.unmodifiableList(in.createTypedArrayList(ConversationAction.CREATOR));
mId = in.readString();
}
@Override
@@ -160,14 +164,26 @@ public final class ConversationActions implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeTypedList(mConversationActions);
parcel.writeString(mId);
}
/** Returns an immutable list of {@link ConversationAction} objects. */
/**
* Returns an immutable list of {@link ConversationAction} objects, which are ordered from high
* confidence to low confidence.
*/
@NonNull
public List<ConversationAction> getConversationActions() {
return mConversationActions;
}
/**
* Returns the id, if one exists, for this object.
*/
@Nullable
public String getId() {
return mId;
}
/** Represents the action suggested by a {@link TextClassifier} on a given conversation. */
public static final class ConversationAction implements Parcelable {
@@ -678,35 +694,37 @@ public final class ConversationActions implements Parcelable {
private final List<String> mHints;
@Nullable
private String mCallingPackageName;
@Nullable
private final String mConversationId;
private Request(
@NonNull List<Message> conversation,
@NonNull TypeConfig typeConfig,
int maxSuggestions,
String conversationId,
@Nullable @Hint List<String> hints) {
mConversation = Preconditions.checkNotNull(conversation);
mTypeConfig = Preconditions.checkNotNull(typeConfig);
mMaxSuggestions = maxSuggestions;
mConversationId = conversationId;
mHints = hints;
}
private static Request readFromParcel(Parcel in) {
List<Message> conversation = new ArrayList<>();
in.readParcelableList(conversation, null);
TypeConfig typeConfig = in.readParcelable(null);
int maxSuggestions = in.readInt();
String conversationId = in.readString();
List<String> hints = new ArrayList<>();
in.readStringList(hints);
String callingPackageName = in.readString();
Request request = new Request(
conversation,
typeConfig,
maxSuggestions,
conversationId,
hints);
request.setCallingPackageName(callingPackageName);
return request;
@@ -717,6 +735,7 @@ public final class ConversationActions implements Parcelable {
parcel.writeParcelableList(mConversation, flags);
parcel.writeParcelable(mTypeConfig, flags);
parcel.writeInt(mMaxSuggestions);
parcel.writeString(mConversationId);
parcel.writeStringList(mHints);
parcel.writeString(mCallingPackageName);
}
@@ -759,6 +778,16 @@ public final class ConversationActions implements Parcelable {
return mMaxSuggestions;
}
/**
* Return an unique identifier of the conversation that is generating actions for. This
* identifier is unique within the calling package only, so use it with
* {@link #getCallingPackageName()}.
*/
@Nullable
public String getConversationId() {
return mConversationId;
}
/** Returns an immutable list of hints */
@Nullable
@Hint
@@ -794,6 +823,8 @@ public final class ConversationActions implements Parcelable {
private TypeConfig mTypeConfig;
private int mMaxSuggestions;
@Nullable
private String mConversationId;
@Nullable
@Hint
private List<String> mHints;
@@ -823,7 +854,8 @@ public final class ConversationActions implements Parcelable {
return this;
}
/** Sets the maximum number of suggestions you want.
/**
* Sets the maximum number of suggestions you want.
* <p>
* Value 0 means no restriction.
*/
@@ -833,6 +865,15 @@ public final class ConversationActions implements Parcelable {
return this;
}
/**
* Sets an unique identifier of the conversation that is generating actions for.
*/
@NonNull
public Builder setConversationId(@Nullable String conversationId) {
mConversationId = conversationId;
return this;
}
/** Builds the {@link Request} object. */
@NonNull
public Request build() {
@@ -840,6 +881,7 @@ public final class ConversationActions implements Parcelable {
Collections.unmodifiableList(mConversation),
mTypeConfig == null ? new TypeConfig.Builder().build() : mTypeConfig,
mMaxSuggestions,
mConversationId,
mHints == null
? Collections.emptyList()
: Collections.unmodifiableList(mHints));

View File

@@ -146,6 +146,18 @@ public final class SystemTextClassifier implements TextClassifier {
}
}
@Override
public void onTextClassifierEvent(@NonNull TextClassifierEvent event) {
Preconditions.checkNotNull(event);
Utils.checkMainThread();
try {
mManagerService.onTextClassifierEvent(mSessionId, event);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Error reporting textclassifier event.", e);
}
}
@Override
public TextLanguage detectLanguage(TextLanguage.Request request) {
Preconditions.checkNotNull(request);

View File

@@ -127,32 +127,34 @@ public interface TextClassifier {
@StringDef({WIDGET_TYPE_TEXTVIEW, WIDGET_TYPE_EDITTEXT, WIDGET_TYPE_UNSELECTABLE_TEXTVIEW,
WIDGET_TYPE_WEBVIEW, WIDGET_TYPE_EDIT_WEBVIEW, WIDGET_TYPE_CUSTOM_TEXTVIEW,
WIDGET_TYPE_CUSTOM_EDITTEXT, WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW,
WIDGET_TYPE_UNKNOWN})
WIDGET_TYPE_NOTIFICATION, WIDGET_TYPE_UNKNOWN})
@interface WidgetType {}
/** The widget involved in the text classification session is a standard
/** The widget involved in the text classification context is a standard
* {@link android.widget.TextView}. */
String WIDGET_TYPE_TEXTVIEW = "textview";
/** The widget involved in the text classification session is a standard
/** The widget involved in the text classification context is a standard
* {@link android.widget.EditText}. */
String WIDGET_TYPE_EDITTEXT = "edittext";
/** The widget involved in the text classification session is a standard non-selectable
/** The widget involved in the text classification context is a standard non-selectable
* {@link android.widget.TextView}. */
String WIDGET_TYPE_UNSELECTABLE_TEXTVIEW = "nosel-textview";
/** The widget involved in the text classification session is a standard
/** The widget involved in the text classification context is a standard
* {@link android.webkit.WebView}. */
String WIDGET_TYPE_WEBVIEW = "webview";
/** The widget involved in the text classification session is a standard editable
/** The widget involved in the text classification context is a standard editable
* {@link android.webkit.WebView}. */
String WIDGET_TYPE_EDIT_WEBVIEW = "edit-webview";
/** The widget involved in the text classification session is a custom text widget. */
/** The widget involved in the text classification context is a custom text widget. */
String WIDGET_TYPE_CUSTOM_TEXTVIEW = "customview";
/** The widget involved in the text classification session is a custom editable text widget. */
/** The widget involved in the text classification context is a custom editable text widget. */
String WIDGET_TYPE_CUSTOM_EDITTEXT = "customedit";
/** The widget involved in the text classification session is a custom non-selectable text
/** The widget involved in the text classification context is a custom non-selectable text
* widget. */
String WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
/** The widget involved in the text classification session is of an unknown/unspecified type. */
/** The widget involved in the text classification context is a notification */
String WIDGET_TYPE_NOTIFICATION = "notification";
/** The widget involved in the text classification context is of an unknown/unspecified type. */
String WIDGET_TYPE_UNKNOWN = "unknown";
/**
@@ -349,16 +351,30 @@ public interface TextClassifier {
@NonNull ConversationActions.Request request) {
Preconditions.checkNotNull(request);
Utils.checkMainThread();
return new ConversationActions(Collections.emptyList());
return new ConversationActions(Collections.emptyList(), null);
}
/**
* <strong>NOTE: </strong>Use {@link #onTextClassifierEvent(TextClassifierEvent)} instead.
* <p>
* Reports a selection event.
*
* <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
* throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
*/
default void onSelectionEvent(@NonNull SelectionEvent event) {}
default void onSelectionEvent(@NonNull SelectionEvent event) {
// TODO: Consider rerouting to onTextClassifierEvent()
}
/**
* Reports a text classifier event.
* <p>
* <strong>NOTE: </strong>Call on a worker thread.
*
* @throws IllegalStateException if this TextClassifier has been destroyed.
* @see #isDestroyed()
*/
default void onTextClassifierEvent(@NonNull TextClassifierEvent event) {}
/**
* Destroys this TextClassifier.

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2018 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;
parcelable TextClassifierEvent;

View File

@@ -0,0 +1,501 @@
/*
* Copyright 2018 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.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* A text classifier event.
*/
// TODO: Comprehensive javadoc.
public final class TextClassifierEvent implements Parcelable {
public static final Creator<TextClassifierEvent> CREATOR = new Creator<TextClassifierEvent>() {
@Override
public TextClassifierEvent createFromParcel(Parcel in) {
return readFromParcel(in);
}
@Override
public TextClassifierEvent[] newArray(int size) {
return new TextClassifierEvent[size];
}
};
/** @hide **/
@Retention(RetentionPolicy.SOURCE)
@IntDef({CATEGORY_UNDEFINED, CATEGORY_SELECTION, CATEGORY_LINKIFY,
CATEGORY_CONVERSATION_ACTIONS, CATEGORY_LANGUAGE_DETECTION})
public @interface Category {
// For custom event categories, use range 1000+.
}
/** Undefined category */
public static final int CATEGORY_UNDEFINED = 0;
/** Smart selection */
public static final int CATEGORY_SELECTION = 1;
/** Linkify */
public static final int CATEGORY_LINKIFY = 2;
/** Conversation actions */
public static final int CATEGORY_CONVERSATION_ACTIONS = 3;
/** Language detection */
public static final int CATEGORY_LANGUAGE_DETECTION = 4;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef({TYPE_UNDEFINED, TYPE_SELECTION_STARTED, TYPE_SELECTION_MODIFIED,
TYPE_SMART_SELECTION_SINGLE, TYPE_SMART_SELECTION_MULTI, TYPE_AUTO_SELECTION,
TYPE_ACTIONS_SHOWN, TYPE_LINK_CLICKED, TYPE_OVERTYPE, TYPE_COPY_ACTION,
TYPE_PASTE_ACTION, TYPE_CUT_ACTION, TYPE_SHARE_ACTION, TYPE_SMART_ACTION,
TYPE_SELECTION_DRAG, TYPE_SELECTION_DESTROYED, TYPE_OTHER_ACTION, TYPE_SELECT_ALL,
TYPE_SELECTION_RESET, TYPE_MANUAL_REPLY})
public @interface Type {
// For custom event types, use range 1,000,000+.
}
/** User started a new selection. */
public static final int TYPE_UNDEFINED = 0;
/** User started a new selection. */
public static final int TYPE_SELECTION_STARTED = 1;
/** User modified an existing selection. */
public static final int TYPE_SELECTION_MODIFIED = 2;
/** Smart selection triggered for a single token (word). */
public static final int TYPE_SMART_SELECTION_SINGLE = 3;
/** Smart selection triggered spanning multiple tokens (words). */
public static final int TYPE_SMART_SELECTION_MULTI = 4;
/** Something else other than user or the default TextClassifier triggered a selection. */
public static final int TYPE_AUTO_SELECTION = 5;
/** Smart actions shown to the user. */
public static final int TYPE_ACTIONS_SHOWN = 6;
/** User clicked a link. */
public static final int TYPE_LINK_CLICKED = 7;
/** User typed over the selection. */
public static final int TYPE_OVERTYPE = 8;
/** User clicked on Copy action. */
public static final int TYPE_COPY_ACTION = 9;
/** User clicked on Paste action. */
public static final int TYPE_PASTE_ACTION = 10;
/** User clicked on Cut action. */
public static final int TYPE_CUT_ACTION = 11;
/** User clicked on Share action. */
public static final int TYPE_SHARE_ACTION = 12;
/** User clicked on a Smart action. */
public static final int TYPE_SMART_ACTION = 13;
/** User dragged+dropped the selection. */
public static final int TYPE_SELECTION_DRAG = 14;
/** Selection is destroyed. */
public static final int TYPE_SELECTION_DESTROYED = 15;
/** User clicked on a custom action. */
public static final int TYPE_OTHER_ACTION = 16;
/** User clicked on Select All action */
public static final int TYPE_SELECT_ALL = 17;
/** User reset the smart selection. */
public static final int TYPE_SELECTION_RESET = 18;
/** User composed a reply. */
public static final int TYPE_MANUAL_REPLY = 19;
@Category private final int mEventCategory;
@Type private final int mEventType;
@Nullable private final String mEntityType;
@Nullable private final TextClassificationContext mEventContext;
@Nullable private final String mResultId;
private final int mEventIndex;
private final long mEventTime;
private final Bundle mExtras;
// Smart selection.
private final int mRelativeWordStartIndex;
private final int mRelativeWordEndIndex;
private final int mRelativeSuggestedWordStartIndex;
private final int mRelativeSuggestedWordEndIndex;
// Smart action.
private final int[] mActionIndices;
// Language detection.
@Nullable private final String mLanguage;
private TextClassifierEvent(
int eventCategory,
int eventType,
String entityType,
TextClassificationContext eventContext,
String resultId,
int eventIndex,
long eventTime,
Bundle extras,
int relativeWordStartIndex,
int relativeWordEndIndex,
int relativeSuggestedWordStartIndex,
int relativeSuggestedWordEndIndex,
int[] actionIndex,
String language) {
mEventCategory = eventCategory;
mEventType = eventType;
mEntityType = entityType;
mEventContext = eventContext;
mResultId = resultId;
mEventIndex = eventIndex;
mEventTime = eventTime;
mExtras = extras;
mRelativeWordStartIndex = relativeWordStartIndex;
mRelativeWordEndIndex = relativeWordEndIndex;
mRelativeSuggestedWordStartIndex = relativeSuggestedWordStartIndex;
mRelativeSuggestedWordEndIndex = relativeSuggestedWordEndIndex;
mActionIndices = actionIndex;
mLanguage = language;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mEventCategory);
dest.writeInt(mEventType);
dest.writeString(mEntityType);
dest.writeParcelable(mEventContext, flags);
dest.writeString(mResultId);
dest.writeInt(mEventIndex);
dest.writeLong(mEventTime);
dest.writeBundle(mExtras);
dest.writeInt(mRelativeWordStartIndex);
dest.writeInt(mRelativeWordEndIndex);
dest.writeInt(mRelativeSuggestedWordStartIndex);
dest.writeInt(mRelativeSuggestedWordEndIndex);
dest.writeIntArray(mActionIndices);
dest.writeString(mLanguage);
}
private static TextClassifierEvent readFromParcel(Parcel in) {
return new TextClassifierEvent(
/* eventCategory= */ in.readInt(),
/* eventType= */ in.readInt(),
/* entityType= */ in.readString(),
/* eventContext= */ in.readParcelable(null),
/* resultId= */ in.readString(),
/* eventIndex= */ in.readInt(),
/* eventTime= */ in.readLong(),
/* extras= */ in.readBundle(),
/* relativeWordStartIndex= */ in.readInt(),
/* relativeWordEndIndex= */ in.readInt(),
/* relativeSuggestedWordStartIndex= */ in.readInt(),
/* relativeSuggestedWordEndIndex= */ in.readInt(),
/* actionIndices= */ in.createIntArray(),
/* language= */ in.readString());
}
/**
* Returns the event category. e.g. {@link #CATEGORY_SELECTION}.
*/
@Category
public int getEventCategory() {
return mEventCategory;
}
/**
* Returns the event type. e.g. {@link #TYPE_SELECTION_STARTED}.
*/
@Type
public int getEventType() {
return mEventType;
}
/**
* Returns the entity type. e.g. {@link TextClassifier#TYPE_ADDRESS}.
*/
@Nullable
public String getEntityType() {
return mEntityType;
}
/**
* Returns the event context.
*/
@Nullable
public TextClassificationContext getEventContext() {
return mEventContext;
}
/**
* Returns the id of the text classifier result related to this event.
*/
@Nullable
public String getResultId() {
return mResultId;
}
/**
* Returns the index of this event in the series of event it belongs to.
*/
public int getEventIndex() {
return mEventIndex;
}
/**
* Returns the time this event occurred. This is the number of milliseconds since
* January 1, 1970, 00:00:00 GMT. 0 indicates not set.
*/
public long getEventTime() {
return mEventTime;
}
/**
* Returns a bundle containing non-structured extra information about this event.
*
* <p><b>NOTE: </b>Do not modify this bundle.
*/
@NonNull
public Bundle getExtras() {
return mExtras;
}
/**
* For smart selection. Returns the relative word index of the start of the selection.
*/
public int getRelativeWordStartIndex() {
return mRelativeWordStartIndex;
}
/**
* For smart selection. Returns the relative word (exclusive) index of the end of the selection.
*/
public int getRelativeWordEndIndex() {
return mRelativeWordEndIndex;
}
/**
* For smart selection. Returns the relative word index of the start of the smart selection.
*/
public int getRelativeSuggestedWordStartIndex() {
return mRelativeSuggestedWordStartIndex;
}
/**
* For smart selection. Returns the relative word (exclusive) index of the end of the
* smart selection.
*/
public int getRelativeSuggestedWordEndIndex() {
return mRelativeSuggestedWordEndIndex;
}
/**
* Returns the indices of the actions relating to this event.
* Actions are usually returned by the text classifier in priority order with the most
* preferred action at index 0. This list gives an indication of the position of the actions
* that are being reported.
*/
@NonNull
public int[] getActionIndices() {
return mActionIndices;
}
/**
* For language detection. Returns the language tag for the detected locale.
* @see java.util.Locale#forLanguageTag(String).
*/
@Nullable
public String getLanguage() {
return mLanguage;
}
/**
* Builder to build a text classifier event.
*/
public static final class Builder {
private final int mEventCategory;
private final int mEventType;
@Nullable private String mEntityType;
@Nullable private TextClassificationContext mEventContext;
@Nullable private String mResultId;
private int mEventIndex;
private long mEventTime;
@Nullable private Bundle mExtras;
private int mRelativeWordStartIndex;
private int mRelativeWordEndIndex;
private int mRelativeSuggestedWordStartIndex;
private int mRelativeSuggestedWordEndIndex;
private int[] mActionIndices = new int[0];
@Nullable private String mLanguage;
/**
* Creates a builder for building {@link TextClassifierEvent}s.
*
* @param eventCategory The event category. e.g. {@link #CATEGORY_SELECTION}
* @param eventType The event type. e.g. {@link #TYPE_SELECTION_STARTED}
*/
public Builder(@Category int eventCategory, @Type int eventType) {
mEventCategory = eventCategory;
mEventType = eventType;
}
/**
* Sets the entity type. e.g. {@link TextClassifier#TYPE_ADDRESS}.
*/
@NonNull
public Builder setEntityType(@Nullable String entityType) {
mEntityType = entityType;
return this;
}
/**
* Sets the event context.
*/
@NonNull
public Builder setEventContext(@Nullable TextClassificationContext eventContext) {
mEventContext = eventContext;
return this;
}
/**
* Sets the id of the text classifier result related to this event.
*/
@NonNull
public Builder setResultId(@Nullable String resultId) {
mResultId = resultId;
return this;
}
/**
* Sets the index of this events in the series of events it belongs to.
*/
@NonNull
public Builder setEventIndex(int eventIndex) {
mEventIndex = eventIndex;
return this;
}
/**
* Sets the time this event occurred. This is the number of milliseconds since
* January 1, 1970, 00:00:00 GMT. 0 indicates not set.
*/
@NonNull
public Builder setEventTime(long eventTime) {
mEventTime = eventTime;
return this;
}
/**
* Sets a bundle containing non-structured extra information about the event.
*
* <p><b>NOTE: </b>Prefer to set only immutable values on the bundle otherwise, avoid
* updating the internals of this bundle as it may have unexpected consequences on the
* clients of the built event object. For similar reasons, avoid depending on mutable
* objects in this bundle.
*/
@NonNull
public Builder setExtras(@NonNull Bundle extras) {
mExtras = Preconditions.checkNotNull(extras);
return this;
}
/**
* For smart selection. Sets the relative word index of the start of the selection.
*/
@NonNull
public Builder setRelativeWordStartIndex(int relativeWordStartIndex) {
mRelativeWordStartIndex = relativeWordStartIndex;
return this;
}
/**
* For smart selection. Sets the relative word (exclusive) index of the end of the
* selection.
*/
@NonNull
public Builder setRelativeWordEndIndex(int relativeWordEndIndex) {
mRelativeWordEndIndex = relativeWordEndIndex;
return this;
}
/**
* For smart selection. Sets the relative word index of the start of the smart selection.
*/
@NonNull
public Builder setRelativeSuggestedWordStartIndex(int relativeSuggestedWordStartIndex) {
mRelativeSuggestedWordStartIndex = relativeSuggestedWordStartIndex;
return this;
}
/**
* For smart selection. Sets the relative word (exclusive) index of the end of the
* smart selection.
*/
@NonNull
public Builder setRelativeSuggestedWordEndIndex(int relativeSuggestedWordEndIndex) {
mRelativeSuggestedWordEndIndex = relativeSuggestedWordEndIndex;
return this;
}
/**
* Sets the indices of the actions involved in this event. Actions are usually returned by
* the text classifier in priority order with the most preferred action at index 0.
* This index gives an indication of the position of the action that is being reported.
*/
@NonNull
public Builder setActionIndices(@NonNull int... actionIndices) {
mActionIndices = new int[actionIndices.length];
System.arraycopy(actionIndices, 0, mActionIndices, 0, actionIndices.length);
return this;
}
/**
* For language detection. Sets the language tag for the detected locale.
* @see java.util.Locale#forLanguageTag(String).
*/
@NonNull
public Builder setLanguage(@Nullable String language) {
mLanguage = language;
return this;
}
/**
* Builds and returns a text classifier event.
*/
@NonNull
public TextClassifierEvent build() {
mExtras = mExtras == null ? Bundle.EMPTY : mExtras;
return new TextClassifierEvent(
mEventCategory,
mEventType,
mEntityType,
mEventContext,
mResultId,
mEventIndex,
mEventTime,
mExtras,
mRelativeWordStartIndex,
mRelativeWordEndIndex,
mRelativeSuggestedWordStartIndex,
mRelativeSuggestedWordEndIndex,
mActionIndices,
mLanguage);
}
// TODO: Add build(boolean validate).
}
}

View File

@@ -341,6 +341,11 @@ public final class TextClassifierImpl implements TextClassifier {
}
}
@Override
public void onTextClassifierEvent(@NonNull TextClassifierEvent event) {
// TODO: Implement.
}
/** @inheritDoc */
@Override
public TextLanguage detectLanguage(@NonNull TextLanguage.Request request) {
@@ -387,7 +392,10 @@ public final class TextClassifierImpl implements TextClassifier {
Collection<String> expectedTypes = resolveActionTypesFromRequest(request);
List<ConversationActions.ConversationAction> conversationActions = new ArrayList<>();
int maxSuggestions = Math.min(request.getMaxSuggestions(), nativeSuggestions.length);
int maxSuggestions = nativeSuggestions.length;
if (request.getMaxSuggestions() > 0) {
maxSuggestions = Math.min(request.getMaxSuggestions(), nativeSuggestions.length);
}
for (int i = 0; i < maxSuggestions; i++) {
ActionsSuggestionsModel.ActionSuggestion nativeSuggestion = nativeSuggestions[i];
String actionType = nativeSuggestion.getActionType();
@@ -400,7 +408,7 @@ public final class TextClassifierImpl implements TextClassifier {
.setConfidenceScore(nativeSuggestion.getScore())
.build());
}
return new ConversationActions(conversationActions);
return new ConversationActions(conversationActions, /*id*/ null);
} catch (Throwable t) {
// Avoid throwing from this method. Log the error.
Log.e(LOG_TAG, "Error suggesting conversation actions.", t);

View File

@@ -18,7 +18,6 @@ package android.view.textclassifier;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -375,7 +374,7 @@ public class TextClassifierTest {
ConversationActions.Message message =
new ConversationActions.Message.Builder(
ConversationActions.Message.PERSON_USER_REMOTE)
.setText("Hello")
.setText("Where are you?")
.build();
ConversationActions.TypeConfig typeConfig =
new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false)
@@ -384,19 +383,44 @@ public class TextClassifierTest {
.build();
ConversationActions.Request request =
new ConversationActions.Request.Builder(Collections.singletonList(message))
.setMaxSuggestions(3)
.setMaxSuggestions(1)
.setTypeConfig(typeConfig)
.build();
ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
assertTrue(conversationActions.getConversationActions().size() > 0);
assertTrue(conversationActions.getConversationActions().size() <= 3);
assertTrue(conversationActions.getConversationActions().size() == 1);
for (ConversationActions.ConversationAction conversationAction :
conversationActions.getConversationActions()) {
assertEquals(conversationAction.getType(), ConversationActions.TYPE_TEXT_REPLY);
assertNotNull(conversationAction.getTextReply());
assertTrue(conversationAction.getConfidenceScore() > 0);
assertTrue(conversationAction.getConfidenceScore() <= 1);
assertThat(conversationAction,
isConversationAction(ConversationActions.TYPE_TEXT_REPLY));
}
}
@Test
public void testSuggestConversationActions_textReplyOnly_noMax() {
if (isTextClassifierDisabled()) return;
ConversationActions.Message message =
new ConversationActions.Message.Builder(
ConversationActions.Message.PERSON_USER_REMOTE)
.setText("Where are you?")
.build();
ConversationActions.TypeConfig typeConfig =
new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false)
.setIncludedTypes(
Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY))
.build();
ConversationActions.Request request =
new ConversationActions.Request.Builder(Collections.singletonList(message))
.setTypeConfig(typeConfig)
.build();
ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
assertTrue(conversationActions.getConversationActions().size() > 1);
for (ConversationActions.ConversationAction conversationAction :
conversationActions.getConversationActions()) {
assertThat(conversationAction,
isConversationAction(ConversationActions.TYPE_TEXT_REPLY));
}
}
@@ -498,4 +522,36 @@ public class TextClassifierTest {
}
};
}
private static Matcher<ConversationActions.ConversationAction> isConversationAction(
String actionType) {
return new BaseMatcher<ConversationActions.ConversationAction>() {
@Override
public boolean matches(Object o) {
if (!(o instanceof ConversationActions.ConversationAction)) {
return false;
}
ConversationActions.ConversationAction conversationAction =
(ConversationActions.ConversationAction) o;
if (!actionType.equals(conversationAction.getType())) {
return false;
}
if (ConversationActions.TYPE_TEXT_REPLY.equals(actionType)) {
if (conversationAction.getTextReply() == null) {
return false;
}
}
if (conversationAction.getConfidenceScore() < 0
|| conversationAction.getConfidenceScore() > 1) {
return false;
}
return true;
}
@Override
public void describeTo(Description description) {
description.appendText("actionType=").appendValue(actionType);
}
};
}
}

View File

@@ -75,7 +75,7 @@ public class SmartActionHelperTest {
mContext.getSystemService(TextClassificationManager.class)
.setTextClassifier(mTextClassifier);
when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
.thenReturn(new ConversationActions(Collections.emptyList()));
.thenReturn(new ConversationActions(Collections.emptyList(), null));
when(mNotificationEntry.getSbn()).thenReturn(mStatusBarNotification);
// The notification is eligible to have smart suggestions.

View File

@@ -42,6 +42,7 @@ import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassificationSessionId;
import android.view.textclassifier.TextClassifierEvent;
import android.view.textclassifier.TextLanguage;
import android.view.textclassifier.TextLinks;
import android.view.textclassifier.TextSelection;
@@ -214,6 +215,27 @@ public final class TextClassificationManagerService extends ITextClassifierServi
}
}
}
@Override
public void onTextClassifierEvent(
TextClassificationSessionId sessionId,
TextClassifierEvent event) throws RemoteException {
Preconditions.checkNotNull(event);
final String packageName = event.getEventContext() == null
? null
: event.getEventContext().getPackageName();
validateInput(mContext, packageName);
synchronized (mLock) {
UserState userState = getCallingUserStateLocked();
if (userState.isBoundLocked()) {
userState.mService.onTextClassifierEvent(sessionId, event);
} else {
userState.mPendingRequests.add(new PendingRequest(
() -> onTextClassifierEvent(sessionId, event),
null /* onServiceFailure */, null /* binder */, this, userState));
}
}
}
@Override
public void onDetectLanguage(