blob: dc7521296e9f14046e0352e3fc5a759d131b5ea4 [file] [log] [blame]
Tony Makc9d31e22018-10-22 16:17:45 +01001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package android.view.textclassifier;
17
18import static java.lang.annotation.RetentionPolicy.SOURCE;
19
Tony Makc9d31e22018-10-22 16:17:45 +010020import android.annotation.IntRange;
21import android.annotation.NonNull;
22import android.annotation.Nullable;
23import android.annotation.StringDef;
24import android.app.Person;
Tony Makc9d31e22018-10-22 16:17:45 +010025import android.os.Bundle;
26import android.os.Parcel;
27import android.os.Parcelable;
28import android.text.SpannedString;
Tony Makc9d31e22018-10-22 16:17:45 +010029
Abodunrinwa Toki0f138962018-12-03 22:35:10 +000030import com.android.internal.annotations.VisibleForTesting;
Tony Makc9d31e22018-10-22 16:17:45 +010031import com.android.internal.util.Preconditions;
32
33import java.lang.annotation.Retention;
34import java.time.ZonedDateTime;
35import java.time.format.DateTimeFormatter;
36import java.util.ArrayList;
Tony Makc9d31e22018-10-22 16:17:45 +010037import java.util.Collections;
38import java.util.List;
Tony Makc9d31e22018-10-22 16:17:45 +010039
40/**
41 * Represents a list of actions suggested by a {@link TextClassifier} on a given conversation.
42 *
43 * @see TextClassifier#suggestConversationActions(Request)
44 */
45public final class ConversationActions implements Parcelable {
46
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -070047 public static final @android.annotation.NonNull Creator<ConversationActions> CREATOR =
Tony Makc9d31e22018-10-22 16:17:45 +010048 new Creator<ConversationActions>() {
49 @Override
50 public ConversationActions createFromParcel(Parcel in) {
51 return new ConversationActions(in);
52 }
53
54 @Override
55 public ConversationActions[] newArray(int size) {
56 return new ConversationActions[size];
57 }
58 };
59
Tony Makc4359bf2018-12-11 19:38:53 +080060 private final List<ConversationAction> mConversationActions;
61 private final String mId;
Tony Makc9d31e22018-10-22 16:17:45 +010062
63 /** Constructs a {@link ConversationActions} object. */
Tony Makc4359bf2018-12-11 19:38:53 +080064 public ConversationActions(
65 @NonNull List<ConversationAction> conversationActions, @Nullable String id) {
Tony Makc9d31e22018-10-22 16:17:45 +010066 mConversationActions =
67 Collections.unmodifiableList(Preconditions.checkNotNull(conversationActions));
Tony Makc4359bf2018-12-11 19:38:53 +080068 mId = id;
Tony Makc9d31e22018-10-22 16:17:45 +010069 }
70
71 private ConversationActions(Parcel in) {
72 mConversationActions =
73 Collections.unmodifiableList(in.createTypedArrayList(ConversationAction.CREATOR));
Tony Makc4359bf2018-12-11 19:38:53 +080074 mId = in.readString();
Tony Makc9d31e22018-10-22 16:17:45 +010075 }
76
77 @Override
78 public int describeContents() {
79 return 0;
80 }
81
82 @Override
83 public void writeToParcel(Parcel parcel, int flags) {
84 parcel.writeTypedList(mConversationActions);
Tony Makc4359bf2018-12-11 19:38:53 +080085 parcel.writeString(mId);
Tony Makc9d31e22018-10-22 16:17:45 +010086 }
87
Tony Makc4359bf2018-12-11 19:38:53 +080088 /**
89 * Returns an immutable list of {@link ConversationAction} objects, which are ordered from high
90 * confidence to low confidence.
91 */
Tony Makc9d31e22018-10-22 16:17:45 +010092 @NonNull
93 public List<ConversationAction> getConversationActions() {
94 return mConversationActions;
95 }
96
Tony Makc4359bf2018-12-11 19:38:53 +080097 /**
98 * Returns the id, if one exists, for this object.
99 */
100 @Nullable
101 public String getId() {
102 return mId;
103 }
104
Tony Makc9d31e22018-10-22 16:17:45 +0100105 /** Represents a message in the conversation. */
106 public static final class Message implements Parcelable {
Tony Makf99ee172018-11-23 12:14:39 +0000107 /**
108 * Represents the local user.
109 *
Tony Mak82fa8d92018-12-07 17:37:43 +0000110 * @see Builder#Builder(Person)
Tony Makf99ee172018-11-23 12:14:39 +0000111 */
Tony Mak42ab9842019-03-05 15:38:51 +0000112 @NonNull
Tony Mak91daa152019-01-24 16:00:28 +0000113 public static final Person PERSON_USER_SELF =
Tony Makf99ee172018-11-23 12:14:39 +0000114 new Person.Builder()
Tony Mak91daa152019-01-24 16:00:28 +0000115 .setKey("text-classifier-conversation-actions-user-self")
Tony Makf99ee172018-11-23 12:14:39 +0000116 .build();
117
Tony Mak82fa8d92018-12-07 17:37:43 +0000118 /**
119 * Represents the remote user.
120 * <p>
121 * If possible, you are suggested to create a {@link Person} object that can identify
122 * the remote user better, so that the underlying model could differentiate between
123 * different remote users.
124 *
125 * @see Builder#Builder(Person)
126 */
Tony Mak42ab9842019-03-05 15:38:51 +0000127 @NonNull
Tony Mak91daa152019-01-24 16:00:28 +0000128 public static final Person PERSON_USER_OTHERS =
Tony Mak82fa8d92018-12-07 17:37:43 +0000129 new Person.Builder()
Tony Mak91daa152019-01-24 16:00:28 +0000130 .setKey("text-classifier-conversation-actions-user-others")
Tony Mak82fa8d92018-12-07 17:37:43 +0000131 .build();
132
Tony Makc9d31e22018-10-22 16:17:45 +0100133 @Nullable
134 private final Person mAuthor;
135 @Nullable
Tony Mak82fa8d92018-12-07 17:37:43 +0000136 private final ZonedDateTime mReferenceTime;
Tony Makc9d31e22018-10-22 16:17:45 +0100137 @Nullable
138 private final CharSequence mText;
139 @NonNull
140 private final Bundle mExtras;
141
142 private Message(
143 @Nullable Person author,
Tony Mak82fa8d92018-12-07 17:37:43 +0000144 @Nullable ZonedDateTime referenceTime,
Tony Makc9d31e22018-10-22 16:17:45 +0100145 @Nullable CharSequence text,
146 @NonNull Bundle bundle) {
147 mAuthor = author;
Tony Mak82fa8d92018-12-07 17:37:43 +0000148 mReferenceTime = referenceTime;
Tony Makc9d31e22018-10-22 16:17:45 +0100149 mText = text;
150 mExtras = Preconditions.checkNotNull(bundle);
151 }
152
153 private Message(Parcel in) {
154 mAuthor = in.readParcelable(null);
Tony Mak82fa8d92018-12-07 17:37:43 +0000155 mReferenceTime =
Tony Makc9d31e22018-10-22 16:17:45 +0100156 in.readInt() == 0
157 ? null
158 : ZonedDateTime.parse(
159 in.readString(), DateTimeFormatter.ISO_ZONED_DATE_TIME);
160 mText = in.readCharSequence();
161 mExtras = in.readBundle();
162 }
163
164 @Override
165 public void writeToParcel(Parcel parcel, int flags) {
166 parcel.writeParcelable(mAuthor, flags);
Tony Mak82fa8d92018-12-07 17:37:43 +0000167 parcel.writeInt(mReferenceTime != null ? 1 : 0);
168 if (mReferenceTime != null) {
169 parcel.writeString(mReferenceTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
Tony Makc9d31e22018-10-22 16:17:45 +0100170 }
171 parcel.writeCharSequence(mText);
172 parcel.writeBundle(mExtras);
173 }
174
175 @Override
176 public int describeContents() {
177 return 0;
178 }
179
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700180 public static final @android.annotation.NonNull Creator<Message> CREATOR =
Tony Makc9d31e22018-10-22 16:17:45 +0100181 new Creator<Message>() {
182 @Override
183 public Message createFromParcel(Parcel in) {
184 return new Message(in);
185 }
186
187 @Override
188 public Message[] newArray(int size) {
189 return new Message[size];
190 }
191 };
192
193 /** Returns the person that composed the message. */
Tony Mak82fa8d92018-12-07 17:37:43 +0000194 @NonNull
Tony Makc9d31e22018-10-22 16:17:45 +0100195 public Person getAuthor() {
196 return mAuthor;
197 }
198
Tony Mak82fa8d92018-12-07 17:37:43 +0000199 /**
200 * Returns the reference time of the message, for example it could be the compose or send
201 * time of this message.
202 */
Tony Makc9d31e22018-10-22 16:17:45 +0100203 @Nullable
Tony Mak82fa8d92018-12-07 17:37:43 +0000204 public ZonedDateTime getReferenceTime() {
205 return mReferenceTime;
Tony Makc9d31e22018-10-22 16:17:45 +0100206 }
207
208 /** Returns the text of the message. */
209 @Nullable
210 public CharSequence getText() {
211 return mText;
212 }
213
214 /**
215 * Returns the extended data related to this conversation action.
216 *
217 * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
218 * prefer to hold a reference to the returned bundle rather than frequently calling this
219 * method.
220 */
221 @NonNull
222 public Bundle getExtras() {
223 return mExtras.deepCopy();
224 }
225
226 /** Builder class to construct a {@link Message} */
227 public static final class Builder {
228 @Nullable
229 private Person mAuthor;
230 @Nullable
Tony Mak82fa8d92018-12-07 17:37:43 +0000231 private ZonedDateTime mReferenceTime;
Tony Makc9d31e22018-10-22 16:17:45 +0100232 @Nullable
233 private CharSequence mText;
234 @Nullable
235 private Bundle mExtras;
236
Tony Makf99ee172018-11-23 12:14:39 +0000237 /**
Tony Mak82fa8d92018-12-07 17:37:43 +0000238 * Constructs a builder.
239 *
Tony Mak91daa152019-01-24 16:00:28 +0000240 * @param author the person that composed the message, use {@link #PERSON_USER_SELF}
Tony Mak82fa8d92018-12-07 17:37:43 +0000241 * to represent the local user. If it is not possible to identify the
242 * remote user that the local user is conversing with, use
Tony Mak91daa152019-01-24 16:00:28 +0000243 * {@link #PERSON_USER_OTHERS} to represent a remote user.
Tony Makf99ee172018-11-23 12:14:39 +0000244 */
Tony Mak82fa8d92018-12-07 17:37:43 +0000245 public Builder(@NonNull Person author) {
246 mAuthor = Preconditions.checkNotNull(author);
Tony Makc9d31e22018-10-22 16:17:45 +0100247 }
248
Tony Mak82fa8d92018-12-07 17:37:43 +0000249 /** Sets the text of this message. */
Tony Makc9d31e22018-10-22 16:17:45 +0100250 @NonNull
251 public Builder setText(@Nullable CharSequence text) {
252 mText = text;
253 return this;
254 }
255
Tony Mak82fa8d92018-12-07 17:37:43 +0000256 /**
257 * Sets the reference time of this message, for example it could be the compose or send
258 * time of this message.
259 */
Tony Makc9d31e22018-10-22 16:17:45 +0100260 @NonNull
Tony Mak82fa8d92018-12-07 17:37:43 +0000261 public Builder setReferenceTime(@Nullable ZonedDateTime referenceTime) {
262 mReferenceTime = referenceTime;
Tony Makc9d31e22018-10-22 16:17:45 +0100263 return this;
264 }
265
266 /** Sets a set of extended data to the message. */
267 @NonNull
268 public Builder setExtras(@Nullable Bundle bundle) {
269 this.mExtras = bundle;
270 return this;
271 }
272
273 /** Builds the {@link Message} object. */
274 @NonNull
275 public Message build() {
276 return new Message(
277 mAuthor,
Tony Mak82fa8d92018-12-07 17:37:43 +0000278 mReferenceTime,
Tony Makc9d31e22018-10-22 16:17:45 +0100279 mText == null ? null : new SpannedString(mText),
280 mExtras == null ? new Bundle() : mExtras.deepCopy());
281 }
282 }
283 }
284
Tony Makc9d31e22018-10-22 16:17:45 +0100285 /**
286 * A request object for generating conversation action suggestions.
287 *
288 * @see TextClassifier#suggestConversationActions(Request)
289 */
290 public static final class Request implements Parcelable {
Tony Makae85aae2019-01-09 15:59:56 +0000291
292 /** @hide */
293 @Retention(SOURCE)
294 @StringDef(
295 value = {
296 HINT_FOR_NOTIFICATION,
297 HINT_FOR_IN_APP,
298 },
299 prefix = "HINT_")
300 public @interface Hint {}
301
302 /**
303 * To indicate the generated actions will be used within the app.
304 */
305 public static final String HINT_FOR_IN_APP = "in_app";
306 /**
307 * To indicate the generated actions will be used for notification.
308 */
309 public static final String HINT_FOR_NOTIFICATION = "notification";
310
Tony Makc9d31e22018-10-22 16:17:45 +0100311 @NonNull
312 private final List<Message> mConversation;
313 @NonNull
Tony Makae85aae2019-01-09 15:59:56 +0000314 private final TextClassifier.EntityConfig mTypeConfig;
Tony Makc9d31e22018-10-22 16:17:45 +0100315 private final int mMaxSuggestions;
316 @NonNull
317 @Hint
318 private final List<String> mHints;
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000319 @Nullable
320 private String mCallingPackageName;
Tony Makc9d31e22018-10-22 16:17:45 +0100321
322 private Request(
323 @NonNull List<Message> conversation,
Tony Makae85aae2019-01-09 15:59:56 +0000324 @NonNull TextClassifier.EntityConfig typeConfig,
Tony Makc9d31e22018-10-22 16:17:45 +0100325 int maxSuggestions,
326 @Nullable @Hint List<String> hints) {
327 mConversation = Preconditions.checkNotNull(conversation);
328 mTypeConfig = Preconditions.checkNotNull(typeConfig);
329 mMaxSuggestions = maxSuggestions;
330 mHints = hints;
331 }
332
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000333 private static Request readFromParcel(Parcel in) {
Tony Makc9d31e22018-10-22 16:17:45 +0100334 List<Message> conversation = new ArrayList<>();
335 in.readParcelableList(conversation, null);
Tony Makae85aae2019-01-09 15:59:56 +0000336 TextClassifier.EntityConfig typeConfig = in.readParcelable(null);
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000337 int maxSuggestions = in.readInt();
Tony Makc9d31e22018-10-22 16:17:45 +0100338 List<String> hints = new ArrayList<>();
339 in.readStringList(hints);
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000340 String callingPackageName = in.readString();
341
342 Request request = new Request(
343 conversation,
344 typeConfig,
345 maxSuggestions,
346 hints);
347 request.setCallingPackageName(callingPackageName);
348 return request;
Tony Makc9d31e22018-10-22 16:17:45 +0100349 }
350
351 @Override
352 public void writeToParcel(Parcel parcel, int flags) {
353 parcel.writeParcelableList(mConversation, flags);
354 parcel.writeParcelable(mTypeConfig, flags);
355 parcel.writeInt(mMaxSuggestions);
356 parcel.writeStringList(mHints);
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000357 parcel.writeString(mCallingPackageName);
Tony Makc9d31e22018-10-22 16:17:45 +0100358 }
359
360 @Override
361 public int describeContents() {
362 return 0;
363 }
364
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700365 public static final @android.annotation.NonNull Creator<Request> CREATOR =
Tony Makc9d31e22018-10-22 16:17:45 +0100366 new Creator<Request>() {
367 @Override
368 public Request createFromParcel(Parcel in) {
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000369 return readFromParcel(in);
Tony Makc9d31e22018-10-22 16:17:45 +0100370 }
371
372 @Override
373 public Request[] newArray(int size) {
374 return new Request[size];
375 }
376 };
377
378 /** Returns the type config. */
379 @NonNull
Tony Makae85aae2019-01-09 15:59:56 +0000380 public TextClassifier.EntityConfig getTypeConfig() {
Tony Makc9d31e22018-10-22 16:17:45 +0100381 return mTypeConfig;
382 }
383
384 /** Returns an immutable list of messages that make up the conversation. */
385 @NonNull
386 public List<Message> getConversation() {
387 return mConversation;
388 }
389
390 /**
Tony Makae33c3b2019-01-31 14:29:19 +0000391 * Return the maximal number of suggestions the caller wants, value -1 means no restriction
392 * and this is the default.
Tony Makc9d31e22018-10-22 16:17:45 +0100393 */
Tony Makae33c3b2019-01-31 14:29:19 +0000394 @IntRange(from = -1)
Tony Makc9d31e22018-10-22 16:17:45 +0100395 public int getMaxSuggestions() {
396 return mMaxSuggestions;
397 }
398
399 /** Returns an immutable list of hints */
400 @Nullable
401 @Hint
402 public List<String> getHints() {
403 return mHints;
404 }
405
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000406 /**
407 * Sets the name of the package that is sending this request.
408 * <p>
409 * Package-private for SystemTextClassifier's use.
410 * @hide
411 */
412 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
413 public void setCallingPackageName(@Nullable String callingPackageName) {
414 mCallingPackageName = callingPackageName;
415 }
416
417 /**
418 * Returns the name of the package that sent this request.
419 * This returns {@code null} if no calling package name is set.
420 */
421 @Nullable
422 public String getCallingPackageName() {
423 return mCallingPackageName;
424 }
425
Tony Makc9d31e22018-10-22 16:17:45 +0100426 /** Builder object to construct the {@link Request} object. */
427 public static final class Builder {
428 @NonNull
429 private List<Message> mConversation;
430 @Nullable
Tony Makae85aae2019-01-09 15:59:56 +0000431 private TextClassifier.EntityConfig mTypeConfig;
Tony Makae33c3b2019-01-31 14:29:19 +0000432 private int mMaxSuggestions = -1;
Tony Makc9d31e22018-10-22 16:17:45 +0100433 @Nullable
434 @Hint
435 private List<String> mHints;
436
437 /**
438 * Constructs a builder.
439 *
440 * @param conversation the conversation that the text classifier is going to generate
441 * actions for.
442 */
443 public Builder(@NonNull List<Message> conversation) {
444 mConversation = Preconditions.checkNotNull(conversation);
445 }
446
447 /**
448 * Sets the hints to help text classifier to generate actions. It could be used to help
449 * text classifier to infer what types of actions the caller may be interested in.
450 */
Tony Mak42ab9842019-03-05 15:38:51 +0000451 @NonNull
Tony Makc9d31e22018-10-22 16:17:45 +0100452 public Builder setHints(@Nullable @Hint List<String> hints) {
453 mHints = hints;
454 return this;
455 }
456
457 /** Sets the type config. */
458 @NonNull
Tony Makae85aae2019-01-09 15:59:56 +0000459 public Builder setTypeConfig(@Nullable TextClassifier.EntityConfig typeConfig) {
Tony Makc9d31e22018-10-22 16:17:45 +0100460 mTypeConfig = typeConfig;
461 return this;
462 }
463
Tony Makc4359bf2018-12-11 19:38:53 +0800464 /**
Tony Makae33c3b2019-01-31 14:29:19 +0000465 * Sets the maximum number of suggestions you want. Value -1 means no restriction and
466 * this is the default.
Tony Makc9d31e22018-10-22 16:17:45 +0100467 */
468 @NonNull
Tony Makae33c3b2019-01-31 14:29:19 +0000469 public Builder setMaxSuggestions(@IntRange(from = -1) int maxSuggestions) {
Tony Makc9d31e22018-10-22 16:17:45 +0100470 mMaxSuggestions = Preconditions.checkArgumentNonnegative(maxSuggestions);
471 return this;
472 }
473
474 /** Builds the {@link Request} object. */
475 @NonNull
476 public Request build() {
477 return new Request(
478 Collections.unmodifiableList(mConversation),
Tony Makae85aae2019-01-09 15:59:56 +0000479 mTypeConfig == null
480 ? new TextClassifier.EntityConfig.Builder().build()
481 : mTypeConfig,
Tony Makc9d31e22018-10-22 16:17:45 +0100482 mMaxSuggestions,
Tony Makc9d31e22018-10-22 16:17:45 +0100483 mHints == null
484 ? Collections.emptyList()
485 : Collections.unmodifiableList(mHints));
486 }
487 }
488 }
489}