blob: 9f9e66699d84217f45522f3e8673c4833560f314 [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 Mak91daa152019-01-24 16:00:28 +0000112 public static final Person PERSON_USER_SELF =
Tony Makf99ee172018-11-23 12:14:39 +0000113 new Person.Builder()
Tony Mak91daa152019-01-24 16:00:28 +0000114 .setKey("text-classifier-conversation-actions-user-self")
Tony Makf99ee172018-11-23 12:14:39 +0000115 .build();
116
Tony Mak82fa8d92018-12-07 17:37:43 +0000117 /**
118 * Represents the remote user.
119 * <p>
120 * If possible, you are suggested to create a {@link Person} object that can identify
121 * the remote user better, so that the underlying model could differentiate between
122 * different remote users.
123 *
124 * @see Builder#Builder(Person)
125 */
Tony Mak91daa152019-01-24 16:00:28 +0000126 public static final Person PERSON_USER_OTHERS =
Tony Mak82fa8d92018-12-07 17:37:43 +0000127 new Person.Builder()
Tony Mak91daa152019-01-24 16:00:28 +0000128 .setKey("text-classifier-conversation-actions-user-others")
Tony Mak82fa8d92018-12-07 17:37:43 +0000129 .build();
130
Tony Makc9d31e22018-10-22 16:17:45 +0100131 @Nullable
132 private final Person mAuthor;
133 @Nullable
Tony Mak82fa8d92018-12-07 17:37:43 +0000134 private final ZonedDateTime mReferenceTime;
Tony Makc9d31e22018-10-22 16:17:45 +0100135 @Nullable
136 private final CharSequence mText;
137 @NonNull
138 private final Bundle mExtras;
139
140 private Message(
141 @Nullable Person author,
Tony Mak82fa8d92018-12-07 17:37:43 +0000142 @Nullable ZonedDateTime referenceTime,
Tony Makc9d31e22018-10-22 16:17:45 +0100143 @Nullable CharSequence text,
144 @NonNull Bundle bundle) {
145 mAuthor = author;
Tony Mak82fa8d92018-12-07 17:37:43 +0000146 mReferenceTime = referenceTime;
Tony Makc9d31e22018-10-22 16:17:45 +0100147 mText = text;
148 mExtras = Preconditions.checkNotNull(bundle);
149 }
150
151 private Message(Parcel in) {
152 mAuthor = in.readParcelable(null);
Tony Mak82fa8d92018-12-07 17:37:43 +0000153 mReferenceTime =
Tony Makc9d31e22018-10-22 16:17:45 +0100154 in.readInt() == 0
155 ? null
156 : ZonedDateTime.parse(
157 in.readString(), DateTimeFormatter.ISO_ZONED_DATE_TIME);
158 mText = in.readCharSequence();
159 mExtras = in.readBundle();
160 }
161
162 @Override
163 public void writeToParcel(Parcel parcel, int flags) {
164 parcel.writeParcelable(mAuthor, flags);
Tony Mak82fa8d92018-12-07 17:37:43 +0000165 parcel.writeInt(mReferenceTime != null ? 1 : 0);
166 if (mReferenceTime != null) {
167 parcel.writeString(mReferenceTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
Tony Makc9d31e22018-10-22 16:17:45 +0100168 }
169 parcel.writeCharSequence(mText);
170 parcel.writeBundle(mExtras);
171 }
172
173 @Override
174 public int describeContents() {
175 return 0;
176 }
177
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700178 public static final @android.annotation.NonNull Creator<Message> CREATOR =
Tony Makc9d31e22018-10-22 16:17:45 +0100179 new Creator<Message>() {
180 @Override
181 public Message createFromParcel(Parcel in) {
182 return new Message(in);
183 }
184
185 @Override
186 public Message[] newArray(int size) {
187 return new Message[size];
188 }
189 };
190
191 /** Returns the person that composed the message. */
Tony Mak82fa8d92018-12-07 17:37:43 +0000192 @NonNull
Tony Makc9d31e22018-10-22 16:17:45 +0100193 public Person getAuthor() {
194 return mAuthor;
195 }
196
Tony Mak82fa8d92018-12-07 17:37:43 +0000197 /**
198 * Returns the reference time of the message, for example it could be the compose or send
199 * time of this message.
200 */
Tony Makc9d31e22018-10-22 16:17:45 +0100201 @Nullable
Tony Mak82fa8d92018-12-07 17:37:43 +0000202 public ZonedDateTime getReferenceTime() {
203 return mReferenceTime;
Tony Makc9d31e22018-10-22 16:17:45 +0100204 }
205
206 /** Returns the text of the message. */
207 @Nullable
208 public CharSequence getText() {
209 return mText;
210 }
211
212 /**
213 * Returns the extended data related to this conversation action.
214 *
215 * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
216 * prefer to hold a reference to the returned bundle rather than frequently calling this
217 * method.
218 */
219 @NonNull
220 public Bundle getExtras() {
221 return mExtras.deepCopy();
222 }
223
224 /** Builder class to construct a {@link Message} */
225 public static final class Builder {
226 @Nullable
227 private Person mAuthor;
228 @Nullable
Tony Mak82fa8d92018-12-07 17:37:43 +0000229 private ZonedDateTime mReferenceTime;
Tony Makc9d31e22018-10-22 16:17:45 +0100230 @Nullable
231 private CharSequence mText;
232 @Nullable
233 private Bundle mExtras;
234
Tony Makf99ee172018-11-23 12:14:39 +0000235 /**
Tony Mak82fa8d92018-12-07 17:37:43 +0000236 * Constructs a builder.
237 *
Tony Mak91daa152019-01-24 16:00:28 +0000238 * @param author the person that composed the message, use {@link #PERSON_USER_SELF}
Tony Mak82fa8d92018-12-07 17:37:43 +0000239 * to represent the local user. If it is not possible to identify the
240 * remote user that the local user is conversing with, use
Tony Mak91daa152019-01-24 16:00:28 +0000241 * {@link #PERSON_USER_OTHERS} to represent a remote user.
Tony Makf99ee172018-11-23 12:14:39 +0000242 */
Tony Mak82fa8d92018-12-07 17:37:43 +0000243 public Builder(@NonNull Person author) {
244 mAuthor = Preconditions.checkNotNull(author);
Tony Makc9d31e22018-10-22 16:17:45 +0100245 }
246
Tony Mak82fa8d92018-12-07 17:37:43 +0000247 /** Sets the text of this message. */
Tony Makc9d31e22018-10-22 16:17:45 +0100248 @NonNull
249 public Builder setText(@Nullable CharSequence text) {
250 mText = text;
251 return this;
252 }
253
Tony Mak82fa8d92018-12-07 17:37:43 +0000254 /**
255 * Sets the reference time of this message, for example it could be the compose or send
256 * time of this message.
257 */
Tony Makc9d31e22018-10-22 16:17:45 +0100258 @NonNull
Tony Mak82fa8d92018-12-07 17:37:43 +0000259 public Builder setReferenceTime(@Nullable ZonedDateTime referenceTime) {
260 mReferenceTime = referenceTime;
Tony Makc9d31e22018-10-22 16:17:45 +0100261 return this;
262 }
263
264 /** Sets a set of extended data to the message. */
265 @NonNull
266 public Builder setExtras(@Nullable Bundle bundle) {
267 this.mExtras = bundle;
268 return this;
269 }
270
271 /** Builds the {@link Message} object. */
272 @NonNull
273 public Message build() {
274 return new Message(
275 mAuthor,
Tony Mak82fa8d92018-12-07 17:37:43 +0000276 mReferenceTime,
Tony Makc9d31e22018-10-22 16:17:45 +0100277 mText == null ? null : new SpannedString(mText),
278 mExtras == null ? new Bundle() : mExtras.deepCopy());
279 }
280 }
281 }
282
Tony Makc9d31e22018-10-22 16:17:45 +0100283 /**
284 * A request object for generating conversation action suggestions.
285 *
286 * @see TextClassifier#suggestConversationActions(Request)
287 */
288 public static final class Request implements Parcelable {
Tony Makae85aae2019-01-09 15:59:56 +0000289
290 /** @hide */
291 @Retention(SOURCE)
292 @StringDef(
293 value = {
294 HINT_FOR_NOTIFICATION,
295 HINT_FOR_IN_APP,
296 },
297 prefix = "HINT_")
298 public @interface Hint {}
299
300 /**
301 * To indicate the generated actions will be used within the app.
302 */
303 public static final String HINT_FOR_IN_APP = "in_app";
304 /**
305 * To indicate the generated actions will be used for notification.
306 */
307 public static final String HINT_FOR_NOTIFICATION = "notification";
308
Tony Makc9d31e22018-10-22 16:17:45 +0100309 @NonNull
310 private final List<Message> mConversation;
311 @NonNull
Tony Makae85aae2019-01-09 15:59:56 +0000312 private final TextClassifier.EntityConfig mTypeConfig;
Tony Makc9d31e22018-10-22 16:17:45 +0100313 private final int mMaxSuggestions;
314 @NonNull
315 @Hint
316 private final List<String> mHints;
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000317 @Nullable
318 private String mCallingPackageName;
Tony Makc4359bf2018-12-11 19:38:53 +0800319 @Nullable
320 private final String mConversationId;
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,
Tony Makc4359bf2018-12-11 19:38:53 +0800326 String conversationId,
Tony Makc9d31e22018-10-22 16:17:45 +0100327 @Nullable @Hint List<String> hints) {
328 mConversation = Preconditions.checkNotNull(conversation);
329 mTypeConfig = Preconditions.checkNotNull(typeConfig);
330 mMaxSuggestions = maxSuggestions;
Tony Makc4359bf2018-12-11 19:38:53 +0800331 mConversationId = conversationId;
Tony Makc9d31e22018-10-22 16:17:45 +0100332 mHints = hints;
333 }
334
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000335 private static Request readFromParcel(Parcel in) {
Tony Makc9d31e22018-10-22 16:17:45 +0100336 List<Message> conversation = new ArrayList<>();
337 in.readParcelableList(conversation, null);
Tony Makae85aae2019-01-09 15:59:56 +0000338 TextClassifier.EntityConfig typeConfig = in.readParcelable(null);
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000339 int maxSuggestions = in.readInt();
Tony Makc4359bf2018-12-11 19:38:53 +0800340 String conversationId = in.readString();
Tony Makc9d31e22018-10-22 16:17:45 +0100341 List<String> hints = new ArrayList<>();
342 in.readStringList(hints);
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000343 String callingPackageName = in.readString();
344
345 Request request = new Request(
346 conversation,
347 typeConfig,
348 maxSuggestions,
Tony Makc4359bf2018-12-11 19:38:53 +0800349 conversationId,
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000350 hints);
351 request.setCallingPackageName(callingPackageName);
352 return request;
Tony Makc9d31e22018-10-22 16:17:45 +0100353 }
354
355 @Override
356 public void writeToParcel(Parcel parcel, int flags) {
357 parcel.writeParcelableList(mConversation, flags);
358 parcel.writeParcelable(mTypeConfig, flags);
359 parcel.writeInt(mMaxSuggestions);
Tony Makc4359bf2018-12-11 19:38:53 +0800360 parcel.writeString(mConversationId);
Tony Makc9d31e22018-10-22 16:17:45 +0100361 parcel.writeStringList(mHints);
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000362 parcel.writeString(mCallingPackageName);
Tony Makc9d31e22018-10-22 16:17:45 +0100363 }
364
365 @Override
366 public int describeContents() {
367 return 0;
368 }
369
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700370 public static final @android.annotation.NonNull Creator<Request> CREATOR =
Tony Makc9d31e22018-10-22 16:17:45 +0100371 new Creator<Request>() {
372 @Override
373 public Request createFromParcel(Parcel in) {
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000374 return readFromParcel(in);
Tony Makc9d31e22018-10-22 16:17:45 +0100375 }
376
377 @Override
378 public Request[] newArray(int size) {
379 return new Request[size];
380 }
381 };
382
383 /** Returns the type config. */
384 @NonNull
Tony Makae85aae2019-01-09 15:59:56 +0000385 public TextClassifier.EntityConfig getTypeConfig() {
Tony Makc9d31e22018-10-22 16:17:45 +0100386 return mTypeConfig;
387 }
388
389 /** Returns an immutable list of messages that make up the conversation. */
390 @NonNull
391 public List<Message> getConversation() {
392 return mConversation;
393 }
394
395 /**
Tony Makae33c3b2019-01-31 14:29:19 +0000396 * Return the maximal number of suggestions the caller wants, value -1 means no restriction
397 * and this is the default.
Tony Makc9d31e22018-10-22 16:17:45 +0100398 */
Tony Makae33c3b2019-01-31 14:29:19 +0000399 @IntRange(from = -1)
Tony Makc9d31e22018-10-22 16:17:45 +0100400 public int getMaxSuggestions() {
401 return mMaxSuggestions;
402 }
403
Tony Makc4359bf2018-12-11 19:38:53 +0800404 /**
405 * Return an unique identifier of the conversation that is generating actions for. This
406 * identifier is unique within the calling package only, so use it with
407 * {@link #getCallingPackageName()}.
408 */
409 @Nullable
410 public String getConversationId() {
411 return mConversationId;
412 }
413
Tony Makc9d31e22018-10-22 16:17:45 +0100414 /** Returns an immutable list of hints */
415 @Nullable
416 @Hint
417 public List<String> getHints() {
418 return mHints;
419 }
420
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000421 /**
422 * Sets the name of the package that is sending this request.
423 * <p>
424 * Package-private for SystemTextClassifier's use.
425 * @hide
426 */
427 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
428 public void setCallingPackageName(@Nullable String callingPackageName) {
429 mCallingPackageName = callingPackageName;
430 }
431
432 /**
433 * Returns the name of the package that sent this request.
434 * This returns {@code null} if no calling package name is set.
435 */
436 @Nullable
437 public String getCallingPackageName() {
438 return mCallingPackageName;
439 }
440
Tony Makc9d31e22018-10-22 16:17:45 +0100441 /** Builder object to construct the {@link Request} object. */
442 public static final class Builder {
443 @NonNull
444 private List<Message> mConversation;
445 @Nullable
Tony Makae85aae2019-01-09 15:59:56 +0000446 private TextClassifier.EntityConfig mTypeConfig;
Tony Makae33c3b2019-01-31 14:29:19 +0000447 private int mMaxSuggestions = -1;
Tony Makc9d31e22018-10-22 16:17:45 +0100448 @Nullable
Tony Makc4359bf2018-12-11 19:38:53 +0800449 private String mConversationId;
450 @Nullable
Tony Makc9d31e22018-10-22 16:17:45 +0100451 @Hint
452 private List<String> mHints;
453
454 /**
455 * Constructs a builder.
456 *
457 * @param conversation the conversation that the text classifier is going to generate
458 * actions for.
459 */
460 public Builder(@NonNull List<Message> conversation) {
461 mConversation = Preconditions.checkNotNull(conversation);
462 }
463
464 /**
465 * Sets the hints to help text classifier to generate actions. It could be used to help
466 * text classifier to infer what types of actions the caller may be interested in.
467 */
468 public Builder setHints(@Nullable @Hint List<String> hints) {
469 mHints = hints;
470 return this;
471 }
472
473 /** Sets the type config. */
474 @NonNull
Tony Makae85aae2019-01-09 15:59:56 +0000475 public Builder setTypeConfig(@Nullable TextClassifier.EntityConfig typeConfig) {
Tony Makc9d31e22018-10-22 16:17:45 +0100476 mTypeConfig = typeConfig;
477 return this;
478 }
479
Tony Makc4359bf2018-12-11 19:38:53 +0800480 /**
Tony Makae33c3b2019-01-31 14:29:19 +0000481 * Sets the maximum number of suggestions you want. Value -1 means no restriction and
482 * this is the default.
Tony Makc9d31e22018-10-22 16:17:45 +0100483 */
484 @NonNull
Tony Makae33c3b2019-01-31 14:29:19 +0000485 public Builder setMaxSuggestions(@IntRange(from = -1) int maxSuggestions) {
Tony Makc9d31e22018-10-22 16:17:45 +0100486 mMaxSuggestions = Preconditions.checkArgumentNonnegative(maxSuggestions);
487 return this;
488 }
489
Tony Makc4359bf2018-12-11 19:38:53 +0800490 /**
491 * Sets an unique identifier of the conversation that is generating actions for.
492 */
493 @NonNull
494 public Builder setConversationId(@Nullable String conversationId) {
495 mConversationId = conversationId;
496 return this;
497 }
498
Tony Makc9d31e22018-10-22 16:17:45 +0100499 /** Builds the {@link Request} object. */
500 @NonNull
501 public Request build() {
502 return new Request(
503 Collections.unmodifiableList(mConversation),
Tony Makae85aae2019-01-09 15:59:56 +0000504 mTypeConfig == null
505 ? new TextClassifier.EntityConfig.Builder().build()
506 : mTypeConfig,
Tony Makc9d31e22018-10-22 16:17:45 +0100507 mMaxSuggestions,
Tony Makc4359bf2018-12-11 19:38:53 +0800508 mConversationId,
Tony Makc9d31e22018-10-22 16:17:45 +0100509 mHints == null
510 ? Collections.emptyList()
511 : Collections.unmodifiableList(mHints));
512 }
513 }
514 }
515}