Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 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 | */ |
| 16 | |
| 17 | package android.view.textclassifier; |
| 18 | |
Abodunrinwa Toki | c7073a4 | 2018-02-28 23:02:13 +0000 | [diff] [blame] | 19 | import android.annotation.IntDef; |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 20 | import android.annotation.IntRange; |
| 21 | import android.annotation.NonNull; |
Abodunrinwa Toki | 4cfda0b | 2017-02-28 18:56:47 +0000 | [diff] [blame] | 22 | import android.annotation.Nullable; |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 23 | import android.annotation.StringDef; |
Abodunrinwa Toki | e0b5789 | 2017-04-28 19:59:57 +0100 | [diff] [blame] | 24 | import android.annotation.WorkerThread; |
Abodunrinwa Toki | 4cfda0b | 2017-02-28 18:56:47 +0000 | [diff] [blame] | 25 | import android.os.LocaleList; |
Abodunrinwa Toki | d32906c | 2018-01-18 04:34:44 -0800 | [diff] [blame] | 26 | import android.os.Looper; |
Jan Althaus | 0d9fbb9 | 2017-11-28 12:19:33 +0100 | [diff] [blame] | 27 | import android.os.Parcel; |
| 28 | import android.os.Parcelable; |
Abodunrinwa Toki | 6563833 | 2018-03-16 21:08:50 +0000 | [diff] [blame] | 29 | import android.text.Spannable; |
| 30 | import android.text.SpannableString; |
| 31 | import android.text.style.URLSpan; |
| 32 | import android.text.util.Linkify; |
| 33 | import android.text.util.Linkify.LinkifyMask; |
| 34 | import android.util.ArrayMap; |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 35 | |
Tony Mak | f93e9e5 | 2018-07-16 14:46:29 +0200 | [diff] [blame] | 36 | import com.android.internal.util.IndentingPrintWriter; |
Abodunrinwa Toki | 4d232d6 | 2017-11-23 12:22:45 +0000 | [diff] [blame] | 37 | import com.android.internal.util.Preconditions; |
| 38 | |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 39 | import java.lang.annotation.Retention; |
| 40 | import java.lang.annotation.RetentionPolicy; |
Richard Ledley | db18a57 | 2017-11-30 17:33:51 +0000 | [diff] [blame] | 41 | import java.util.ArrayList; |
| 42 | import java.util.Collection; |
| 43 | import java.util.Collections; |
Richard Ledley | ab669a0 | 2018-04-03 15:15:43 +0100 | [diff] [blame] | 44 | import java.util.HashSet; |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 45 | import java.util.List; |
Abodunrinwa Toki | 6563833 | 2018-03-16 21:08:50 +0000 | [diff] [blame] | 46 | import java.util.Map; |
Richard Ledley | ab669a0 | 2018-04-03 15:15:43 +0100 | [diff] [blame] | 47 | import java.util.Set; |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 48 | |
| 49 | /** |
| 50 | * Interface for providing text classification related features. |
Abodunrinwa Toki | aa750a4 | 2018-11-09 13:47:59 +0000 | [diff] [blame] | 51 | * <p> |
| 52 | * The TextClassifier may be used to understand the meaning of text, as well as generating predicted |
| 53 | * next actions based on the text. |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 54 | * |
Abodunrinwa Toki | d32906c | 2018-01-18 04:34:44 -0800 | [diff] [blame] | 55 | * <p><strong>NOTE: </strong>Unless otherwise stated, methods of this interface are blocking |
| 56 | * operations. Call on a worker thread. |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 57 | */ |
| 58 | public interface TextClassifier { |
| 59 | |
Abodunrinwa Toki | 692b196 | 2017-08-15 15:05:11 +0100 | [diff] [blame] | 60 | /** @hide */ |
Abodunrinwa Toki | 008f387 | 2017-11-27 19:32:35 +0000 | [diff] [blame] | 61 | String DEFAULT_LOG_TAG = "androidtc"; |
Abodunrinwa Toki | 692b196 | 2017-08-15 15:05:11 +0100 | [diff] [blame] | 62 | |
Abodunrinwa Toki | c7073a4 | 2018-02-28 23:02:13 +0000 | [diff] [blame] | 63 | |
| 64 | /** @hide */ |
| 65 | @Retention(RetentionPolicy.SOURCE) |
| 66 | @IntDef(value = {LOCAL, SYSTEM}) |
| 67 | @interface TextClassifierType {} // TODO: Expose as system APIs. |
| 68 | /** Specifies a TextClassifier that runs locally in the app's process. @hide */ |
| 69 | int LOCAL = 0; |
| 70 | /** Specifies a TextClassifier that runs in the system process and serves all apps. @hide */ |
| 71 | int SYSTEM = 1; |
| 72 | |
Jan Althaus | 705b9e9 | 2018-01-22 18:22:29 +0100 | [diff] [blame] | 73 | /** The TextClassifier failed to run. */ |
Abodunrinwa Toki | 4d232d6 | 2017-11-23 12:22:45 +0000 | [diff] [blame] | 74 | String TYPE_UNKNOWN = ""; |
Jan Althaus | 705b9e9 | 2018-01-22 18:22:29 +0100 | [diff] [blame] | 75 | /** The classifier ran, but didn't recognize a known entity. */ |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 76 | String TYPE_OTHER = "other"; |
Jan Althaus | 705b9e9 | 2018-01-22 18:22:29 +0100 | [diff] [blame] | 77 | /** E-mail address (e.g. "noreply@android.com"). */ |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 78 | String TYPE_EMAIL = "email"; |
Jan Althaus | 705b9e9 | 2018-01-22 18:22:29 +0100 | [diff] [blame] | 79 | /** Phone number (e.g. "555-123 456"). */ |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 80 | String TYPE_PHONE = "phone"; |
Jan Althaus | 705b9e9 | 2018-01-22 18:22:29 +0100 | [diff] [blame] | 81 | /** Physical address. */ |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 82 | String TYPE_ADDRESS = "address"; |
Jan Althaus | 705b9e9 | 2018-01-22 18:22:29 +0100 | [diff] [blame] | 83 | /** Web URL. */ |
Abodunrinwa Toki | 9b4c82a | 2017-02-06 20:29:36 +0000 | [diff] [blame] | 84 | String TYPE_URL = "url"; |
Jan Althaus | 705b9e9 | 2018-01-22 18:22:29 +0100 | [diff] [blame] | 85 | /** Time reference that is no more specific than a date. May be absolute such as "01/01/2000" or |
| 86 | * relative like "tomorrow". **/ |
| 87 | String TYPE_DATE = "date"; |
| 88 | /** Time reference that includes a specific time. May be absolute such as "01/01/2000 5:30pm" or |
| 89 | * relative like "tomorrow at 5:30pm". **/ |
| 90 | String TYPE_DATE_TIME = "datetime"; |
| 91 | /** Flight number in IATA format. */ |
| 92 | String TYPE_FLIGHT_NUMBER = "flight"; |
Tony Mak | e1f3ac06 | 2018-11-27 14:30:21 +0000 | [diff] [blame] | 93 | /** |
| 94 | * Word that users may be interested to look up for meaning. |
| 95 | * @hide |
| 96 | */ |
| 97 | String TYPE_DICTIONARY = "dictionary"; |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 98 | |
Abodunrinwa Toki | 45cb3e6 | 2017-03-29 21:51:45 +0100 | [diff] [blame] | 99 | /** @hide */ |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 100 | @Retention(RetentionPolicy.SOURCE) |
Jeff Sharkey | 5db9a91 | 2017-12-08 17:32:32 -0700 | [diff] [blame] | 101 | @StringDef(prefix = { "TYPE_" }, value = { |
| 102 | TYPE_UNKNOWN, |
| 103 | TYPE_OTHER, |
| 104 | TYPE_EMAIL, |
| 105 | TYPE_PHONE, |
| 106 | TYPE_ADDRESS, |
| 107 | TYPE_URL, |
Jan Althaus | 705b9e9 | 2018-01-22 18:22:29 +0100 | [diff] [blame] | 108 | TYPE_DATE, |
| 109 | TYPE_DATE_TIME, |
| 110 | TYPE_FLIGHT_NUMBER, |
Tony Mak | e1f3ac06 | 2018-11-27 14:30:21 +0000 | [diff] [blame] | 111 | TYPE_DICTIONARY |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 112 | }) |
| 113 | @interface EntityType {} |
| 114 | |
Richard Ledley | 1fc998b | 2018-02-16 15:45:06 +0000 | [diff] [blame] | 115 | /** Designates that the text in question is editable. **/ |
| 116 | String HINT_TEXT_IS_EDITABLE = "android.text_is_editable"; |
| 117 | /** Designates that the text in question is not editable. **/ |
| 118 | String HINT_TEXT_IS_NOT_EDITABLE = "android.text_is_not_editable"; |
Richard Ledley | db18a57 | 2017-11-30 17:33:51 +0000 | [diff] [blame] | 119 | |
| 120 | /** @hide */ |
| 121 | @Retention(RetentionPolicy.SOURCE) |
Richard Ledley | 1fc998b | 2018-02-16 15:45:06 +0000 | [diff] [blame] | 122 | @StringDef(prefix = { "HINT_" }, value = {HINT_TEXT_IS_EDITABLE, HINT_TEXT_IS_NOT_EDITABLE}) |
| 123 | @interface Hints {} |
Richard Ledley | db18a57 | 2017-11-30 17:33:51 +0000 | [diff] [blame] | 124 | |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 125 | /** @hide */ |
| 126 | @Retention(RetentionPolicy.SOURCE) |
Tony Mak | 4dd4314 | 2018-05-02 16:49:01 +0100 | [diff] [blame] | 127 | @StringDef({WIDGET_TYPE_TEXTVIEW, WIDGET_TYPE_EDITTEXT, WIDGET_TYPE_UNSELECTABLE_TEXTVIEW, |
| 128 | WIDGET_TYPE_WEBVIEW, WIDGET_TYPE_EDIT_WEBVIEW, WIDGET_TYPE_CUSTOM_TEXTVIEW, |
| 129 | WIDGET_TYPE_CUSTOM_EDITTEXT, WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW, |
Abodunrinwa Toki | 37ccedc | 2018-12-11 00:35:11 +0000 | [diff] [blame] | 130 | WIDGET_TYPE_NOTIFICATION, WIDGET_TYPE_UNKNOWN}) |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 131 | @interface WidgetType {} |
| 132 | |
Abodunrinwa Toki | 37ccedc | 2018-12-11 00:35:11 +0000 | [diff] [blame] | 133 | /** The widget involved in the text classification context is a standard |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 134 | * {@link android.widget.TextView}. */ |
| 135 | String WIDGET_TYPE_TEXTVIEW = "textview"; |
Abodunrinwa Toki | 37ccedc | 2018-12-11 00:35:11 +0000 | [diff] [blame] | 136 | /** The widget involved in the text classification context is a standard |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 137 | * {@link android.widget.EditText}. */ |
| 138 | String WIDGET_TYPE_EDITTEXT = "edittext"; |
Abodunrinwa Toki | 37ccedc | 2018-12-11 00:35:11 +0000 | [diff] [blame] | 139 | /** The widget involved in the text classification context is a standard non-selectable |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 140 | * {@link android.widget.TextView}. */ |
| 141 | String WIDGET_TYPE_UNSELECTABLE_TEXTVIEW = "nosel-textview"; |
Abodunrinwa Toki | 37ccedc | 2018-12-11 00:35:11 +0000 | [diff] [blame] | 142 | /** The widget involved in the text classification context is a standard |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 143 | * {@link android.webkit.WebView}. */ |
| 144 | String WIDGET_TYPE_WEBVIEW = "webview"; |
Abodunrinwa Toki | 37ccedc | 2018-12-11 00:35:11 +0000 | [diff] [blame] | 145 | /** The widget involved in the text classification context is a standard editable |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 146 | * {@link android.webkit.WebView}. */ |
| 147 | String WIDGET_TYPE_EDIT_WEBVIEW = "edit-webview"; |
Abodunrinwa Toki | 37ccedc | 2018-12-11 00:35:11 +0000 | [diff] [blame] | 148 | /** The widget involved in the text classification context is a custom text widget. */ |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 149 | String WIDGET_TYPE_CUSTOM_TEXTVIEW = "customview"; |
Abodunrinwa Toki | 37ccedc | 2018-12-11 00:35:11 +0000 | [diff] [blame] | 150 | /** The widget involved in the text classification context is a custom editable text widget. */ |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 151 | String WIDGET_TYPE_CUSTOM_EDITTEXT = "customedit"; |
Abodunrinwa Toki | 37ccedc | 2018-12-11 00:35:11 +0000 | [diff] [blame] | 152 | /** The widget involved in the text classification context is a custom non-selectable text |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 153 | * widget. */ |
| 154 | String WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview"; |
Abodunrinwa Toki | 37ccedc | 2018-12-11 00:35:11 +0000 | [diff] [blame] | 155 | /** The widget involved in the text classification context is a notification */ |
| 156 | String WIDGET_TYPE_NOTIFICATION = "notification"; |
| 157 | /** The widget involved in the text classification context is of an unknown/unspecified type. */ |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 158 | String WIDGET_TYPE_UNKNOWN = "unknown"; |
| 159 | |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 160 | /** |
| 161 | * No-op TextClassifier. |
| 162 | * This may be used to turn off TextClassifier features. |
| 163 | */ |
Abodunrinwa Toki | 520b2f8 | 2019-01-27 07:48:02 +0000 | [diff] [blame] | 164 | TextClassifier NO_OP = new TextClassifier() { |
| 165 | @Override |
| 166 | public String toString() { |
| 167 | return "TextClassifier.NO_OP"; |
| 168 | } |
| 169 | }; |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 170 | |
| 171 | /** |
Tony Mak | 63abbe2 | 2019-01-28 15:14:36 +0000 | [diff] [blame] | 172 | * Extra that is included on activity intents coming from a TextClassifier when |
| 173 | * it suggests actions to its caller. |
Tony Mak | c5a4612 | 2018-12-05 22:19:22 +0000 | [diff] [blame] | 174 | * <p> |
Tony Mak | 63abbe2 | 2019-01-28 15:14:36 +0000 | [diff] [blame] | 175 | * All {@link TextClassifier} implementations should make sure this extra exists in their |
Tony Mak | c5a4612 | 2018-12-05 22:19:22 +0000 | [diff] [blame] | 176 | * generated intents. |
| 177 | */ |
| 178 | String EXTRA_FROM_TEXT_CLASSIFIER = "android.view.textclassifier.extra.FROM_TEXT_CLASSIFIER"; |
| 179 | |
| 180 | /** |
Abodunrinwa Toki | 33ff200 | 2017-10-24 00:49:27 +0100 | [diff] [blame] | 181 | * Returns suggested text selection start and end indices, recognized entity types, and their |
| 182 | * associated confidence scores. The entity types are ordered from highest to lowest scoring. |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 183 | * |
Abodunrinwa Toki | d32906c | 2018-01-18 04:34:44 -0800 | [diff] [blame] | 184 | * <p><strong>NOTE: </strong>Call on a worker thread. |
| 185 | * |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 186 | * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 187 | * throw an {@link IllegalStateException}. See {@link #isDestroyed()}. |
| 188 | * |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 189 | * @param request the text selection request |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 190 | */ |
Abodunrinwa Toki | e0b5789 | 2017-04-28 19:59:57 +0100 | [diff] [blame] | 191 | @WorkerThread |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 192 | @NonNull |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 193 | default TextSelection suggestSelection(@NonNull TextSelection.Request request) { |
| 194 | Preconditions.checkNotNull(request); |
| 195 | Utils.checkMainThread(); |
| 196 | return new TextSelection.Builder(request.getStartIndex(), request.getEndIndex()).build(); |
Abodunrinwa Toki | 2b6020f | 2017-10-28 02:28:45 +0100 | [diff] [blame] | 197 | } |
| 198 | |
| 199 | /** |
Abodunrinwa Toki | 4d232d6 | 2017-11-23 12:22:45 +0000 | [diff] [blame] | 200 | * Returns suggested text selection start and end indices, recognized entity types, and their |
| 201 | * associated confidence scores. The entity types are ordered from highest to lowest scoring. |
| 202 | * |
Abodunrinwa Toki | d32906c | 2018-01-18 04:34:44 -0800 | [diff] [blame] | 203 | * <p><strong>NOTE: </strong>Call on a worker thread. |
| 204 | * |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 205 | * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 206 | * throw an {@link IllegalStateException}. See {@link #isDestroyed()}. |
| 207 | * |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 208 | * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls |
| 209 | * {@link #suggestSelection(TextSelection.Request)}. If that method calls this method, |
| 210 | * a stack overflow error will happen. |
| 211 | * |
Abodunrinwa Toki | 4d232d6 | 2017-11-23 12:22:45 +0000 | [diff] [blame] | 212 | * @param text text providing context for the selected text (which is specified |
| 213 | * by the sub sequence starting at selectionStartIndex and ending at selectionEndIndex) |
| 214 | * @param selectionStartIndex start index of the selected part of text |
| 215 | * @param selectionEndIndex end index of the selected part of text |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 216 | * @param defaultLocales ordered list of locale preferences that may be used to |
| 217 | * disambiguate the provided text. If no locale preferences exist, set this to null |
| 218 | * or an empty locale list. |
Abodunrinwa Toki | 4d232d6 | 2017-11-23 12:22:45 +0000 | [diff] [blame] | 219 | * |
| 220 | * @throws IllegalArgumentException if text is null; selectionStartIndex is negative; |
| 221 | * selectionEndIndex is greater than text.length() or not greater than selectionStartIndex |
| 222 | * |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 223 | * @see #suggestSelection(TextSelection.Request) |
Abodunrinwa Toki | 4d232d6 | 2017-11-23 12:22:45 +0000 | [diff] [blame] | 224 | */ |
Abodunrinwa Toki | 2b6020f | 2017-10-28 02:28:45 +0100 | [diff] [blame] | 225 | @WorkerThread |
| 226 | @NonNull |
| 227 | default TextSelection suggestSelection( |
| 228 | @NonNull CharSequence text, |
| 229 | @IntRange(from = 0) int selectionStartIndex, |
| 230 | @IntRange(from = 0) int selectionEndIndex, |
| 231 | @Nullable LocaleList defaultLocales) { |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 232 | final TextSelection.Request request = new TextSelection.Request.Builder( |
| 233 | text, selectionStartIndex, selectionEndIndex) |
| 234 | .setDefaultLocales(defaultLocales) |
| 235 | .build(); |
| 236 | return suggestSelection(request); |
Abodunrinwa Toki | 2b6020f | 2017-10-28 02:28:45 +0100 | [diff] [blame] | 237 | } |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 238 | |
| 239 | /** |
Abodunrinwa Toki | e0b5789 | 2017-04-28 19:59:57 +0100 | [diff] [blame] | 240 | * Classifies the specified text and returns a {@link TextClassification} object that can be |
| 241 | * used to generate a widget for handling the classified text. |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 242 | * |
Abodunrinwa Toki | d32906c | 2018-01-18 04:34:44 -0800 | [diff] [blame] | 243 | * <p><strong>NOTE: </strong>Call on a worker thread. |
| 244 | * |
Abodunrinwa Toki | 7cefd4f | 2018-09-14 16:00:03 +0100 | [diff] [blame] | 245 | * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 246 | * throw an {@link IllegalStateException}. See {@link #isDestroyed()}. |
| 247 | * |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 248 | * @param request the text classification request |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 249 | */ |
Abodunrinwa Toki | e0b5789 | 2017-04-28 19:59:57 +0100 | [diff] [blame] | 250 | @WorkerThread |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 251 | @NonNull |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 252 | default TextClassification classifyText(@NonNull TextClassification.Request request) { |
| 253 | Preconditions.checkNotNull(request); |
| 254 | Utils.checkMainThread(); |
Abodunrinwa Toki | 2b6020f | 2017-10-28 02:28:45 +0100 | [diff] [blame] | 255 | return TextClassification.EMPTY; |
| 256 | } |
| 257 | |
| 258 | /** |
Abodunrinwa Toki | 4d232d6 | 2017-11-23 12:22:45 +0000 | [diff] [blame] | 259 | * Classifies the specified text and returns a {@link TextClassification} object that can be |
| 260 | * used to generate a widget for handling the classified text. |
| 261 | * |
Abodunrinwa Toki | d32906c | 2018-01-18 04:34:44 -0800 | [diff] [blame] | 262 | * <p><strong>NOTE: </strong>Call on a worker thread. |
| 263 | * |
Abodunrinwa Toki | 4d232d6 | 2017-11-23 12:22:45 +0000 | [diff] [blame] | 264 | * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 265 | * {@link #classifyText(TextClassification.Request)}. If that method calls this method, |
| 266 | * a stack overflow error will happen. |
Abodunrinwa Toki | 4d232d6 | 2017-11-23 12:22:45 +0000 | [diff] [blame] | 267 | * |
Abodunrinwa Toki | 7cefd4f | 2018-09-14 16:00:03 +0100 | [diff] [blame] | 268 | * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 269 | * throw an {@link IllegalStateException}. See {@link #isDestroyed()}. |
| 270 | * |
Abodunrinwa Toki | 4d232d6 | 2017-11-23 12:22:45 +0000 | [diff] [blame] | 271 | * @param text text providing context for the text to classify (which is specified |
| 272 | * by the sub sequence starting at startIndex and ending at endIndex) |
| 273 | * @param startIndex start index of the text to classify |
| 274 | * @param endIndex end index of the text to classify |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 275 | * @param defaultLocales ordered list of locale preferences that may be used to |
| 276 | * disambiguate the provided text. If no locale preferences exist, set this to null |
| 277 | * or an empty locale list. |
Abodunrinwa Toki | 4d232d6 | 2017-11-23 12:22:45 +0000 | [diff] [blame] | 278 | * |
| 279 | * @throws IllegalArgumentException if text is null; startIndex is negative; |
| 280 | * endIndex is greater than text.length() or not greater than startIndex |
| 281 | * |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 282 | * @see #classifyText(TextClassification.Request) |
Abodunrinwa Toki | 4d232d6 | 2017-11-23 12:22:45 +0000 | [diff] [blame] | 283 | */ |
Abodunrinwa Toki | 2b6020f | 2017-10-28 02:28:45 +0100 | [diff] [blame] | 284 | @WorkerThread |
| 285 | @NonNull |
| 286 | default TextClassification classifyText( |
| 287 | @NonNull CharSequence text, |
| 288 | @IntRange(from = 0) int startIndex, |
| 289 | @IntRange(from = 0) int endIndex, |
| 290 | @Nullable LocaleList defaultLocales) { |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 291 | final TextClassification.Request request = new TextClassification.Request.Builder( |
| 292 | text, startIndex, endIndex) |
| 293 | .setDefaultLocales(defaultLocales) |
| 294 | .build(); |
| 295 | return classifyText(request); |
Abodunrinwa Toki | 2b6020f | 2017-10-28 02:28:45 +0100 | [diff] [blame] | 296 | } |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 297 | |
| 298 | /** |
Abodunrinwa Toki | d32906c | 2018-01-18 04:34:44 -0800 | [diff] [blame] | 299 | * Generates and returns a {@link TextLinks} that may be applied to the text to annotate it with |
| 300 | * links information. |
Richard Ledley | 68d9452 | 2017-10-05 10:52:19 +0100 | [diff] [blame] | 301 | * |
Abodunrinwa Toki | d32906c | 2018-01-18 04:34:44 -0800 | [diff] [blame] | 302 | * <p><strong>NOTE: </strong>Call on a worker thread. |
Richard Ledley | db18a57 | 2017-11-30 17:33:51 +0000 | [diff] [blame] | 303 | * |
Abodunrinwa Toki | 7cefd4f | 2018-09-14 16:00:03 +0100 | [diff] [blame] | 304 | * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 305 | * throw an {@link IllegalStateException}. See {@link #isDestroyed()}. |
| 306 | * |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 307 | * @param request the text links request |
Richard Ledley | 68d9452 | 2017-10-05 10:52:19 +0100 | [diff] [blame] | 308 | * |
Jan Althaus | 108aad3 | 2018-01-30 15:26:55 +0100 | [diff] [blame] | 309 | * @see #getMaxGenerateLinksTextLength() |
Richard Ledley | 68d9452 | 2017-10-05 10:52:19 +0100 | [diff] [blame] | 310 | */ |
| 311 | @WorkerThread |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 312 | @NonNull |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 313 | default TextLinks generateLinks(@NonNull TextLinks.Request request) { |
| 314 | Preconditions.checkNotNull(request); |
| 315 | Utils.checkMainThread(); |
| 316 | return new TextLinks.Builder(request.getText().toString()).build(); |
Abodunrinwa Toki | 4d232d6 | 2017-11-23 12:22:45 +0000 | [diff] [blame] | 317 | } |
| 318 | |
| 319 | /** |
Jan Althaus | 108aad3 | 2018-01-30 15:26:55 +0100 | [diff] [blame] | 320 | * Returns the maximal length of text that can be processed by generateLinks. |
| 321 | * |
Abodunrinwa Toki | 7cefd4f | 2018-09-14 16:00:03 +0100 | [diff] [blame] | 322 | * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 323 | * throw an {@link IllegalStateException}. See {@link #isDestroyed()}. |
| 324 | * |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 325 | * @see #generateLinks(TextLinks.Request) |
Jan Althaus | 108aad3 | 2018-01-30 15:26:55 +0100 | [diff] [blame] | 326 | */ |
Abodunrinwa Toki | ad52f4b | 2018-02-06 23:32:41 +0000 | [diff] [blame] | 327 | @WorkerThread |
Jan Althaus | 108aad3 | 2018-01-30 15:26:55 +0100 | [diff] [blame] | 328 | default int getMaxGenerateLinksTextLength() { |
| 329 | return Integer.MAX_VALUE; |
| 330 | } |
| 331 | |
| 332 | /** |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 333 | * Detects the language of the text in the given request. |
Abodunrinwa Toki | 7cefd4f | 2018-09-14 16:00:03 +0100 | [diff] [blame] | 334 | * |
| 335 | * <p><strong>NOTE: </strong>Call on a worker thread. |
| 336 | * |
| 337 | * |
| 338 | * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should |
| 339 | * throw an {@link IllegalStateException}. See {@link #isDestroyed()}. |
| 340 | * |
| 341 | * @param request the {@link TextLanguage} request. |
| 342 | * @return the {@link TextLanguage} result. |
| 343 | */ |
| 344 | @WorkerThread |
| 345 | @NonNull |
| 346 | default TextLanguage detectLanguage(@NonNull TextLanguage.Request request) { |
| 347 | Preconditions.checkNotNull(request); |
| 348 | Utils.checkMainThread(); |
| 349 | return TextLanguage.EMPTY; |
| 350 | } |
| 351 | |
| 352 | /** |
Tony Mak | c9d31e2 | 2018-10-22 16:17:45 +0100 | [diff] [blame] | 353 | * Suggests and returns a list of actions according to the given conversation. |
| 354 | */ |
| 355 | @WorkerThread |
Tony Mak | 42ab984 | 2019-03-05 15:38:51 +0000 | [diff] [blame] | 356 | @NonNull |
Tony Mak | c9d31e2 | 2018-10-22 16:17:45 +0100 | [diff] [blame] | 357 | default ConversationActions suggestConversationActions( |
| 358 | @NonNull ConversationActions.Request request) { |
| 359 | Preconditions.checkNotNull(request); |
| 360 | Utils.checkMainThread(); |
Tony Mak | c4359bf | 2018-12-11 19:38:53 +0800 | [diff] [blame] | 361 | return new ConversationActions(Collections.emptyList(), null); |
Tony Mak | c9d31e2 | 2018-10-22 16:17:45 +0100 | [diff] [blame] | 362 | } |
| 363 | |
| 364 | /** |
Abodunrinwa Toki | 37ccedc | 2018-12-11 00:35:11 +0000 | [diff] [blame] | 365 | * <strong>NOTE: </strong>Use {@link #onTextClassifierEvent(TextClassifierEvent)} instead. |
| 366 | * <p> |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 367 | * Reports a selection event. |
| 368 | * |
Abodunrinwa Toki | 7cefd4f | 2018-09-14 16:00:03 +0100 | [diff] [blame] | 369 | * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 370 | * throw an {@link IllegalStateException}. See {@link #isDestroyed()}. |
| 371 | */ |
Abodunrinwa Toki | 37ccedc | 2018-12-11 00:35:11 +0000 | [diff] [blame] | 372 | default void onSelectionEvent(@NonNull SelectionEvent event) { |
| 373 | // TODO: Consider rerouting to onTextClassifierEvent() |
| 374 | } |
| 375 | |
| 376 | /** |
| 377 | * Reports a text classifier event. |
| 378 | * <p> |
| 379 | * <strong>NOTE: </strong>Call on a worker thread. |
| 380 | * |
| 381 | * @throws IllegalStateException if this TextClassifier has been destroyed. |
| 382 | * @see #isDestroyed() |
| 383 | */ |
| 384 | default void onTextClassifierEvent(@NonNull TextClassifierEvent event) {} |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 385 | |
| 386 | /** |
| 387 | * Destroys this TextClassifier. |
| 388 | * |
Abodunrinwa Toki | 7cefd4f | 2018-09-14 16:00:03 +0100 | [diff] [blame] | 389 | * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to its methods should |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 390 | * throw an {@link IllegalStateException}. See {@link #isDestroyed()}. |
| 391 | * |
| 392 | * <p>Subsequent calls to this method are no-ops. |
| 393 | */ |
| 394 | default void destroy() {} |
| 395 | |
| 396 | /** |
| 397 | * Returns whether or not this TextClassifier has been destroyed. |
| 398 | * |
Abodunrinwa Toki | 7cefd4f | 2018-09-14 16:00:03 +0100 | [diff] [blame] | 399 | * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, caller should not interact |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 400 | * with the classifier and an attempt to do so would throw an {@link IllegalStateException}. |
| 401 | * However, this method should never throw an {@link IllegalStateException}. |
| 402 | * |
| 403 | * @see #destroy() |
| 404 | */ |
| 405 | default boolean isDestroyed() { |
| 406 | return false; |
| 407 | } |
| 408 | |
Tony Mak | f93e9e5 | 2018-07-16 14:46:29 +0200 | [diff] [blame] | 409 | /** @hide **/ |
Abodunrinwa Toki | 7cefd4f | 2018-09-14 16:00:03 +0100 | [diff] [blame] | 410 | default void dump(@NonNull IndentingPrintWriter printWriter) {} |
Tony Mak | f93e9e5 | 2018-07-16 14:46:29 +0200 | [diff] [blame] | 411 | |
Abodunrinwa Toki | 88be5a6 | 2018-03-23 04:01:28 +0000 | [diff] [blame] | 412 | /** |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 413 | * Configuration object for specifying what entity types to identify. |
Richard Ledley | db18a57 | 2017-11-30 17:33:51 +0000 | [diff] [blame] | 414 | * |
| 415 | * Configs are initially based on a predefined preset, and can be modified from there. |
| 416 | */ |
Jan Althaus | 0d9fbb9 | 2017-11-28 12:19:33 +0100 | [diff] [blame] | 417 | final class EntityConfig implements Parcelable { |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 418 | private final List<String> mIncludedTypes; |
| 419 | private final List<String> mExcludedTypes; |
| 420 | private final List<String> mHints; |
| 421 | private final boolean mIncludeTypesFromTextClassifier; |
Richard Ledley | db18a57 | 2017-11-30 17:33:51 +0000 | [diff] [blame] | 422 | |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 423 | private EntityConfig( |
| 424 | List<String> includedEntityTypes, |
| 425 | List<String> excludedEntityTypes, |
| 426 | List<String> hints, |
| 427 | boolean includeTypesFromTextClassifier) { |
| 428 | mIncludedTypes = Preconditions.checkNotNull(includedEntityTypes); |
| 429 | mExcludedTypes = Preconditions.checkNotNull(excludedEntityTypes); |
| 430 | mHints = Preconditions.checkNotNull(hints); |
| 431 | mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier; |
| 432 | } |
| 433 | |
| 434 | private EntityConfig(Parcel in) { |
| 435 | mIncludedTypes = new ArrayList<>(); |
| 436 | in.readStringList(mIncludedTypes); |
| 437 | mExcludedTypes = new ArrayList<>(); |
| 438 | in.readStringList(mExcludedTypes); |
| 439 | List<String> tmpHints = new ArrayList<>(); |
| 440 | in.readStringList(tmpHints); |
| 441 | mHints = Collections.unmodifiableList(tmpHints); |
| 442 | mIncludeTypesFromTextClassifier = in.readByte() != 0; |
| 443 | } |
| 444 | |
| 445 | @Override |
| 446 | public void writeToParcel(Parcel parcel, int flags) { |
| 447 | parcel.writeStringList(mIncludedTypes); |
| 448 | parcel.writeStringList(mExcludedTypes); |
| 449 | parcel.writeStringList(mHints); |
| 450 | parcel.writeByte((byte) (mIncludeTypesFromTextClassifier ? 1 : 0)); |
Richard Ledley | db18a57 | 2017-11-30 17:33:51 +0000 | [diff] [blame] | 451 | } |
| 452 | |
| 453 | /** |
Richard Ledley | 1fc998b | 2018-02-16 15:45:06 +0000 | [diff] [blame] | 454 | * Creates an EntityConfig. |
| 455 | * |
| 456 | * @param hints Hints for the TextClassifier to determine what types of entities to find. |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 457 | * |
| 458 | * @deprecated Use {@link Builder} instead. |
Richard Ledley | 1fc998b | 2018-02-16 15:45:06 +0000 | [diff] [blame] | 459 | */ |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 460 | @Deprecated |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 461 | public static EntityConfig createWithHints(@Nullable Collection<String> hints) { |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 462 | return new EntityConfig.Builder() |
| 463 | .includeTypesFromTextClassifier(true) |
| 464 | .setHints(hints) |
| 465 | .build(); |
Abodunrinwa Toki | ae82e7a | 2018-04-03 23:49:16 +0100 | [diff] [blame] | 466 | } |
| 467 | |
Richard Ledley | 1fc998b | 2018-02-16 15:45:06 +0000 | [diff] [blame] | 468 | /** |
| 469 | * Creates an EntityConfig. |
| 470 | * |
| 471 | * @param hints Hints for the TextClassifier to determine what types of entities to find |
| 472 | * @param includedEntityTypes Entity types, e.g. {@link #TYPE_EMAIL}, to explicitly include |
| 473 | * @param excludedEntityTypes Entity types, e.g. {@link #TYPE_PHONE}, to explicitly exclude |
| 474 | * |
Richard Ledley | db18a57 | 2017-11-30 17:33:51 +0000 | [diff] [blame] | 475 | * |
| 476 | * Note that if an entity has been excluded, the exclusion will take precedence. |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 477 | * |
| 478 | * @deprecated Use {@link Builder} instead. |
Richard Ledley | db18a57 | 2017-11-30 17:33:51 +0000 | [diff] [blame] | 479 | */ |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 480 | @Deprecated |
Richard Ledley | 1fc998b | 2018-02-16 15:45:06 +0000 | [diff] [blame] | 481 | public static EntityConfig create(@Nullable Collection<String> hints, |
| 482 | @Nullable Collection<String> includedEntityTypes, |
| 483 | @Nullable Collection<String> excludedEntityTypes) { |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 484 | return new EntityConfig.Builder() |
| 485 | .setIncludedTypes(includedEntityTypes) |
| 486 | .setExcludedTypes(excludedEntityTypes) |
| 487 | .setHints(hints) |
| 488 | .includeTypesFromTextClassifier(true) |
| 489 | .build(); |
Richard Ledley | db18a57 | 2017-11-30 17:33:51 +0000 | [diff] [blame] | 490 | } |
| 491 | |
| 492 | /** |
Richard Ledley | 1fc998b | 2018-02-16 15:45:06 +0000 | [diff] [blame] | 493 | * Creates an EntityConfig with an explicit entity list. |
| 494 | * |
| 495 | * @param entityTypes Complete set of entities, e.g. {@link #TYPE_URL} to find. |
| 496 | * |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 497 | * @deprecated Use {@link Builder} instead. |
Richard Ledley | db18a57 | 2017-11-30 17:33:51 +0000 | [diff] [blame] | 498 | */ |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 499 | @Deprecated |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 500 | public static EntityConfig createWithExplicitEntityList( |
| 501 | @Nullable Collection<String> entityTypes) { |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 502 | return new EntityConfig.Builder() |
| 503 | .setIncludedTypes(entityTypes) |
| 504 | .includeTypesFromTextClassifier(false) |
| 505 | .build(); |
Abodunrinwa Toki | ae82e7a | 2018-04-03 23:49:16 +0100 | [diff] [blame] | 506 | } |
| 507 | |
Richard Ledley | db18a57 | 2017-11-30 17:33:51 +0000 | [diff] [blame] | 508 | /** |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 509 | * Returns a final list of entity types to find. |
Richard Ledley | 1fc998b | 2018-02-16 15:45:06 +0000 | [diff] [blame] | 510 | * |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 511 | * @param entityTypes Entity types we think should be found before factoring in |
| 512 | * includes/excludes |
Richard Ledley | 1fc998b | 2018-02-16 15:45:06 +0000 | [diff] [blame] | 513 | * |
| 514 | * This method is intended for use by TextClassifier implementations. |
Richard Ledley | db18a57 | 2017-11-30 17:33:51 +0000 | [diff] [blame] | 515 | */ |
Richard Ledley | ab669a0 | 2018-04-03 15:15:43 +0100 | [diff] [blame] | 516 | public Collection<String> resolveEntityListModifications( |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 517 | @NonNull Collection<String> entityTypes) { |
| 518 | final Set<String> finalSet = new HashSet<>(); |
| 519 | if (mIncludeTypesFromTextClassifier) { |
| 520 | finalSet.addAll(entityTypes); |
Richard Ledley | db18a57 | 2017-11-30 17:33:51 +0000 | [diff] [blame] | 521 | } |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 522 | finalSet.addAll(mIncludedTypes); |
| 523 | finalSet.removeAll(mExcludedTypes); |
Richard Ledley | ab669a0 | 2018-04-03 15:15:43 +0100 | [diff] [blame] | 524 | return finalSet; |
Richard Ledley | 1fc998b | 2018-02-16 15:45:06 +0000 | [diff] [blame] | 525 | } |
| 526 | |
| 527 | /** |
| 528 | * Retrieves the list of hints. |
| 529 | * |
| 530 | * @return An unmodifiable collection of the hints. |
| 531 | */ |
| 532 | public Collection<String> getHints() { |
| 533 | return mHints; |
Richard Ledley | db18a57 | 2017-11-30 17:33:51 +0000 | [diff] [blame] | 534 | } |
Jan Althaus | 0d9fbb9 | 2017-11-28 12:19:33 +0100 | [diff] [blame] | 535 | |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 536 | /** |
| 537 | * Return whether the client allows the text classifier to include its own list of |
| 538 | * default types. If this function returns {@code true}, a default list of types suggested |
| 539 | * from a text classifier will be taking into account. |
| 540 | * |
| 541 | * <p>NOTE: This method is intended for use by a text classifier. |
| 542 | * |
| 543 | * @see #resolveEntityListModifications(Collection) |
| 544 | */ |
| 545 | public boolean shouldIncludeTypesFromTextClassifier() { |
| 546 | return mIncludeTypesFromTextClassifier; |
Jan Althaus | 0d9fbb9 | 2017-11-28 12:19:33 +0100 | [diff] [blame] | 547 | } |
| 548 | |
| 549 | @Override |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 550 | public int describeContents() { |
| 551 | return 0; |
Jan Althaus | 0d9fbb9 | 2017-11-28 12:19:33 +0100 | [diff] [blame] | 552 | } |
| 553 | |
Jeff Sharkey | 9e8f83d | 2019-02-28 12:06:45 -0700 | [diff] [blame] | 554 | public static final @android.annotation.NonNull Parcelable.Creator<EntityConfig> CREATOR = |
Jan Althaus | 0d9fbb9 | 2017-11-28 12:19:33 +0100 | [diff] [blame] | 555 | new Parcelable.Creator<EntityConfig>() { |
| 556 | @Override |
| 557 | public EntityConfig createFromParcel(Parcel in) { |
| 558 | return new EntityConfig(in); |
| 559 | } |
| 560 | |
| 561 | @Override |
| 562 | public EntityConfig[] newArray(int size) { |
| 563 | return new EntityConfig[size]; |
| 564 | } |
| 565 | }; |
| 566 | |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 567 | |
| 568 | |
| 569 | /** Builder class to construct the {@link EntityConfig} object. */ |
| 570 | public static final class Builder { |
| 571 | @Nullable |
| 572 | private Collection<String> mIncludedTypes; |
| 573 | @Nullable |
| 574 | private Collection<String> mExcludedTypes; |
| 575 | @Nullable |
| 576 | private Collection<String> mHints; |
| 577 | private boolean mIncludeTypesFromTextClassifier = true; |
| 578 | |
| 579 | /** |
| 580 | * Sets a collection of types that are explicitly included. |
| 581 | */ |
| 582 | @NonNull |
| 583 | public Builder setIncludedTypes(@Nullable Collection<String> includedTypes) { |
| 584 | mIncludedTypes = includedTypes; |
| 585 | return this; |
| 586 | } |
| 587 | |
| 588 | /** |
| 589 | * Sets a collection of types that are explicitly excluded. |
| 590 | */ |
| 591 | @NonNull |
| 592 | public Builder setExcludedTypes(@Nullable Collection<String> excludedTypes) { |
| 593 | mExcludedTypes = excludedTypes; |
| 594 | return this; |
| 595 | } |
| 596 | |
| 597 | /** |
| 598 | * Specifies whether or not to include the types suggested by the text classifier. By |
| 599 | * default, it is included. |
| 600 | */ |
| 601 | @NonNull |
| 602 | public Builder includeTypesFromTextClassifier(boolean includeTypesFromTextClassifier) { |
| 603 | mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier; |
| 604 | return this; |
| 605 | } |
| 606 | |
| 607 | |
| 608 | /** |
| 609 | * Sets the hints for the TextClassifier to determine what types of entities to find. |
| 610 | * These hints will only be used if {@link #includeTypesFromTextClassifier} is |
| 611 | * set to be true. |
| 612 | */ |
Tony Mak | 42ab984 | 2019-03-05 15:38:51 +0000 | [diff] [blame] | 613 | @NonNull |
| 614 | public Builder setHints(@Nullable Collection<String> hints) { |
Tony Mak | ae85aae | 2019-01-09 15:59:56 +0000 | [diff] [blame] | 615 | mHints = hints; |
| 616 | return this; |
| 617 | } |
| 618 | |
| 619 | /** |
| 620 | * Combines all of the options that have been set and returns a new {@link EntityConfig} |
| 621 | * object. |
| 622 | */ |
| 623 | @NonNull |
| 624 | public EntityConfig build() { |
| 625 | return new EntityConfig( |
| 626 | mIncludedTypes == null |
| 627 | ? Collections.emptyList() |
| 628 | : new ArrayList<>(mIncludedTypes), |
| 629 | mExcludedTypes == null |
| 630 | ? Collections.emptyList() |
| 631 | : new ArrayList<>(mExcludedTypes), |
| 632 | mHints == null |
| 633 | ? Collections.emptyList() |
| 634 | : Collections.unmodifiableList(new ArrayList<>(mHints)), |
| 635 | mIncludeTypesFromTextClassifier); |
| 636 | } |
Jan Althaus | 0d9fbb9 | 2017-11-28 12:19:33 +0100 | [diff] [blame] | 637 | } |
Richard Ledley | db18a57 | 2017-11-30 17:33:51 +0000 | [diff] [blame] | 638 | } |
Abodunrinwa Toki | 4d232d6 | 2017-11-23 12:22:45 +0000 | [diff] [blame] | 639 | |
| 640 | /** |
| 641 | * Utility functions for TextClassifier methods. |
| 642 | * |
| 643 | * <ul> |
| 644 | * <li>Provides validation of input parameters to TextClassifier methods |
| 645 | * </ul> |
| 646 | * |
| 647 | * Intended to be used only in this package. |
| 648 | * @hide |
| 649 | */ |
| 650 | final class Utils { |
| 651 | |
| 652 | /** |
| 653 | * @throws IllegalArgumentException if text is null; startIndex is negative; |
| 654 | * endIndex is greater than text.length() or is not greater than startIndex; |
| 655 | * options is null |
| 656 | */ |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 657 | static void checkArgument(@NonNull CharSequence text, int startIndex, int endIndex) { |
Abodunrinwa Toki | 4d232d6 | 2017-11-23 12:22:45 +0000 | [diff] [blame] | 658 | Preconditions.checkArgument(text != null); |
| 659 | Preconditions.checkArgument(startIndex >= 0); |
| 660 | Preconditions.checkArgument(endIndex <= text.length()); |
| 661 | Preconditions.checkArgument(endIndex > startIndex); |
| 662 | } |
| 663 | |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 664 | static void checkTextLength(CharSequence text, int maxLength) { |
Jan Althaus | 108aad3 | 2018-01-30 15:26:55 +0100 | [diff] [blame] | 665 | Preconditions.checkArgumentInRange(text.length(), 0, maxLength, "text.length()"); |
| 666 | } |
| 667 | |
Abodunrinwa Toki | 6563833 | 2018-03-16 21:08:50 +0000 | [diff] [blame] | 668 | /** |
| 669 | * Generates links using legacy {@link Linkify}. |
| 670 | */ |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 671 | public static TextLinks generateLegacyLinks(@NonNull TextLinks.Request request) { |
| 672 | final String string = request.getText().toString(); |
Abodunrinwa Toki | 6563833 | 2018-03-16 21:08:50 +0000 | [diff] [blame] | 673 | final TextLinks.Builder links = new TextLinks.Builder(string); |
| 674 | |
Richard Ledley | ab669a0 | 2018-04-03 15:15:43 +0100 | [diff] [blame] | 675 | final Collection<String> entities = request.getEntityConfig() |
Abodunrinwa Toki | 6563833 | 2018-03-16 21:08:50 +0000 | [diff] [blame] | 676 | .resolveEntityListModifications(Collections.emptyList()); |
| 677 | if (entities.contains(TextClassifier.TYPE_URL)) { |
| 678 | addLinks(links, string, TextClassifier.TYPE_URL); |
| 679 | } |
| 680 | if (entities.contains(TextClassifier.TYPE_PHONE)) { |
| 681 | addLinks(links, string, TextClassifier.TYPE_PHONE); |
| 682 | } |
| 683 | if (entities.contains(TextClassifier.TYPE_EMAIL)) { |
| 684 | addLinks(links, string, TextClassifier.TYPE_EMAIL); |
| 685 | } |
| 686 | // NOTE: Do not support MAP_ADDRESSES. Legacy version does not work well. |
| 687 | return links.build(); |
| 688 | } |
| 689 | |
| 690 | private static void addLinks( |
| 691 | TextLinks.Builder links, String string, @EntityType String entityType) { |
| 692 | final Spannable spannable = new SpannableString(string); |
| 693 | if (Linkify.addLinks(spannable, linkMask(entityType))) { |
| 694 | final URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class); |
| 695 | for (URLSpan urlSpan : spans) { |
| 696 | links.addLink( |
| 697 | spannable.getSpanStart(urlSpan), |
| 698 | spannable.getSpanEnd(urlSpan), |
| 699 | entityScores(entityType), |
| 700 | urlSpan); |
| 701 | } |
| 702 | } |
| 703 | } |
| 704 | |
| 705 | @LinkifyMask |
| 706 | private static int linkMask(@EntityType String entityType) { |
| 707 | switch (entityType) { |
| 708 | case TextClassifier.TYPE_URL: |
| 709 | return Linkify.WEB_URLS; |
| 710 | case TextClassifier.TYPE_PHONE: |
| 711 | return Linkify.PHONE_NUMBERS; |
| 712 | case TextClassifier.TYPE_EMAIL: |
| 713 | return Linkify.EMAIL_ADDRESSES; |
| 714 | default: |
| 715 | // NOTE: Do not support MAP_ADDRESSES. Legacy version does not work well. |
| 716 | return 0; |
| 717 | } |
| 718 | } |
| 719 | |
| 720 | private static Map<String, Float> entityScores(@EntityType String entityType) { |
| 721 | final Map<String, Float> scores = new ArrayMap<>(); |
| 722 | scores.put(entityType, 1f); |
| 723 | return scores; |
| 724 | } |
| 725 | |
Abodunrinwa Toki | 080c854 | 2018-03-27 00:04:06 +0100 | [diff] [blame] | 726 | static void checkMainThread() { |
| 727 | if (Looper.myLooper() == Looper.getMainLooper()) { |
| 728 | Log.w(DEFAULT_LOG_TAG, "TextClassifier called on main thread"); |
Abodunrinwa Toki | d32906c | 2018-01-18 04:34:44 -0800 | [diff] [blame] | 729 | } |
Abodunrinwa Toki | 4d232d6 | 2017-11-23 12:22:45 +0000 | [diff] [blame] | 730 | } |
| 731 | } |
Abodunrinwa Toki | f001fef | 2017-01-04 23:51:42 +0000 | [diff] [blame] | 732 | } |