Loading core/java/android/text/LangId.java 0 → 100644 +60 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.text; /** * Java wrapper for LangId native library interface. * This class is used to detect languages in text. * @hide */ public final class LangId { // TODO: Move this to android.view.textclassifier and make it package-private. // We'll have to update the native library code to do this. static { System.loadLibrary("smart-selection_jni"); } private final long mModelPtr; /** * Creates a new instance of LangId predictor, using the provided model image. */ public LangId(int fd) { mModelPtr = nativeNew(fd); } /** * Detects the language for given text. */ public String findLanguage(String text) { return nativeFindLanguage(mModelPtr, text); } /** * Frees up the allocated memory. */ public void close() { nativeClose(mModelPtr); } private static native long nativeNew(int fd); private static native String nativeFindLanguage(long context, String text); private static native void nativeClose(long context); } core/java/android/text/SmartSelection.java 0 → 100644 +84 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.text; /** * Java wrapper for SmartSelection native library interface. * This library is used for detecting entities in text. * @hide */ public final class SmartSelection { // TODO: Move this to android.view.textclassifier and make it package-private. // We'll have to update the native library code to do this. static { System.loadLibrary("smart-selection_jni"); } private final long mCtx; /** * Creates a new instance of SmartSelect predictor, using the provided model image, * given as a file descriptor. */ public SmartSelection(int fd) { mCtx = nativeNew(fd); } /** * Given a string context and current selection, computes the SmartSelection suggestion. * * The begin and end are character indices into the context UTF8 string. selectionBegin is the * character index where the selection begins, and selectionEnd is the index of one character * past the selection span. * * The return value is an array of two ints: suggested selection beginning and end, with the * same semantics as the input selectionBeginning and selectionEnd. */ public int[] suggest(String context, int selectionBegin, int selectionEnd) { return nativeSuggest(mCtx, context, selectionBegin, selectionEnd); } /** * Given a string context and current selection, classifies the type of the selected text. * * The begin and end params are character indices in the context string. * * Returns the type of the selection, e.g. "email", "address", "phone". */ public String classifyText(String context, int selectionBegin, int selectionEnd) { return nativeClassifyText(mCtx, context, selectionBegin, selectionEnd); } /** * Frees up the allocated memory. */ public void close() { nativeClose(mCtx); } private static native long nativeNew(int fd); private static native int[] nativeSuggest( long context, String text, int selectionBegin, int selectionEnd); private static native String nativeClassifyText( long context, String text, int selectionBegin, int selectionEnd); private static native void nativeClose(long context); } core/java/android/view/textclassifier/TextClassificationManager.java +56 −3 Original line number Diff line number Diff line Loading @@ -18,9 +18,18 @@ package android.view.textclassifier; import android.annotation.NonNull; import android.content.Context; import android.os.ParcelFileDescriptor; import android.text.LangId; import android.util.Log; import com.android.internal.util.Preconditions; import java.io.File; import java.io.FileNotFoundException; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; /** * Interface to the text classification service. Loading @@ -30,14 +39,35 @@ import java.util.List; */ public final class TextClassificationManager { private static final String LOG_TAG = "TextClassificationManager"; private final Context mContext; // TODO: Implement a way to close the file descriptor. private ParcelFileDescriptor mFd; private TextClassifier mDefault; private LangId mLangId; /** @hide */ public TextClassificationManager(Context context) {} public TextClassificationManager(Context context) { mContext = Preconditions.checkNotNull(context); } /** * Returns the default text classifier. */ public TextClassifier getDefaultTextClassifier() { return TextClassifier.NO_OP; if (mDefault == null) { try { mFd = ParcelFileDescriptor.open( new File("/etc/assistant/smart-selection.model"), ParcelFileDescriptor.MODE_READ_ONLY); mDefault = new TextClassifierImpl(mContext, mFd); } catch (FileNotFoundException e) { Log.e(LOG_TAG, "Error accessing 'text classifier selection' model file.", e); mDefault = TextClassifier.NO_OP; } } return mDefault; } /** Loading @@ -47,7 +77,30 @@ public final class TextClassificationManager { * @throws IllegalArgumentException if text is null */ public List<TextLanguage> detectLanguages(@NonNull CharSequence text) { // TODO: Implement Preconditions.checkArgument(text != null); try { if (text.length() > 0) { final String language = getLanguageDetector().findLanguage(text.toString()); final Locale locale = new Locale.Builder().setLanguageTag(language).build(); return Collections.unmodifiableList(Arrays.asList( new TextLanguage.Builder(0, text.length()) .setLanguage(locale, 1.0f /* confidence */) .build())); } } catch (Throwable t) { // Avoid throwing from this method. Log the error. Log.e(LOG_TAG, "Error detecting languages for text. Returning empty result.", t); } // Getting here means something went wrong. Return an empty result. return Collections.emptyList(); } private LangId getLanguageDetector() { if (mLangId == null) { // TODO: Use a file descriptor as soon as we start to depend on a model file // for language detection. mLangId = new LangId(0); } return mLangId; } } core/java/android/view/textclassifier/TextClassifierImpl.java 0 → 100644 +180 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view.textclassifier; import android.annotation.NonNull; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.ParcelFileDescriptor; import android.text.SmartSelection; import android.text.TextUtils; import android.util.Log; import com.android.internal.util.Preconditions; import java.io.FileNotFoundException; /** * Default implementation of the {@link TextClassifier} interface. * * <p>This class uses machine learning to recognize entities in text. * Unless otherwise stated, methods of this class are blocking operations and should most * likely not be called on the UI thread. * * @hide */ final class TextClassifierImpl implements TextClassifier { private static final String LOG_TAG = "TextClassifierImpl"; private final Context mContext; private final ParcelFileDescriptor mFd; private SmartSelection mSmartSelection; TextClassifierImpl(Context context, ParcelFileDescriptor fd) { mContext = Preconditions.checkNotNull(context); mFd = Preconditions.checkNotNull(fd); } @Override public TextSelection suggestSelection( @NonNull CharSequence text, int selectionStartIndex, int selectionEndIndex) { validateInput(text, selectionStartIndex, selectionEndIndex); try { if (text.length() > 0) { final String string = text.toString(); final int[] startEnd = getSmartSelection() .suggest(string, selectionStartIndex, selectionEndIndex); final int start = startEnd[0]; final int end = startEnd[1]; if (start >= 0 && end <= string.length() && start <= end) { final String type = getSmartSelection().classifyText(string, start, end); return new TextSelection.Builder(start, end) .setEntityType(type, 1.0f) .build(); } else { // We can not trust the result. Log the issue and ignore the result. Log.d(LOG_TAG, "Got bad indices for input text. Ignoring result."); } } } catch (Throwable t) { // Avoid throwing from this method. Log the error. Log.e(LOG_TAG, "Error suggesting selection for text. No changes to selection suggested.", t); } // Getting here means something went wrong, return a NO_OP result. return TextClassifier.NO_OP.suggestSelection( text, selectionStartIndex, selectionEndIndex); } @Override public TextClassificationResult getTextClassificationResult( @NonNull CharSequence text, int startIndex, int endIndex) { validateInput(text, startIndex, endIndex); try { if (text.length() > 0) { final CharSequence classified = text.subSequence(startIndex, endIndex); String type = getSmartSelection() .classifyText(text.toString(), startIndex, endIndex); if (!TextUtils.isEmpty(type)) { type = type.toLowerCase().trim(); // TODO: Added this log for debug only. Remove before release. Log.d(LOG_TAG, String.format("Classification type: %s", type)); final Intent intent; final String title; switch (type) { case TextClassifier.TYPE_EMAIL: intent = new Intent(Intent.ACTION_SENDTO); intent.setData(Uri.parse(String.format("mailto:%s", text))); title = mContext.getString(com.android.internal.R.string.email); return createClassificationResult(classified, type, intent, title); case TextClassifier.TYPE_PHONE: intent = new Intent(Intent.ACTION_DIAL); intent.setData(Uri.parse(String.format("tel:%s", text))); title = mContext.getString(com.android.internal.R.string.dial); return createClassificationResult(classified, type, intent, title); case TextClassifier.TYPE_ADDRESS: intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(String.format("geo:0,0?q=%s", text))); title = mContext.getString(com.android.internal.R.string.map); return createClassificationResult(classified, type, intent, title); default: // No classification type found. Return a no-op result. break; // TODO: Add other classification types. } } } } catch (Throwable t) { // Avoid throwing from this method. Log the error. Log.e(LOG_TAG, "Error getting assist info.", t); } // Getting here means something went wrong, return a NO_OP result. return TextClassifier.NO_OP.getTextClassificationResult(text, startIndex, endIndex); } @Override public LinksInfo getLinks(@NonNull CharSequence text, int linkMask) { // TODO: Implement return TextClassifier.NO_OP.getLinks(text, linkMask); } private synchronized SmartSelection getSmartSelection() throws FileNotFoundException { if (mSmartSelection == null) { mSmartSelection = new SmartSelection(mFd.getFd()); } return mSmartSelection; } private TextClassificationResult createClassificationResult( CharSequence text, String type, Intent intent, String label) { TextClassificationResult.Builder builder = new TextClassificationResult.Builder() .setText(text.toString()) .setEntityType(type, 1.0f /* confidence */) .setIntent(intent) .setOnClickListener(TextClassificationResult.createStartActivityOnClick( mContext, intent)) .setLabel(label); PackageManager pm = mContext.getPackageManager(); ResolveInfo resolveInfo = pm.resolveActivity(intent, 0); // TODO: If the resolveInfo is the "chooser", do not set the package name and use a // default icon for this classification type. intent.setPackage(resolveInfo.activityInfo.packageName); Drawable icon = resolveInfo.activityInfo.loadIcon(pm); if (icon == null) { icon = resolveInfo.loadIcon(pm); } builder.setIcon(icon); return builder.build(); } /** * @throws IllegalArgumentException if text is null; startIndex is negative; * endIndex is greater than text.length() or less than startIndex */ private static void validateInput(@NonNull CharSequence text, int startIndex, int endIndex) { Preconditions.checkArgument(text != null); Preconditions.checkArgument(startIndex >= 0); Preconditions.checkArgument(endIndex <= text.length()); Preconditions.checkArgument(endIndex >= startIndex); } } core/res/res/values/strings.xml +9 −0 Original line number Diff line number Diff line Loading @@ -2589,6 +2589,15 @@ <!-- Title for EditText context menu [CHAR LIMIT=20] --> <string name="editTextMenuTitle">Text actions</string> <!-- Label for item in the text selection menu to trigger an Email app [CHAR LIMIT=20] --> <string name="email">Email</string> <!-- Label for item in the text selection menu to trigger a Dialer app [CHAR LIMIT=20] --> <string name="dial">Dial</string> <!-- Label for item in the text selection menu to trigger a Map app [CHAR LIMIT=20] --> <string name="map">Map</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. --> Loading Loading
core/java/android/text/LangId.java 0 → 100644 +60 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.text; /** * Java wrapper for LangId native library interface. * This class is used to detect languages in text. * @hide */ public final class LangId { // TODO: Move this to android.view.textclassifier and make it package-private. // We'll have to update the native library code to do this. static { System.loadLibrary("smart-selection_jni"); } private final long mModelPtr; /** * Creates a new instance of LangId predictor, using the provided model image. */ public LangId(int fd) { mModelPtr = nativeNew(fd); } /** * Detects the language for given text. */ public String findLanguage(String text) { return nativeFindLanguage(mModelPtr, text); } /** * Frees up the allocated memory. */ public void close() { nativeClose(mModelPtr); } private static native long nativeNew(int fd); private static native String nativeFindLanguage(long context, String text); private static native void nativeClose(long context); }
core/java/android/text/SmartSelection.java 0 → 100644 +84 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.text; /** * Java wrapper for SmartSelection native library interface. * This library is used for detecting entities in text. * @hide */ public final class SmartSelection { // TODO: Move this to android.view.textclassifier and make it package-private. // We'll have to update the native library code to do this. static { System.loadLibrary("smart-selection_jni"); } private final long mCtx; /** * Creates a new instance of SmartSelect predictor, using the provided model image, * given as a file descriptor. */ public SmartSelection(int fd) { mCtx = nativeNew(fd); } /** * Given a string context and current selection, computes the SmartSelection suggestion. * * The begin and end are character indices into the context UTF8 string. selectionBegin is the * character index where the selection begins, and selectionEnd is the index of one character * past the selection span. * * The return value is an array of two ints: suggested selection beginning and end, with the * same semantics as the input selectionBeginning and selectionEnd. */ public int[] suggest(String context, int selectionBegin, int selectionEnd) { return nativeSuggest(mCtx, context, selectionBegin, selectionEnd); } /** * Given a string context and current selection, classifies the type of the selected text. * * The begin and end params are character indices in the context string. * * Returns the type of the selection, e.g. "email", "address", "phone". */ public String classifyText(String context, int selectionBegin, int selectionEnd) { return nativeClassifyText(mCtx, context, selectionBegin, selectionEnd); } /** * Frees up the allocated memory. */ public void close() { nativeClose(mCtx); } private static native long nativeNew(int fd); private static native int[] nativeSuggest( long context, String text, int selectionBegin, int selectionEnd); private static native String nativeClassifyText( long context, String text, int selectionBegin, int selectionEnd); private static native void nativeClose(long context); }
core/java/android/view/textclassifier/TextClassificationManager.java +56 −3 Original line number Diff line number Diff line Loading @@ -18,9 +18,18 @@ package android.view.textclassifier; import android.annotation.NonNull; import android.content.Context; import android.os.ParcelFileDescriptor; import android.text.LangId; import android.util.Log; import com.android.internal.util.Preconditions; import java.io.File; import java.io.FileNotFoundException; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; /** * Interface to the text classification service. Loading @@ -30,14 +39,35 @@ import java.util.List; */ public final class TextClassificationManager { private static final String LOG_TAG = "TextClassificationManager"; private final Context mContext; // TODO: Implement a way to close the file descriptor. private ParcelFileDescriptor mFd; private TextClassifier mDefault; private LangId mLangId; /** @hide */ public TextClassificationManager(Context context) {} public TextClassificationManager(Context context) { mContext = Preconditions.checkNotNull(context); } /** * Returns the default text classifier. */ public TextClassifier getDefaultTextClassifier() { return TextClassifier.NO_OP; if (mDefault == null) { try { mFd = ParcelFileDescriptor.open( new File("/etc/assistant/smart-selection.model"), ParcelFileDescriptor.MODE_READ_ONLY); mDefault = new TextClassifierImpl(mContext, mFd); } catch (FileNotFoundException e) { Log.e(LOG_TAG, "Error accessing 'text classifier selection' model file.", e); mDefault = TextClassifier.NO_OP; } } return mDefault; } /** Loading @@ -47,7 +77,30 @@ public final class TextClassificationManager { * @throws IllegalArgumentException if text is null */ public List<TextLanguage> detectLanguages(@NonNull CharSequence text) { // TODO: Implement Preconditions.checkArgument(text != null); try { if (text.length() > 0) { final String language = getLanguageDetector().findLanguage(text.toString()); final Locale locale = new Locale.Builder().setLanguageTag(language).build(); return Collections.unmodifiableList(Arrays.asList( new TextLanguage.Builder(0, text.length()) .setLanguage(locale, 1.0f /* confidence */) .build())); } } catch (Throwable t) { // Avoid throwing from this method. Log the error. Log.e(LOG_TAG, "Error detecting languages for text. Returning empty result.", t); } // Getting here means something went wrong. Return an empty result. return Collections.emptyList(); } private LangId getLanguageDetector() { if (mLangId == null) { // TODO: Use a file descriptor as soon as we start to depend on a model file // for language detection. mLangId = new LangId(0); } return mLangId; } }
core/java/android/view/textclassifier/TextClassifierImpl.java 0 → 100644 +180 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view.textclassifier; import android.annotation.NonNull; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.ParcelFileDescriptor; import android.text.SmartSelection; import android.text.TextUtils; import android.util.Log; import com.android.internal.util.Preconditions; import java.io.FileNotFoundException; /** * Default implementation of the {@link TextClassifier} interface. * * <p>This class uses machine learning to recognize entities in text. * Unless otherwise stated, methods of this class are blocking operations and should most * likely not be called on the UI thread. * * @hide */ final class TextClassifierImpl implements TextClassifier { private static final String LOG_TAG = "TextClassifierImpl"; private final Context mContext; private final ParcelFileDescriptor mFd; private SmartSelection mSmartSelection; TextClassifierImpl(Context context, ParcelFileDescriptor fd) { mContext = Preconditions.checkNotNull(context); mFd = Preconditions.checkNotNull(fd); } @Override public TextSelection suggestSelection( @NonNull CharSequence text, int selectionStartIndex, int selectionEndIndex) { validateInput(text, selectionStartIndex, selectionEndIndex); try { if (text.length() > 0) { final String string = text.toString(); final int[] startEnd = getSmartSelection() .suggest(string, selectionStartIndex, selectionEndIndex); final int start = startEnd[0]; final int end = startEnd[1]; if (start >= 0 && end <= string.length() && start <= end) { final String type = getSmartSelection().classifyText(string, start, end); return new TextSelection.Builder(start, end) .setEntityType(type, 1.0f) .build(); } else { // We can not trust the result. Log the issue and ignore the result. Log.d(LOG_TAG, "Got bad indices for input text. Ignoring result."); } } } catch (Throwable t) { // Avoid throwing from this method. Log the error. Log.e(LOG_TAG, "Error suggesting selection for text. No changes to selection suggested.", t); } // Getting here means something went wrong, return a NO_OP result. return TextClassifier.NO_OP.suggestSelection( text, selectionStartIndex, selectionEndIndex); } @Override public TextClassificationResult getTextClassificationResult( @NonNull CharSequence text, int startIndex, int endIndex) { validateInput(text, startIndex, endIndex); try { if (text.length() > 0) { final CharSequence classified = text.subSequence(startIndex, endIndex); String type = getSmartSelection() .classifyText(text.toString(), startIndex, endIndex); if (!TextUtils.isEmpty(type)) { type = type.toLowerCase().trim(); // TODO: Added this log for debug only. Remove before release. Log.d(LOG_TAG, String.format("Classification type: %s", type)); final Intent intent; final String title; switch (type) { case TextClassifier.TYPE_EMAIL: intent = new Intent(Intent.ACTION_SENDTO); intent.setData(Uri.parse(String.format("mailto:%s", text))); title = mContext.getString(com.android.internal.R.string.email); return createClassificationResult(classified, type, intent, title); case TextClassifier.TYPE_PHONE: intent = new Intent(Intent.ACTION_DIAL); intent.setData(Uri.parse(String.format("tel:%s", text))); title = mContext.getString(com.android.internal.R.string.dial); return createClassificationResult(classified, type, intent, title); case TextClassifier.TYPE_ADDRESS: intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(String.format("geo:0,0?q=%s", text))); title = mContext.getString(com.android.internal.R.string.map); return createClassificationResult(classified, type, intent, title); default: // No classification type found. Return a no-op result. break; // TODO: Add other classification types. } } } } catch (Throwable t) { // Avoid throwing from this method. Log the error. Log.e(LOG_TAG, "Error getting assist info.", t); } // Getting here means something went wrong, return a NO_OP result. return TextClassifier.NO_OP.getTextClassificationResult(text, startIndex, endIndex); } @Override public LinksInfo getLinks(@NonNull CharSequence text, int linkMask) { // TODO: Implement return TextClassifier.NO_OP.getLinks(text, linkMask); } private synchronized SmartSelection getSmartSelection() throws FileNotFoundException { if (mSmartSelection == null) { mSmartSelection = new SmartSelection(mFd.getFd()); } return mSmartSelection; } private TextClassificationResult createClassificationResult( CharSequence text, String type, Intent intent, String label) { TextClassificationResult.Builder builder = new TextClassificationResult.Builder() .setText(text.toString()) .setEntityType(type, 1.0f /* confidence */) .setIntent(intent) .setOnClickListener(TextClassificationResult.createStartActivityOnClick( mContext, intent)) .setLabel(label); PackageManager pm = mContext.getPackageManager(); ResolveInfo resolveInfo = pm.resolveActivity(intent, 0); // TODO: If the resolveInfo is the "chooser", do not set the package name and use a // default icon for this classification type. intent.setPackage(resolveInfo.activityInfo.packageName); Drawable icon = resolveInfo.activityInfo.loadIcon(pm); if (icon == null) { icon = resolveInfo.loadIcon(pm); } builder.setIcon(icon); return builder.build(); } /** * @throws IllegalArgumentException if text is null; startIndex is negative; * endIndex is greater than text.length() or less than startIndex */ private static void validateInput(@NonNull CharSequence text, int startIndex, int endIndex) { Preconditions.checkArgument(text != null); Preconditions.checkArgument(startIndex >= 0); Preconditions.checkArgument(endIndex <= text.length()); Preconditions.checkArgument(endIndex >= startIndex); } }
core/res/res/values/strings.xml +9 −0 Original line number Diff line number Diff line Loading @@ -2589,6 +2589,15 @@ <!-- Title for EditText context menu [CHAR LIMIT=20] --> <string name="editTextMenuTitle">Text actions</string> <!-- Label for item in the text selection menu to trigger an Email app [CHAR LIMIT=20] --> <string name="email">Email</string> <!-- Label for item in the text selection menu to trigger a Dialer app [CHAR LIMIT=20] --> <string name="dial">Dial</string> <!-- Label for item in the text selection menu to trigger a Map app [CHAR LIMIT=20] --> <string name="map">Map</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. --> Loading