Browse Source

Lazy-load textclassifier settings.

Re-enables reading settings from device_config.
See: I6b7ab56e4015448ee068deb49e7f6fa133fea53c
Updates tests.
Updates documentation.

Bug: 129934185
Test: atest android.view.textclassifier
Test: (Performance) Test: frameworks/base/apct-tests/perftests/textclassifier/run.sh
      Compare performance results with ConfigParser.ENABLE_DEVICE_CONFIG
      set to true vs false. Trivial regression recorded i.e. 1.03x.
Test: (Manual) Change flags and see them reflected. e.g.
      adb shell cmd device_config put textclassifier system_textclassifier_enabled false
      Verify that app no longer uses the OEM TCS but the AOSP TC.
Change-Id: I4c6ff781c97fc2e3d3da55dc49123fa1d759670a
ten
Abodunrinwa Toki 2 years ago
parent
commit
0634af3875

+ 2
- 2
apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java View File

@@ -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();
}
}
}

+ 1
- 0
core/java/android/provider/DeviceConfig.java View File

@@ -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";

+ 14
- 13
core/java/android/provider/Settings.java View File

@@ -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>

+ 202
- 44
core/java/android/view/textclassifier/ConfigParser.java View File

@@ -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;
}
}
}

+ 146
- 242
core/java/android/view/textclassifier/TextClassificationConstants.java View File

@@ -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();
}
}
}

+ 14
- 7
core/java/android/view/textclassifier/TextClassificationManager.java View File

@@ -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;
}
}


+ 3
- 4
core/java/android/view/textclassifier/TextClassifierImpl.java View File

@@ -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 {
}
}
}


+ 3
- 6
core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java View File

@@ -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,

+ 29
- 8
core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java View File

@@ -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();
}
}

+ 1
- 1
core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java View File

@@ -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";

+ 1
- 1
core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java View File

@@ -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;


Loading…
Cancel
Save