Merge "Adding intent generation for dates and flights"
This commit is contained in:
committed by
Android (Google) Code Review
commit
e2975162dc
@@ -49896,7 +49896,9 @@ package android.view.textclassifier {
|
||||
ctor public TextClassification.Options();
|
||||
method public int describeContents();
|
||||
method public android.os.LocaleList getDefaultLocales();
|
||||
method public java.util.Calendar getReferenceTime();
|
||||
method public android.view.textclassifier.TextClassification.Options setDefaultLocales(android.os.LocaleList);
|
||||
method public android.view.textclassifier.TextClassification.Options setReferenceTime(java.util.Calendar);
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassification.Options> CREATOR;
|
||||
}
|
||||
@@ -49921,7 +49923,10 @@ package android.view.textclassifier {
|
||||
field public static final int ENTITY_PRESET_NONE = 1; // 0x1
|
||||
field public static final android.view.textclassifier.TextClassifier NO_OP;
|
||||
field public static final java.lang.String TYPE_ADDRESS = "address";
|
||||
field public static final java.lang.String TYPE_DATE = "date";
|
||||
field public static final java.lang.String TYPE_DATE_TIME = "datetime";
|
||||
field public static final java.lang.String TYPE_EMAIL = "email";
|
||||
field public static final java.lang.String TYPE_FLIGHT_NUMBER = "flight";
|
||||
field public static final java.lang.String TYPE_OTHER = "other";
|
||||
field public static final java.lang.String TYPE_PHONE = "phone";
|
||||
field public static final java.lang.String TYPE_UNKNOWN = "";
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.view.textclassifier;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
|
||||
/**
|
||||
@@ -146,11 +147,24 @@ final class SmartSelection {
|
||||
final String mCollection;
|
||||
/** float range: 0 - 1 */
|
||||
final float mScore;
|
||||
@Nullable final DatetimeParseResult mDatetime;
|
||||
|
||||
ClassificationResult(String collection, float score) {
|
||||
mCollection = collection;
|
||||
mScore = score;
|
||||
mDatetime = null;
|
||||
}
|
||||
|
||||
ClassificationResult(String collection, float score, DatetimeParseResult datetime) {
|
||||
mCollection = collection;
|
||||
mScore = score;
|
||||
mDatetime = datetime;
|
||||
}
|
||||
}
|
||||
|
||||
/** Parsed date information for the classification result. */
|
||||
static final class DatetimeParseResult {
|
||||
long mMsSinceEpoch;
|
||||
}
|
||||
|
||||
/** Represents a result of Annotate call. */
|
||||
|
||||
@@ -36,6 +36,7 @@ import android.view.textclassifier.TextClassifier.EntityType;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@@ -592,6 +593,7 @@ public final class TextClassification {
|
||||
public static final class Options implements Parcelable {
|
||||
|
||||
private @Nullable LocaleList mDefaultLocales;
|
||||
private @Nullable Calendar mReferenceTime;
|
||||
|
||||
public Options() {}
|
||||
|
||||
@@ -605,6 +607,16 @@ public final class TextClassification {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param referenceTime reference time based on which relative dates (e.g. "tomorrow" should
|
||||
* be interpreted. This should usually be the time when the text was originally
|
||||
* composed. If no reference time is set, now is used.
|
||||
*/
|
||||
public Options setReferenceTime(Calendar referenceTime) {
|
||||
mReferenceTime = referenceTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ordered list of locale preferences that can be used to disambiguate
|
||||
* the provided text.
|
||||
@@ -614,6 +626,15 @@ public final class TextClassification {
|
||||
return mDefaultLocales;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return reference time based on which relative dates (e.g. "tomorrow") should be
|
||||
* interpreted.
|
||||
*/
|
||||
@Nullable
|
||||
public Calendar getReferenceTime() {
|
||||
return mReferenceTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
@@ -625,6 +646,10 @@ public final class TextClassification {
|
||||
if (mDefaultLocales != null) {
|
||||
mDefaultLocales.writeToParcel(dest, flags);
|
||||
}
|
||||
dest.writeInt(mReferenceTime != null ? 1 : 0);
|
||||
if (mReferenceTime != null) {
|
||||
dest.writeSerializable(mReferenceTime);
|
||||
}
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<Options> CREATOR =
|
||||
@@ -644,6 +669,9 @@ public final class TextClassification {
|
||||
if (in.readInt() > 0) {
|
||||
mDefaultLocales = LocaleList.CREATOR.createFromParcel(in);
|
||||
}
|
||||
if (in.readInt() > 0) {
|
||||
mReferenceTime = (Calendar) in.readSerializable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,12 +47,26 @@ public interface TextClassifier {
|
||||
/** @hide */
|
||||
String DEFAULT_LOG_TAG = "androidtc";
|
||||
|
||||
/** The TextClassifier failed to run. */
|
||||
String TYPE_UNKNOWN = "";
|
||||
/** The classifier ran, but didn't recognize a known entity. */
|
||||
String TYPE_OTHER = "other";
|
||||
/** E-mail address (e.g. "noreply@android.com"). */
|
||||
String TYPE_EMAIL = "email";
|
||||
/** Phone number (e.g. "555-123 456"). */
|
||||
String TYPE_PHONE = "phone";
|
||||
/** Physical address. */
|
||||
String TYPE_ADDRESS = "address";
|
||||
/** Web URL. */
|
||||
String TYPE_URL = "url";
|
||||
/** Time reference that is no more specific than a date. May be absolute such as "01/01/2000" or
|
||||
* relative like "tomorrow". **/
|
||||
String TYPE_DATE = "date";
|
||||
/** Time reference that includes a specific time. May be absolute such as "01/01/2000 5:30pm" or
|
||||
* relative like "tomorrow at 5:30pm". **/
|
||||
String TYPE_DATE_TIME = "datetime";
|
||||
/** Flight number in IATA format. */
|
||||
String TYPE_FLIGHT_NUMBER = "flight";
|
||||
|
||||
/** @hide */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@@ -63,6 +77,9 @@ public interface TextClassifier {
|
||||
TYPE_PHONE,
|
||||
TYPE_ADDRESS,
|
||||
TYPE_URL,
|
||||
TYPE_DATE,
|
||||
TYPE_DATE_TIME,
|
||||
TYPE_FLIGHT_NUMBER,
|
||||
})
|
||||
@interface EntityType {}
|
||||
|
||||
|
||||
@@ -18,7 +18,9 @@ package android.view.textclassifier;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.SearchManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -28,6 +30,7 @@ import android.net.Uri;
|
||||
import android.os.LocaleList;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.provider.Browser;
|
||||
import android.provider.CalendarContract;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.Settings;
|
||||
import android.text.util.Linkify;
|
||||
@@ -42,6 +45,7 @@ import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -49,6 +53,7 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -73,7 +78,10 @@ final class TextClassifierImpl implements TextClassifier {
|
||||
TextClassifier.TYPE_ADDRESS,
|
||||
TextClassifier.TYPE_EMAIL,
|
||||
TextClassifier.TYPE_PHONE,
|
||||
TextClassifier.TYPE_URL));
|
||||
TextClassifier.TYPE_URL,
|
||||
TextClassifier.TYPE_DATE,
|
||||
TextClassifier.TYPE_DATE_TIME,
|
||||
TextClassifier.TYPE_FLIGHT_NUMBER));
|
||||
private static final List<String> ENTITY_TYPES_BASE =
|
||||
Collections.unmodifiableList(Arrays.asList(
|
||||
TextClassifier.TYPE_ADDRESS,
|
||||
@@ -167,9 +175,8 @@ final class TextClassifierImpl implements TextClassifier {
|
||||
.classifyText(string, startIndex, endIndex,
|
||||
getHintFlags(string, startIndex, endIndex));
|
||||
if (results.length > 0) {
|
||||
final TextClassification classificationResult =
|
||||
createClassificationResult(results, string, startIndex, endIndex);
|
||||
return classificationResult;
|
||||
return createClassificationResult(
|
||||
results, string, startIndex, endIndex, options.getReferenceTime());
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
@@ -410,18 +417,24 @@ final class TextClassifierImpl implements TextClassifier {
|
||||
|
||||
private TextClassification createClassificationResult(
|
||||
SmartSelection.ClassificationResult[] classifications,
|
||||
String text, int start, int end) {
|
||||
String text, int start, int end, @Nullable Calendar referenceTime) {
|
||||
final String classifiedText = text.substring(start, end);
|
||||
final TextClassification.Builder builder = new TextClassification.Builder()
|
||||
.setText(classifiedText);
|
||||
|
||||
final int size = classifications.length;
|
||||
SmartSelection.ClassificationResult highestScoringResult = null;
|
||||
float highestScore = Float.MIN_VALUE;
|
||||
for (int i = 0; i < size; i++) {
|
||||
builder.setEntityType(classifications[i].mCollection, classifications[i].mScore);
|
||||
if (classifications[i].mScore > highestScore) {
|
||||
highestScoringResult = classifications[i];
|
||||
highestScore = classifications[i].mScore;
|
||||
}
|
||||
}
|
||||
|
||||
final String type = getHighestScoringType(classifications);
|
||||
addActions(builder, IntentFactory.create(mContext, type, classifiedText));
|
||||
addActions(builder, IntentFactory.create(
|
||||
mContext, referenceTime, highestScoringResult, classifiedText));
|
||||
|
||||
return builder.setSignature(getSignature(text, start, end)).build();
|
||||
}
|
||||
@@ -441,11 +454,10 @@ final class TextClassifierImpl implements TextClassifier {
|
||||
}
|
||||
if (resolveInfo != null && resolveInfo.activityInfo != null) {
|
||||
final String packageName = resolveInfo.activityInfo.packageName;
|
||||
CharSequence label;
|
||||
final String label = IntentFactory.getLabel(mContext, intent);
|
||||
Drawable icon;
|
||||
if ("android".equals(packageName)) {
|
||||
// Requires the chooser to find an activity to handle the intent.
|
||||
label = IntentFactory.getLabel(mContext, intent);
|
||||
icon = null;
|
||||
} else {
|
||||
// A default activity will handle the intent.
|
||||
@@ -455,16 +467,11 @@ final class TextClassifierImpl implements TextClassifier {
|
||||
if (icon == null) {
|
||||
icon = resolveInfo.loadIcon(pm);
|
||||
}
|
||||
label = resolveInfo.activityInfo.loadLabel(pm);
|
||||
if (label == null) {
|
||||
label = resolveInfo.loadLabel(pm);
|
||||
}
|
||||
}
|
||||
final String labelString = (label != null) ? label.toString() : null;
|
||||
if (i == 0) {
|
||||
builder.setPrimaryAction(intent, labelString, icon);
|
||||
builder.setPrimaryAction(intent, label, icon);
|
||||
} else {
|
||||
builder.addSecondaryAction(intent, labelString, icon);
|
||||
builder.addSecondaryAction(intent, label, icon);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -483,23 +490,6 @@ final class TextClassifierImpl implements TextClassifier {
|
||||
return flag;
|
||||
}
|
||||
|
||||
private static String getHighestScoringType(SmartSelection.ClassificationResult[] types) {
|
||||
if (types.length < 1) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String type = types[0].mCollection;
|
||||
float highestScore = types[0].mScore;
|
||||
final int size = types.length;
|
||||
for (int i = 1; i < size; i++) {
|
||||
if (types[i].mScore > highestScore) {
|
||||
type = types[i].mCollection;
|
||||
highestScore = types[i].mScore;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the ParcelFileDescriptor and logs any errors that occur.
|
||||
*/
|
||||
@@ -514,58 +504,139 @@ final class TextClassifierImpl implements TextClassifier {
|
||||
/**
|
||||
* Creates intents based on the classification type.
|
||||
*/
|
||||
private static final class IntentFactory {
|
||||
static final class IntentFactory {
|
||||
|
||||
private static final long MIN_EVENT_FUTURE_MILLIS = TimeUnit.MINUTES.toMillis(5);
|
||||
private static final long DEFAULT_EVENT_DURATION = TimeUnit.HOURS.toMillis(1);
|
||||
|
||||
private IntentFactory() {}
|
||||
|
||||
@NonNull
|
||||
public static List<Intent> create(Context context, String type, String text) {
|
||||
final List<Intent> intents = new ArrayList<>();
|
||||
type = type.trim().toLowerCase(Locale.ENGLISH);
|
||||
public static List<Intent> create(
|
||||
Context context,
|
||||
@Nullable Calendar referenceTime,
|
||||
SmartSelection.ClassificationResult classification,
|
||||
String text) {
|
||||
final String type = classification.mCollection.trim().toLowerCase(Locale.ENGLISH);
|
||||
text = text.trim();
|
||||
switch (type) {
|
||||
case TextClassifier.TYPE_EMAIL:
|
||||
intents.add(new Intent(Intent.ACTION_SENDTO)
|
||||
.setData(Uri.parse(String.format("mailto:%s", text))));
|
||||
intents.add(new Intent(Intent.ACTION_INSERT_OR_EDIT)
|
||||
return createForEmail(text);
|
||||
case TextClassifier.TYPE_PHONE:
|
||||
return createForPhone(text);
|
||||
case TextClassifier.TYPE_ADDRESS:
|
||||
return createForAddress(text);
|
||||
case TextClassifier.TYPE_URL:
|
||||
return createForUrl(context, text);
|
||||
case TextClassifier.TYPE_DATE:
|
||||
case TextClassifier.TYPE_DATE_TIME:
|
||||
if (classification.mDatetime != null) {
|
||||
Calendar eventTime = Calendar.getInstance();
|
||||
eventTime.setTimeInMillis(classification.mDatetime.mMsSinceEpoch);
|
||||
return createForDatetime(type, referenceTime, eventTime);
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
case TextClassifier.TYPE_FLIGHT_NUMBER:
|
||||
return createForFlight(text);
|
||||
default:
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static List<Intent> createForEmail(String text) {
|
||||
return Arrays.asList(
|
||||
new Intent(Intent.ACTION_SENDTO)
|
||||
.setData(Uri.parse(String.format("mailto:%s", text))),
|
||||
new Intent(Intent.ACTION_INSERT_OR_EDIT)
|
||||
.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
|
||||
.putExtra(ContactsContract.Intents.Insert.EMAIL, text));
|
||||
break;
|
||||
case TextClassifier.TYPE_PHONE:
|
||||
intents.add(new Intent(Intent.ACTION_DIAL)
|
||||
.setData(Uri.parse(String.format("tel:%s", text))));
|
||||
intents.add(new Intent(Intent.ACTION_INSERT_OR_EDIT)
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static List<Intent> createForPhone(String text) {
|
||||
return Arrays.asList(
|
||||
new Intent(Intent.ACTION_DIAL)
|
||||
.setData(Uri.parse(String.format("tel:%s", text))),
|
||||
new Intent(Intent.ACTION_INSERT_OR_EDIT)
|
||||
.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
|
||||
.putExtra(ContactsContract.Intents.Insert.PHONE, text));
|
||||
intents.add(new Intent(Intent.ACTION_SENDTO)
|
||||
.putExtra(ContactsContract.Intents.Insert.PHONE, text),
|
||||
new Intent(Intent.ACTION_SENDTO)
|
||||
.setData(Uri.parse(String.format("smsto:%s", text))));
|
||||
break;
|
||||
case TextClassifier.TYPE_ADDRESS:
|
||||
intents.add(new Intent(Intent.ACTION_VIEW)
|
||||
.setData(Uri.parse(String.format("geo:0,0?q=%s", text))));
|
||||
break;
|
||||
case TextClassifier.TYPE_URL:
|
||||
final String httpPrefix = "http://";
|
||||
final String httpsPrefix = "https://";
|
||||
if (text.toLowerCase().startsWith(httpPrefix)) {
|
||||
text = httpPrefix + text.substring(httpPrefix.length());
|
||||
} else if (text.toLowerCase().startsWith(httpsPrefix)) {
|
||||
text = httpsPrefix + text.substring(httpsPrefix.length());
|
||||
} else {
|
||||
text = httpPrefix + text;
|
||||
}
|
||||
intents.add(new Intent(Intent.ACTION_VIEW, Uri.parse(text))
|
||||
.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()));
|
||||
break;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static List<Intent> createForAddress(String text) {
|
||||
return Arrays.asList(new Intent(Intent.ACTION_VIEW)
|
||||
.setData(Uri.parse(String.format("geo:0,0?q=%s", text))));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static List<Intent> createForUrl(Context context, String text) {
|
||||
final String httpPrefix = "http://";
|
||||
final String httpsPrefix = "https://";
|
||||
if (text.toLowerCase().startsWith(httpPrefix)) {
|
||||
text = httpPrefix + text.substring(httpPrefix.length());
|
||||
} else if (text.toLowerCase().startsWith(httpsPrefix)) {
|
||||
text = httpsPrefix + text.substring(httpsPrefix.length());
|
||||
} else {
|
||||
text = httpPrefix + text;
|
||||
}
|
||||
return Arrays.asList(new Intent(Intent.ACTION_VIEW, Uri.parse(text))
|
||||
.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static List<Intent> createForDatetime(
|
||||
String type, @Nullable Calendar referenceTime, Calendar eventTime) {
|
||||
if (referenceTime == null) {
|
||||
// If no reference time was given, use now.
|
||||
referenceTime = Calendar.getInstance();
|
||||
}
|
||||
List<Intent> intents = new ArrayList<>();
|
||||
intents.add(createCalendarViewIntent(eventTime));
|
||||
final long millisSinceReference =
|
||||
eventTime.getTimeInMillis() - referenceTime.getTimeInMillis();
|
||||
if (millisSinceReference > MIN_EVENT_FUTURE_MILLIS) {
|
||||
intents.add(createCalendarCreateEventIntent(eventTime, type));
|
||||
}
|
||||
return intents;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static List<Intent> createForFlight(String text) {
|
||||
return Arrays.asList(new Intent(Intent.ACTION_WEB_SEARCH)
|
||||
.putExtra(SearchManager.QUERY, text));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static Intent createCalendarViewIntent(Calendar eventTime) {
|
||||
Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
|
||||
builder.appendPath("time");
|
||||
ContentUris.appendId(builder, eventTime.getTimeInMillis());
|
||||
return new Intent(Intent.ACTION_VIEW).setData(builder.build());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static Intent createCalendarCreateEventIntent(
|
||||
Calendar eventTime, @EntityType String type) {
|
||||
final boolean isAllDay = TextClassifier.TYPE_DATE.equals(type);
|
||||
return new Intent(Intent.ACTION_INSERT)
|
||||
.setData(CalendarContract.Events.CONTENT_URI)
|
||||
.putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay)
|
||||
.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, eventTime.getTimeInMillis())
|
||||
.putExtra(CalendarContract.EXTRA_EVENT_END_TIME,
|
||||
eventTime.getTimeInMillis() + DEFAULT_EVENT_DURATION);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getLabel(Context context, @Nullable Intent intent) {
|
||||
if (intent == null || intent.getAction() == null) {
|
||||
return null;
|
||||
}
|
||||
final String authority =
|
||||
intent.getData() == null ? null : intent.getData().getAuthority();
|
||||
switch (intent.getAction()) {
|
||||
case Intent.ACTION_DIAL:
|
||||
return context.getString(com.android.internal.R.string.dial);
|
||||
@@ -578,6 +649,11 @@ final class TextClassifierImpl implements TextClassifier {
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
case Intent.ACTION_INSERT:
|
||||
if (CalendarContract.AUTHORITY.equals(authority)) {
|
||||
return context.getString(com.android.internal.R.string.add_calendar_event);
|
||||
}
|
||||
return null;
|
||||
case Intent.ACTION_INSERT_OR_EDIT:
|
||||
switch (intent.getDataString()) {
|
||||
case ContactsContract.Contacts.CONTENT_ITEM_TYPE:
|
||||
@@ -586,6 +662,9 @@ final class TextClassifierImpl implements TextClassifier {
|
||||
return null;
|
||||
}
|
||||
case Intent.ACTION_VIEW:
|
||||
if (CalendarContract.AUTHORITY.equals(authority)) {
|
||||
return context.getString(com.android.internal.R.string.view_calendar);
|
||||
}
|
||||
switch (intent.getScheme()) {
|
||||
case "geo":
|
||||
return context.getString(com.android.internal.R.string.map);
|
||||
@@ -595,6 +674,8 @@ final class TextClassifierImpl implements TextClassifier {
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
case Intent.ACTION_WEB_SEARCH:
|
||||
return context.getString(com.android.internal.R.string.view_flight);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2731,6 +2731,15 @@
|
||||
<!-- Label for item in the text selection menu to trigger adding a contact [CHAR LIMIT=20] -->
|
||||
<string name="add_contact">Add</string>
|
||||
|
||||
<!-- Label for item in the text selection menu to view the calendar for the selected time/date [CHAR LIMIT=20] -->
|
||||
<string name="view_calendar">View</string>
|
||||
|
||||
<!-- Label for item in the text selection menu to create a calendar event at the selected time/date [CHAR LIMIT=20] -->
|
||||
<string name="add_calendar_event">Schedule</string>
|
||||
|
||||
<!-- Label for item in the text selection menu to track a selected flight number [CHAR LIMIT=20] -->
|
||||
<string name="view_flight">Track</string>
|
||||
|
||||
<!-- If the device is getting low on internal storage, a notification is shown to the user. This is the title of that notification. -->
|
||||
<string name="low_internal_storage_view_title">Storage space running out</string>
|
||||
<!-- If the device is getting low on internal storage, a notification is shown to the user. This is the message of that notification. -->
|
||||
|
||||
@@ -533,6 +533,9 @@
|
||||
<java-symbol type="string" name="browse" />
|
||||
<java-symbol type="string" name="sms" />
|
||||
<java-symbol type="string" name="add_contact" />
|
||||
<java-symbol type="string" name="view_calendar" />
|
||||
<java-symbol type="string" name="add_calendar_event" />
|
||||
<java-symbol type="string" name="view_flight" />
|
||||
<java-symbol type="string" name="textSelectionCABTitle" />
|
||||
<java-symbol type="string" name="BaMmi" />
|
||||
<java-symbol type="string" name="CLIRDefaultOffNextCallOff" />
|
||||
|
||||
@@ -32,7 +32,9 @@ import android.view.View;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@@ -146,8 +148,12 @@ public class TextClassificationTest {
|
||||
|
||||
@Test
|
||||
public void testParcelOptions() {
|
||||
Calendar referenceTime = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.US);
|
||||
referenceTime.setTimeInMillis(946771200000L); // 2000-01-02
|
||||
|
||||
TextClassification.Options reference = new TextClassification.Options();
|
||||
reference.setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY));
|
||||
reference.setReferenceTime(referenceTime);
|
||||
|
||||
// Parcel and unparcel.
|
||||
final Parcel parcel = Parcel.obtain();
|
||||
@@ -157,5 +163,6 @@ public class TextClassificationTest {
|
||||
parcel);
|
||||
|
||||
assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
|
||||
assertEquals(referenceTime, result.getReferenceTime());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user