diff options
11 files changed, 416 insertions, 328 deletions
diff --git a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java index c1491956c798..bd3b6737f505 100644 --- a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java +++ b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java @@ -52,7 +52,7 @@ public class TextClassificationManagerPerfTest { BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { textClassificationManager.getTextClassifier(); - textClassificationManager.invalidate(); + textClassificationManager.invalidateForTesting(); } } @@ -68,7 +68,7 @@ public class TextClassificationManagerPerfTest { BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { textClassificationManager.getTextClassifier(); - textClassificationManager.invalidate(); + textClassificationManager.invalidateForTesting(); } } } diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 774d4ae789b4..9a11104b30c1 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -263,6 +263,7 @@ public final class DeviceConfig { * Namespace for TextClassifier related features. * * @hide + * @see android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS */ @SystemApi public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier"; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 8231a92a6fbf..75b40fd489f2 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -12039,26 +12039,27 @@ public final class Settings { * entity_list_default use ":" as delimiter for values. Ex: * * <pre> - * smart_linkify_enabled (boolean) - * system_textclassifier_enabled (boolean) - * model_dark_launch_enabled (boolean) - * smart_selection_enabled (boolean) - * smart_text_share_enabled (boolean) - * smart_linkify_enabled (boolean) - * smart_select_animation_enabled (boolean) - * suggest_selection_max_range_length (int) * classify_text_max_range_length (int) - * generate_links_max_text_length (int) - * generate_links_log_sample_rate (int) + * detect_language_from_text_enabled (boolean) * entity_list_default (String[]) - * entity_list_not_editable (String[]) * entity_list_editable (String[]) + * entity_list_not_editable (String[]) + * generate_links_log_sample_rate (int) + * generate_links_max_text_length (int) * in_app_conversation_action_types_default (String[]) - * notification_conversation_action_types_default (String[]) + * lang_id_context_settings (float[]) * lang_id_threshold_override (float) + * local_textclassifier_enabled (boolean) + * model_dark_launch_enabled (boolean) + * notification_conversation_action_types_default (String[]) + * smart_linkify_enabled (boolean) + * smart_select_animation_enabled (boolean) + * smart_selection_enabled (boolean) + * smart_text_share_enabled (boolean) + * suggest_selection_max_range_length (int) + * system_textclassifier_enabled (boolean) * template_intent_factory_enabled (boolean) * translate_in_classification_enabled (boolean) - * detect_language_from_text_enabled (boolean) * </pre> * * <p> diff --git a/core/java/android/view/textclassifier/ConfigParser.java b/core/java/android/view/textclassifier/ConfigParser.java index b475412bf669..63de0590ef72 100644 --- a/core/java/android/view/textclassifier/ConfigParser.java +++ b/core/java/android/view/textclassifier/ConfigParser.java @@ -17,9 +17,19 @@ package android.view.textclassifier; import android.annotation.Nullable; import android.provider.DeviceConfig; +import android.util.ArrayMap; import android.util.KeyValueListParser; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.internal.util.Preconditions; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; /** * Retrieves settings from {@link DeviceConfig} and {@link android.provider.Settings}. @@ -27,80 +37,228 @@ import com.android.internal.annotations.VisibleForTesting; * * @hide */ -@VisibleForTesting +@VisibleForTesting(visibility = Visibility.PACKAGE) public final class ConfigParser { private static final String TAG = "ConfigParser"; - private final KeyValueListParser mParser; + static final boolean ENABLE_DEVICE_CONFIG = true; + + private static final String STRING_LIST_DELIMITER = ":"; - // TODO: Re-enable DeviceConfig when it has reasonable performance or just delete the - // option of using DeviceConfig entirely. - static final boolean ENABLE_DEVICE_CONFIG = false; + private final Supplier<String> mLegacySettingsSupplier; + private final Object mLock = new Object(); + @GuardedBy("mLock") + private final Map<String, Object> mCache = new ArrayMap<>(); + @GuardedBy("mLock") + private @Nullable KeyValueListParser mSettingsParser; // Call getLegacySettings() instead. + + public ConfigParser(Supplier<String> legacySettingsSupplier) { + mLegacySettingsSupplier = Preconditions.checkNotNull(legacySettingsSupplier); + } - public ConfigParser(@Nullable String textClassifierConstants) { - final KeyValueListParser parser = new KeyValueListParser(','); - try { - parser.setString(textClassifierConstants); - } catch (IllegalArgumentException e) { - // Failed to parse the settings string, log this and move on with defaults. - Log.w(TAG, "Bad text_classifier_constants: " + textClassifierConstants); + private KeyValueListParser getLegacySettings() { + synchronized (mLock) { + if (mSettingsParser == null) { + final String legacySettings = mLegacySettingsSupplier.get(); + try { + mSettingsParser = new KeyValueListParser(','); + mSettingsParser.setString(legacySettings); + } catch (IllegalArgumentException e) { + // Failed to parse the settings string, log this and move on with defaults. + Log.w(TAG, "Bad text_classifier_constants: " + legacySettings); + } + } + return mSettingsParser; } - mParser = parser; } /** - * Reads a boolean flag. + * Reads a boolean setting through the cache. */ public boolean getBoolean(String key, boolean defaultValue) { - if (ENABLE_DEVICE_CONFIG) { - return DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_TEXTCLASSIFIER, - key, - mParser.getBoolean(key, defaultValue)); - } else { - return mParser.getBoolean(key, defaultValue); + synchronized (mLock) { + final Object cached = mCache.get(key); + if (cached instanceof Boolean) { + return (boolean) cached; + } + final boolean value; + if (ENABLE_DEVICE_CONFIG) { + value = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_TEXTCLASSIFIER, + key, + getLegacySettings().getBoolean(key, defaultValue)); + } else { + value = getLegacySettings().getBoolean(key, defaultValue); + } + mCache.put(key, value); + return value; } } /** - * Reads an integer flag. + * Reads an integer setting through the cache. */ public int getInt(String key, int defaultValue) { - if (ENABLE_DEVICE_CONFIG) { - return DeviceConfig.getInt( - DeviceConfig.NAMESPACE_TEXTCLASSIFIER, - key, - mParser.getInt(key, defaultValue)); - } else { - return mParser.getInt(key, defaultValue); + synchronized (mLock) { + final Object cached = mCache.get(key); + if (cached instanceof Integer) { + return (int) cached; + } + final int value; + if (ENABLE_DEVICE_CONFIG) { + value = DeviceConfig.getInt( + DeviceConfig.NAMESPACE_TEXTCLASSIFIER, + key, + getLegacySettings().getInt(key, defaultValue)); + } else { + value = getLegacySettings().getInt(key, defaultValue); + } + mCache.put(key, value); + return value; } } /** - * Reads a float flag. + * Reads a float setting through the cache. */ public float getFloat(String key, float defaultValue) { - if (ENABLE_DEVICE_CONFIG) { - return DeviceConfig.getFloat( - DeviceConfig.NAMESPACE_TEXTCLASSIFIER, - key, - mParser.getFloat(key, defaultValue)); - } else { - return mParser.getFloat(key, defaultValue); + synchronized (mLock) { + final Object cached = mCache.get(key); + if (cached instanceof Float) { + return (float) cached; + } + final float value; + if (ENABLE_DEVICE_CONFIG) { + value = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_TEXTCLASSIFIER, + key, + getLegacySettings().getFloat(key, defaultValue)); + } else { + value = getLegacySettings().getFloat(key, defaultValue); + } + mCache.put(key, value); + return value; } } /** - * Reads a string flag. + * Reads a string setting through the cache. */ public String getString(String key, String defaultValue) { - if (ENABLE_DEVICE_CONFIG) { - return DeviceConfig.getString( - DeviceConfig.NAMESPACE_TEXTCLASSIFIER, - key, - mParser.getString(key, defaultValue)); + synchronized (mLock) { + final Object cached = mCache.get(key); + if (cached instanceof String) { + return (String) cached; + } + final String value; + if (ENABLE_DEVICE_CONFIG) { + value = DeviceConfig.getString( + DeviceConfig.NAMESPACE_TEXTCLASSIFIER, + key, + getLegacySettings().getString(key, defaultValue)); + } else { + value = getLegacySettings().getString(key, defaultValue); + } + mCache.put(key, value); + return value; + } + } + + /** + * Reads a string list setting through the cache. + */ + public List<String> getStringList(String key, List<String> defaultValue) { + synchronized (mLock) { + final Object cached = mCache.get(key); + if (cached instanceof List) { + final List asList = (List) cached; + if (asList.isEmpty()) { + return Collections.emptyList(); + } else if (asList.get(0) instanceof String) { + return (List<String>) cached; + } + } + final List<String> value; + if (ENABLE_DEVICE_CONFIG) { + value = getDeviceConfigStringList( + key, + getSettingsStringList(key, defaultValue)); + } else { + value = getSettingsStringList(key, defaultValue); + } + mCache.put(key, value); + return value; + } + } + + /** + * Reads a float array through the cache. The returned array should be expected to be of the + * same length as that of the defaultValue. + */ + public float[] getFloatArray(String key, float[] defaultValue) { + synchronized (mLock) { + final Object cached = mCache.get(key); + if (cached instanceof float[]) { + return (float[]) cached; + } + final float[] value; + if (ENABLE_DEVICE_CONFIG) { + value = getDeviceConfigFloatArray( + key, + getSettingsFloatArray(key, defaultValue)); + } else { + value = getSettingsFloatArray(key, defaultValue); + } + mCache.put(key, value); + return value; + } + } + + private List<String> getSettingsStringList(String key, List<String> defaultValue) { + return parse(mSettingsParser.getString(key, null), defaultValue); + } + + private static List<String> getDeviceConfigStringList(String key, List<String> defaultValue) { + return parse( + DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null), + defaultValue); + } + + private static float[] getDeviceConfigFloatArray(String key, float[] defaultValue) { + return parse( + DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null), + defaultValue); + } + + private float[] getSettingsFloatArray(String key, float[] defaultValue) { + return parse(mSettingsParser.getString(key, null), defaultValue); + } + + private static List<String> parse(@Nullable String listStr, List<String> defaultValue) { + if (listStr != null) { + return Collections.unmodifiableList( + Arrays.asList(listStr.split(STRING_LIST_DELIMITER))); + } + return defaultValue; + } + + private static float[] parse(@Nullable String arrayStr, float[] defaultValue) { + if (arrayStr != null) { + final String[] split = arrayStr.split(STRING_LIST_DELIMITER); + if (split.length != defaultValue.length) { + return defaultValue; + } + final float[] result = new float[split.length]; + for (int i = 0; i < split.length; i++) { + try { + result[i] = Float.parseFloat(split[i]); + } catch (NumberFormatException e) { + return defaultValue; + } + } + return result; } else { - return mParser.getString(key, defaultValue); + return defaultValue; } } } diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java index 876e5cccb2e0..2964f515f005 100644 --- a/core/java/android/view/textclassifier/TextClassificationConstants.java +++ b/core/java/android/view/textclassifier/TextClassificationConstants.java @@ -16,58 +16,39 @@ package android.view.textclassifier; -import android.annotation.Nullable; - import com.android.internal.util.IndentingPrintWriter; import java.util.Arrays; -import java.util.Collections; import java.util.List; -import java.util.StringJoiner; +import java.util.function.Supplier; /** * TextClassifier specific settings. - * This is encoded as a key=value list, separated by commas. Ex: - * - * <pre> - * smart_linkify_enabled (boolean) - * system_textclassifier_enabled (boolean) - * model_dark_launch_enabled (boolean) - * smart_selection_enabled (boolean) - * smart_text_share_enabled (boolean) - * smart_linkify_enabled (boolean) - * smart_select_animation_enabled (boolean) - * suggest_selection_max_range_length (int) - * classify_text_max_range_length (int) - * generate_links_max_text_length (int) - * generate_links_log_sample_rate (int) - * entity_list_default (String[]) - * entity_list_not_editable (String[]) - * entity_list_editable (String[]) - * in_app_conversation_action_types_default (String[]) - * notification_conversation_action_types_default (String[]) - * lang_id_threshold_override (float) - * template_intent_factory_enabled (boolean) - * translate_in_classification_enabled (boolean) - * detect_languages_from_text_enabled (boolean) - * lang_id_context_settings (float[]) - * </pre> - * + * This is encoded as a key=value list, separated by commas. * <p> - * Type: string - * see also android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS - * * Example of setting the values for testing. + * <p> + * <pre> * adb shell settings put global text_classifier_constants \ * model_dark_launch_enabled=true,smart_selection_enabled=true, \ * entity_list_default=phone:address, \ * lang_id_context_settings=20:1.0:0.4 + * </pre> + * <p> + * Settings are also available in device config. These take precedence over those in settings + * global. + * <p> + * <pre> + * adb shell cmd device_config put textclassifier system_textclassifier_enabled true + * </pre> + * + * @see android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS + * @see android.provider.DeviceConfig.NAMESPACE_TEXTCLASSIFIER * @hide */ +// TODO: Rename to TextClassifierSettings. public final class TextClassificationConstants { - private static final String LOG_TAG = TextClassifier.DEFAULT_LOG_TAG; - /** * Whether the smart linkify feature is enabled. */ @@ -188,29 +169,26 @@ public final class TextClassificationConstants { private static final int CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000; private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000; private static final int GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT = 100; - private static final String STRING_LIST_DELIMITER = ":"; - private static final String ENTITY_LIST_DEFAULT_VALUE = new StringJoiner(STRING_LIST_DELIMITER) - .add(TextClassifier.TYPE_ADDRESS) - .add(TextClassifier.TYPE_EMAIL) - .add(TextClassifier.TYPE_PHONE) - .add(TextClassifier.TYPE_URL) - .add(TextClassifier.TYPE_DATE) - .add(TextClassifier.TYPE_DATE_TIME) - .add(TextClassifier.TYPE_FLIGHT_NUMBER).toString(); - private static final String CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES = - new StringJoiner(STRING_LIST_DELIMITER) - .add(ConversationAction.TYPE_TEXT_REPLY) - .add(ConversationAction.TYPE_CREATE_REMINDER) - .add(ConversationAction.TYPE_CALL_PHONE) - .add(ConversationAction.TYPE_OPEN_URL) - .add(ConversationAction.TYPE_SEND_EMAIL) - .add(ConversationAction.TYPE_SEND_SMS) - .add(ConversationAction.TYPE_TRACK_FLIGHT) - .add(ConversationAction.TYPE_VIEW_CALENDAR) - .add(ConversationAction.TYPE_VIEW_MAP) - .add(ConversationAction.TYPE_ADD_CONTACT) - .add(ConversationAction.TYPE_COPY) - .toString(); + private static final List<String> ENTITY_LIST_DEFAULT_VALUE = Arrays.asList( + TextClassifier.TYPE_ADDRESS, + TextClassifier.TYPE_EMAIL, + TextClassifier.TYPE_PHONE, + TextClassifier.TYPE_URL, + TextClassifier.TYPE_DATE, + TextClassifier.TYPE_DATE_TIME, + TextClassifier.TYPE_FLIGHT_NUMBER); + private static final List<String> CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES = Arrays.asList( + ConversationAction.TYPE_TEXT_REPLY, + ConversationAction.TYPE_CREATE_REMINDER, + ConversationAction.TYPE_CALL_PHONE, + ConversationAction.TYPE_OPEN_URL, + ConversationAction.TYPE_SEND_EMAIL, + ConversationAction.TYPE_SEND_SMS, + ConversationAction.TYPE_TRACK_FLIGHT, + ConversationAction.TYPE_VIEW_CALENDAR, + ConversationAction.TYPE_VIEW_MAP, + ConversationAction.TYPE_ADD_CONTACT, + ConversationAction.TYPE_COPY); /** * < 0 : Not set. Use value from LangId model. * 0 - 1: Override value in LangId model. @@ -221,259 +199,185 @@ public final class TextClassificationConstants { private static final boolean TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT = true; private static final boolean TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT = true; private static final boolean DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT = true; - private static final String LANG_ID_CONTEXT_SETTINGS_DEFAULT = - new StringJoiner(STRING_LIST_DELIMITER).add("20").add("1.0").add("0.4").toString(); - - private final boolean mSystemTextClassifierEnabled; - private final boolean mLocalTextClassifierEnabled; - private final boolean mModelDarkLaunchEnabled; - private final boolean mSmartSelectionEnabled; - private final boolean mSmartTextShareEnabled; - private final boolean mSmartLinkifyEnabled; - private final boolean mSmartSelectionAnimationEnabled; - private final int mSuggestSelectionMaxRangeLength; - private final int mClassifyTextMaxRangeLength; - private final int mGenerateLinksMaxTextLength; - private final int mGenerateLinksLogSampleRate; - private final List<String> mEntityListDefault; - private final List<String> mEntityListNotEditable; - private final List<String> mEntityListEditable; - private final List<String> mInAppConversationActionTypesDefault; - private final List<String> mNotificationConversationActionTypesDefault; - private final float mLangIdThresholdOverride; - private final boolean mTemplateIntentFactoryEnabled; - private final boolean mTranslateInClassificationEnabled; - private final boolean mDetectLanguagesFromTextEnabled; - private final float[] mLangIdContextSettings; + private static final float[] LANG_ID_CONTEXT_SETTINGS_DEFAULT = new float[] {20f, 1.0f, 0.4f}; - private TextClassificationConstants(@Nullable String settings) { - ConfigParser configParser = new ConfigParser(settings); - mSystemTextClassifierEnabled = - configParser.getBoolean( - SYSTEM_TEXT_CLASSIFIER_ENABLED, - SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT); - mLocalTextClassifierEnabled = - configParser.getBoolean( - LOCAL_TEXT_CLASSIFIER_ENABLED, - LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT); - mModelDarkLaunchEnabled = - configParser.getBoolean( - MODEL_DARK_LAUNCH_ENABLED, - MODEL_DARK_LAUNCH_ENABLED_DEFAULT); - mSmartSelectionEnabled = - configParser.getBoolean( - SMART_SELECTION_ENABLED, - SMART_SELECTION_ENABLED_DEFAULT); - mSmartTextShareEnabled = - configParser.getBoolean( - SMART_TEXT_SHARE_ENABLED, - SMART_TEXT_SHARE_ENABLED_DEFAULT); - mSmartLinkifyEnabled = - configParser.getBoolean( - SMART_LINKIFY_ENABLED, - SMART_LINKIFY_ENABLED_DEFAULT); - mSmartSelectionAnimationEnabled = - configParser.getBoolean( - SMART_SELECT_ANIMATION_ENABLED, - SMART_SELECT_ANIMATION_ENABLED_DEFAULT); - mSuggestSelectionMaxRangeLength = - configParser.getInt( - SUGGEST_SELECTION_MAX_RANGE_LENGTH, - SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT); - mClassifyTextMaxRangeLength = - configParser.getInt( - CLASSIFY_TEXT_MAX_RANGE_LENGTH, - CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT); - mGenerateLinksMaxTextLength = - configParser.getInt( - GENERATE_LINKS_MAX_TEXT_LENGTH, - GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT); - mGenerateLinksLogSampleRate = - configParser.getInt( - GENERATE_LINKS_LOG_SAMPLE_RATE, - GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT); - mEntityListDefault = parseStringList( - configParser.getString( - ENTITY_LIST_DEFAULT, - ENTITY_LIST_DEFAULT_VALUE)); - mEntityListNotEditable = parseStringList( - configParser.getString( - ENTITY_LIST_NOT_EDITABLE, - ENTITY_LIST_DEFAULT_VALUE)); - mEntityListEditable = parseStringList( - configParser.getString( - ENTITY_LIST_EDITABLE, - ENTITY_LIST_DEFAULT_VALUE)); - mInAppConversationActionTypesDefault = parseStringList( - configParser.getString( - IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT, - CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES)); - mNotificationConversationActionTypesDefault = parseStringList( - configParser.getString( - NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT, - CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES)); - mLangIdThresholdOverride = - configParser.getFloat( - LANG_ID_THRESHOLD_OVERRIDE, - LANG_ID_THRESHOLD_OVERRIDE_DEFAULT); - mTemplateIntentFactoryEnabled = - configParser.getBoolean( - TEMPLATE_INTENT_FACTORY_ENABLED, - TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT); - mTranslateInClassificationEnabled = - configParser.getBoolean( - TRANSLATE_IN_CLASSIFICATION_ENABLED, - TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT); - mDetectLanguagesFromTextEnabled = - configParser.getBoolean( - DETECT_LANGUAGES_FROM_TEXT_ENABLED, - DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT); - mLangIdContextSettings = parseFloatArray( - configParser, - LANG_ID_CONTEXT_SETTINGS, - LANG_ID_CONTEXT_SETTINGS_DEFAULT); - } + private final ConfigParser mConfigParser; - /** Load from a settings string. */ - public static TextClassificationConstants loadFromString(String settings) { - return new TextClassificationConstants(settings); + public TextClassificationConstants(Supplier<String> legacySettingsSupplier) { + mConfigParser = new ConfigParser(legacySettingsSupplier); } public boolean isLocalTextClassifierEnabled() { - return mLocalTextClassifierEnabled; + return mConfigParser.getBoolean( + LOCAL_TEXT_CLASSIFIER_ENABLED, + LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT); } public boolean isSystemTextClassifierEnabled() { - return mSystemTextClassifierEnabled; + return mConfigParser.getBoolean( + SYSTEM_TEXT_CLASSIFIER_ENABLED, + SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT); } public boolean isModelDarkLaunchEnabled() { - return mModelDarkLaunchEnabled; + return mConfigParser.getBoolean( + MODEL_DARK_LAUNCH_ENABLED, + MODEL_DARK_LAUNCH_ENABLED_DEFAULT); } public boolean isSmartSelectionEnabled() { - return mSmartSelectionEnabled; + return mConfigParser.getBoolean( + SMART_SELECTION_ENABLED, + SMART_SELECTION_ENABLED_DEFAULT); } public boolean isSmartTextShareEnabled() { - return mSmartTextShareEnabled; + return mConfigParser.getBoolean( + SMART_TEXT_SHARE_ENABLED, + SMART_TEXT_SHARE_ENABLED_DEFAULT); } public boolean isSmartLinkifyEnabled() { - return mSmartLinkifyEnabled; + return mConfigParser.getBoolean( + SMART_LINKIFY_ENABLED, + SMART_LINKIFY_ENABLED_DEFAULT); } public boolean isSmartSelectionAnimationEnabled() { - return mSmartSelectionAnimationEnabled; + return mConfigParser.getBoolean( + SMART_SELECT_ANIMATION_ENABLED, + SMART_SELECT_ANIMATION_ENABLED_DEFAULT); } public int getSuggestSelectionMaxRangeLength() { - return mSuggestSelectionMaxRangeLength; + return mConfigParser.getInt( + SUGGEST_SELECTION_MAX_RANGE_LENGTH, + SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT); } public int getClassifyTextMaxRangeLength() { - return mClassifyTextMaxRangeLength; + return mConfigParser.getInt( + CLASSIFY_TEXT_MAX_RANGE_LENGTH, + CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT); } public int getGenerateLinksMaxTextLength() { - return mGenerateLinksMaxTextLength; + return mConfigParser.getInt( + GENERATE_LINKS_MAX_TEXT_LENGTH, + GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT); } public int getGenerateLinksLogSampleRate() { - return mGenerateLinksLogSampleRate; + return mConfigParser.getInt( + GENERATE_LINKS_LOG_SAMPLE_RATE, + GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT); } public List<String> getEntityListDefault() { - return mEntityListDefault; + return mConfigParser.getStringList( + ENTITY_LIST_DEFAULT, + ENTITY_LIST_DEFAULT_VALUE); } public List<String> getEntityListNotEditable() { - return mEntityListNotEditable; + return mConfigParser.getStringList( + ENTITY_LIST_NOT_EDITABLE, + ENTITY_LIST_DEFAULT_VALUE); } public List<String> getEntityListEditable() { - return mEntityListEditable; + return mConfigParser.getStringList( + ENTITY_LIST_EDITABLE, + ENTITY_LIST_DEFAULT_VALUE); } public List<String> getInAppConversationActionTypes() { - return mInAppConversationActionTypesDefault; + return mConfigParser.getStringList( + IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT, + CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES); } public List<String> getNotificationConversationActionTypes() { - return mNotificationConversationActionTypesDefault; + return mConfigParser.getStringList( + NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT, + CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES); } public float getLangIdThresholdOverride() { - return mLangIdThresholdOverride; + return mConfigParser.getFloat( + LANG_ID_THRESHOLD_OVERRIDE, + LANG_ID_THRESHOLD_OVERRIDE_DEFAULT); } public boolean isTemplateIntentFactoryEnabled() { - return mTemplateIntentFactoryEnabled; + return mConfigParser.getBoolean( + TEMPLATE_INTENT_FACTORY_ENABLED, + TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT); } public boolean isTranslateInClassificationEnabled() { - return mTranslateInClassificationEnabled; + return mConfigParser.getBoolean( + TRANSLATE_IN_CLASSIFICATION_ENABLED, + TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT); } public boolean isDetectLanguagesFromTextEnabled() { - return mDetectLanguagesFromTextEnabled; + return mConfigParser.getBoolean( + DETECT_LANGUAGES_FROM_TEXT_ENABLED, + DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT); } public float[] getLangIdContextSettings() { - return mLangIdContextSettings; - } - - private static List<String> parseStringList(String listStr) { - return Collections.unmodifiableList(Arrays.asList(listStr.split(STRING_LIST_DELIMITER))); - } - - private static float[] parseFloatArray( - ConfigParser configParser, String key, String defaultStr) { - final String str = configParser.getString(key, defaultStr); - final String[] defaultSplit = defaultStr.split(STRING_LIST_DELIMITER); - String[] split = str.split(STRING_LIST_DELIMITER); - if (split.length != defaultSplit.length) { - Log.v(LOG_TAG, "Error parsing " + key + " flag. Using defaults."); - split = defaultSplit; - } - final float[] result = new float[split.length]; - for (int i = 0; i < split.length; i++) { - try { - result[i] = Float.parseFloat(split[i]); - } catch (NumberFormatException e) { - Log.v(LOG_TAG, "Error parsing part of " + key + " flag. Using defaults."); - result[i] = Float.parseFloat(defaultSplit[i]); - } - } - return result; + return mConfigParser.getFloatArray( + LANG_ID_CONTEXT_SETTINGS, + LANG_ID_CONTEXT_SETTINGS_DEFAULT); } void dump(IndentingPrintWriter pw) { pw.println("TextClassificationConstants:"); pw.increaseIndent(); - pw.printPair("isLocalTextClassifierEnabled", mLocalTextClassifierEnabled); - pw.printPair("isSystemTextClassifierEnabled", mSystemTextClassifierEnabled); - pw.printPair("isModelDarkLaunchEnabled", mModelDarkLaunchEnabled); - pw.printPair("isSmartSelectionEnabled", mSmartSelectionEnabled); - pw.printPair("isSmartTextShareEnabled", mSmartTextShareEnabled); - pw.printPair("isSmartLinkifyEnabled", mSmartLinkifyEnabled); - pw.printPair("isSmartSelectionAnimationEnabled", mSmartSelectionAnimationEnabled); - pw.printPair("getSuggestSelectionMaxRangeLength", mSuggestSelectionMaxRangeLength); - pw.printPair("getClassifyTextMaxRangeLength", mClassifyTextMaxRangeLength); - pw.printPair("getGenerateLinksMaxTextLength", mGenerateLinksMaxTextLength); - pw.printPair("getGenerateLinksLogSampleRate", mGenerateLinksLogSampleRate); - pw.printPair("getEntityListDefault", mEntityListDefault); - pw.printPair("getEntityListNotEditable", mEntityListNotEditable); - pw.printPair("getEntityListEditable", mEntityListEditable); - pw.printPair("getInAppConversationActionTypes", mInAppConversationActionTypesDefault); - pw.printPair("getNotificationConversationActionTypes", - mNotificationConversationActionTypesDefault); - pw.printPair("getLangIdThresholdOverride", mLangIdThresholdOverride); - pw.printPair("isTemplateIntentFactoryEnabled", mTemplateIntentFactoryEnabled); - pw.printPair("isTranslateInClassificationEnabled", mTranslateInClassificationEnabled); - pw.printPair("isDetectLanguageFromTextEnabled", mDetectLanguagesFromTextEnabled); - pw.printPair("getLangIdContextSettings", Arrays.toString(mLangIdContextSettings)); + pw.printPair("classify_text_max_range_length", getClassifyTextMaxRangeLength()) + .println(); + pw.printPair("detect_language_from_text_enabled", isDetectLanguagesFromTextEnabled()) + .println(); + pw.printPair("entity_list_default", getEntityListDefault()) + .println(); + pw.printPair("entity_list_editable", getEntityListEditable()) + .println(); + pw.printPair("entity_list_not_editable", getEntityListNotEditable()) + .println(); + pw.printPair("generate_links_log_sample_rate", getGenerateLinksLogSampleRate()) + .println(); + pw.printPair("generate_links_max_text_length", getGenerateLinksMaxTextLength()) + .println(); + pw.printPair("in_app_conversation_action_types_default", getInAppConversationActionTypes()) + .println(); + pw.printPair("lang_id_context_settings", Arrays.toString(getLangIdContextSettings())) + .println(); + pw.printPair("lang_id_threshold_override", getLangIdThresholdOverride()) + .println(); + pw.printPair("local_textclassifier_enabled", isLocalTextClassifierEnabled()) + .println(); + pw.printPair("model_dark_launch_enabled", isModelDarkLaunchEnabled()) + .println(); + pw.printPair("notification_conversation_action_types_default", + getNotificationConversationActionTypes()).println(); + pw.printPair("smart_linkify_enabled", isSmartLinkifyEnabled()) + .println(); + pw.printPair("smart_select_animation_enabled", isSmartSelectionAnimationEnabled()) + .println(); + pw.printPair("smart_selection_enabled", isSmartSelectionEnabled()) + .println(); + pw.printPair("smart_text_share_enabled", isSmartTextShareEnabled()) + .println(); + pw.printPair("suggest_selection_max_range_length", getSuggestSelectionMaxRangeLength()) + .println(); + pw.printPair("system_textclassifier_enabled", isSystemTextClassifierEnabled()) + .println(); + pw.printPair("template_intent_factory_enabled", isTemplateIntentFactoryEnabled()) + .println(); + pw.printPair("translate_in_classification_enabled", isTranslateInClassificationEnabled()) + .println(); pw.decreaseIndent(); - pw.println(); } -} +}
\ No newline at end of file diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java index 042b943c60a3..95ca9deb2871 100644 --- a/core/java/android/view/textclassifier/TextClassificationManager.java +++ b/core/java/android/view/textclassifier/TextClassificationManager.java @@ -45,6 +45,9 @@ public final class TextClassificationManager { private static final String LOG_TAG = "TextClassificationManager"; + private static final TextClassificationConstants sDefaultSettings = + new TextClassificationConstants(() -> null); + private final Object mLock = new Object(); private final TextClassificationSessionFactory mDefaultSessionFactory = classificationContext -> new TextClassificationSession( @@ -129,9 +132,10 @@ public final class TextClassificationManager { private TextClassificationConstants getSettings() { synchronized (mLock) { if (mSettings == null) { - mSettings = TextClassificationConstants.loadFromString(Settings.Global.getString( - getApplicationContext().getContentResolver(), - Settings.Global.TEXT_CLASSIFIER_CONSTANTS)); + mSettings = new TextClassificationConstants( + () -> Settings.Global.getString( + getApplicationContext().getContentResolver(), + Settings.Global.TEXT_CLASSIFIER_CONSTANTS)); } return mSettings; } @@ -251,7 +255,11 @@ public final class TextClassificationManager { /** @hide */ @VisibleForTesting - public void invalidate() { + public void invalidateForTesting() { + invalidate(); + } + + private void invalidate() { synchronized (mLock) { mSettings = null; mLocalTextClassifier = null; @@ -280,9 +288,8 @@ public final class TextClassificationManager { if (tcm != null) { return tcm.getSettings(); } else { - return TextClassificationConstants.loadFromString(Settings.Global.getString( - context.getApplicationContext().getContentResolver(), - Settings.Global.TEXT_CLASSIFIER_CONSTANTS)); + // Use default settings if there is no tcm. + return sDefaultSettings; } } diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java index 3297523b0da9..3e95f1baf4bb 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -301,7 +301,7 @@ public final class TextClassifierImpl implements TextClassifier { final ZonedDateTime refTime = ZonedDateTime.now(); final Collection<String> entitiesToIdentify = request.getEntityConfig() != null ? request.getEntityConfig().resolveEntityListModifications( - getEntitiesForHints(request.getEntityConfig().getHints())) + getEntitiesForHints(request.getEntityConfig().getHints())) : mSettings.getEntityListDefault(); final String localesString = concatenateLocales(request.getDefaultLocales()); final String detectLanguageTags = detectLanguageTagsFromText(request.getText()); @@ -779,8 +779,8 @@ public final class TextClassifierImpl implements TextClassifier { final float moreTextScoreRatio = 1f - subjectTextScoreRatio; Log.v(LOG_TAG, String.format(Locale.US, "LangIdContextSettings: " - + "minimumTextSize=%d, penalizeRatio=%.2f, " - + "subjectTextScoreRatio=%.2f, moreTextScoreRatio=%.2f", + + "minimumTextSize=%d, penalizeRatio=%.2f, " + + "subjectTextScoreRatio=%.2f, moreTextScoreRatio=%.2f", minimumTextSize, penalizeRatio, subjectTextScoreRatio, moreTextScoreRatio)); if (end - start < minimumTextSize && penalizeRatio <= 0) { @@ -903,4 +903,3 @@ public final class TextClassifierImpl implements TextClassifier { } } } - diff --git a/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java b/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java index f1cfe24762a8..d54ce51c3c37 100644 --- a/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java @@ -26,16 +26,17 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import java.io.IOException; +import java.util.function.Supplier; @SmallTest @RunWith(AndroidJUnit4.class) public class ConfigParserTest { - private static final String SETTINGS = "int=42,float=12.3,boolean=true,string=abc"; + private static final Supplier<String> SETTINGS = + () -> "int=42,float=12.3,boolean=true,string=abc"; private static final String CLEAR_DEVICE_CONFIG_KEY_CMD = "device_config delete " + DeviceConfig.NAMESPACE_TEXTCLASSIFIER; private static final String[] DEVICE_CONFIG_KEYS = new String[]{ @@ -59,7 +60,6 @@ public class ConfigParserTest { } @Test - @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized public void getBoolean_deviceConfig() { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_TEXTCLASSIFIER, @@ -79,7 +79,6 @@ public class ConfigParserTest { } @Test - @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized public void getInt_deviceConfig() { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_TEXTCLASSIFIER, @@ -97,7 +96,6 @@ public class ConfigParserTest { } @Test - @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized public void getFloat_deviceConfig() { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_TEXTCLASSIFIER, @@ -115,7 +113,6 @@ public class ConfigParserTest { } @Test - @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized public void getString_deviceConfig() { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_TEXTCLASSIFIER, diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java index f6bb1bff3635..789b829456f3 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java @@ -50,9 +50,11 @@ public class TextClassificationConstantsTest { + "in_app_conversation_action_types_default=text_reply," + "notification_conversation_action_types_default=send_email:call_phone," + "lang_id_threshold_override=0.3," - + "lang_id_context_settings=10:1:0.5"; - final TextClassificationConstants constants = - TextClassificationConstants.loadFromString(s); + + "lang_id_context_settings=10:1:0.5," + + "detect_language_from_text_enabled=true," + + "template_intent_factory_enabled=true," + + "translate_in_classification_enabled=true"; + final TextClassificationConstants constants = new TextClassificationConstants(() -> s); assertWithMessage("local_textclassifier_enabled") .that(constants.isLocalTextClassifierEnabled()).isTrue(); @@ -95,6 +97,12 @@ public class TextClassificationConstantsTest { .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(0.3f); Assert.assertArrayEquals("lang_id_context_settings", constants.getLangIdContextSettings(), new float[]{10, 1, 0.5f}, EPSILON); + assertWithMessage("detect_language_from_text_enabled") + .that(constants.isLocalTextClassifierEnabled()).isTrue(); + assertWithMessage("template_intent_factory_enabled") + .that(constants.isLocalTextClassifierEnabled()).isTrue(); + assertWithMessage("translate_in_classification_enabled") + .that(constants.isLocalTextClassifierEnabled()).isTrue(); } @Test @@ -116,9 +124,11 @@ public class TextClassificationConstantsTest { + "in_app_conversation_action_types_default=view_map:track_flight," + "notification_conversation_action_types_default=share_location," + "lang_id_threshold_override=2," - + "lang_id_context_settings=30:0.5:0.3"; - final TextClassificationConstants constants = - TextClassificationConstants.loadFromString(s); + + "lang_id_context_settings=30:0.5:0.3," + + "detect_language_from_text_enabled=false," + + "template_intent_factory_enabled=false," + + "translate_in_classification_enabled=false"; + final TextClassificationConstants constants = new TextClassificationConstants(() -> s); assertWithMessage("local_textclassifier_enabled") .that(constants.isLocalTextClassifierEnabled()).isFalse(); @@ -161,12 +171,17 @@ public class TextClassificationConstantsTest { .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(2f); Assert.assertArrayEquals("lang_id_context_settings", constants.getLangIdContextSettings(), new float[]{30, 0.5f, 0.3f}, EPSILON); + assertWithMessage("detect_language_from_text_enabled") + .that(constants.isLocalTextClassifierEnabled()).isFalse(); + assertWithMessage("template_intent_factory_enabled") + .that(constants.isLocalTextClassifierEnabled()).isFalse(); + assertWithMessage("translate_in_classification_enabled") + .that(constants.isLocalTextClassifierEnabled()).isFalse(); } @Test public void testLoadFromString_defaultValues() { - final TextClassificationConstants constants = - TextClassificationConstants.loadFromString(""); + final TextClassificationConstants constants = new TextClassificationConstants(() -> ""); assertWithMessage("local_textclassifier_enabled") .that(constants.isLocalTextClassifierEnabled()).isTrue(); @@ -213,5 +228,11 @@ public class TextClassificationConstantsTest { .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(-1f); Assert.assertArrayEquals("lang_id_context_settings", constants.getLangIdContextSettings(), new float[]{20, 1, 0.4f}, EPSILON); + assertWithMessage("detect_language_from_text_enabled") + .that(constants.isLocalTextClassifierEnabled()).isTrue(); + assertWithMessage("template_intent_factory_enabled") + .that(constants.isLocalTextClassifierEnabled()).isTrue(); + assertWithMessage("translate_in_classification_enabled") + .that(constants.isLocalTextClassifierEnabled()).isTrue(); } } diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java index 4fcd51c9be1d..9148185d904b 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java @@ -77,7 +77,7 @@ public class TextClassificationManagerTest { TextClassifier fallback = TextClassifier.NO_OP; TextClassifier classifier = new TextClassifierImpl( - fakeContext, TextClassificationConstants.loadFromString(null), fallback); + fakeContext, new TextClassificationConstants(() -> null), fallback); String text = "Contact me at +12122537077"; String classifiedText = "+12122537077"; diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java index aeb8949c6976..e3eb2a313cf6 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java @@ -59,7 +59,7 @@ public class TextClassifierTest { // TODO: Implement TextClassifierService testing. private static final TextClassificationConstants TC_CONSTANTS = - TextClassificationConstants.loadFromString(""); + new TextClassificationConstants(() -> ""); private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US"); private static final String NO_TYPE = null; |