From db8fc314d2ac9a2ce3209fe9e842c985e6f57d06 Mon Sep 17 00:00:00 2001 From: Abodunrinwa Toki Date: Mon, 26 Feb 2018 21:37:51 +0000 Subject: [PATCH] Associate TCconstants with the TCM instead of TCImpl Also updates flags list. Bug: 72946306 Bug: 72946123 Test: bit FrameworksCoreTests:android.view.textclassifier.TextClassificationManagerTest Test: bit FrameworksCoreTests:android.view.textclassifier.TextClassificationConstantsTest Test: bit CtsWidgetTestCases:android.widget.cts.TextViewTest Test: bit FrameworksCoreTests:android.widget.TextViewActivityTest Change-Id: I8af9d3d1da01836fbadcbbf6ce7c1c0db7456a05 --- core/java/android/provider/Settings.java | 9 +- .../textclassifier/SystemTextClassifier.java | 14 ++- ....java => TextClassificationConstants.java} | 94 +++++++++------- .../TextClassificationManager.java | 23 +++- .../view/textclassifier/TextClassifier.java | 8 -- .../textclassifier/TextClassifierImpl.java | 37 +++--- core/java/android/widget/Editor.java | 11 +- .../widget/SelectionActionModeHelper.java | 31 +++--- .../TextClassificationConstantsTest.java | 105 ++++++++++++++++++ .../TextClassifierConstantsTest.java | 46 -------- 10 files changed, 233 insertions(+), 145 deletions(-) rename core/java/android/view/textclassifier/{TextClassifierConstants.java => TextClassificationConstants.java} (70%) create mode 100644 core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java delete mode 100644 core/tests/coretests/src/android/view/textclassifier/TextClassifierConstantsTest.java diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 02994079d6ecc..569a0db768aa7 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -10516,8 +10516,11 @@ public final class Settings { * entity_list_default use ":" as delimiter for values. Ex: * *
-         * smart_selection_dark_launch              (boolean)
-         * smart_selection_enabled_for_edit_text    (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)
@@ -10530,7 +10533,7 @@ public final class Settings {
          * 

* Type: string * @hide - * see also android.view.textclassifier.TextClassifierConstants + * see also android.view.textclassifier.TextClassificationConstants */ public static final String TEXT_CLASSIFIER_CONSTANTS = "text_classifier_constants"; diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java index 1789edf1e8760..2b335fb09c614 100644 --- a/core/java/android/view/textclassifier/SystemTextClassifier.java +++ b/core/java/android/view/textclassifier/SystemTextClassifier.java @@ -29,6 +29,8 @@ import android.service.textclassifier.ITextClassifierService; import android.service.textclassifier.ITextLinksCallback; import android.service.textclassifier.ITextSelectionCallback; +import com.android.internal.util.Preconditions; + import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -40,13 +42,16 @@ final class SystemTextClassifier implements TextClassifier { private static final String LOG_TAG = "SystemTextClassifier"; private final ITextClassifierService mManagerService; + private final TextClassificationConstants mSettings; private final TextClassifier mFallback; private final String mPackageName; - SystemTextClassifier(Context context) throws ServiceManager.ServiceNotFoundException { + SystemTextClassifier(Context context, TextClassificationConstants settings) + throws ServiceManager.ServiceNotFoundException { mManagerService = ITextClassifierService.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.TEXT_CLASSIFICATION_SERVICE)); - mFallback = new TextClassifierImpl(context); + mSettings = Preconditions.checkNotNull(settings); + mFallback = new TextClassifierImpl(context, settings); mPackageName = context.getPackageName(); } @@ -108,6 +113,11 @@ final class SystemTextClassifier implements TextClassifier { public TextLinks generateLinks( @NonNull CharSequence text, @Nullable TextLinks.Options options) { Utils.validate(text, false /* allowInMainThread */); + + if (!mSettings.isSmartLinkifyEnabled()) { + return TextClassifier.NO_OP.generateLinks(text, options); + } + try { if (options == null) { options = new TextLinks.Options().setCallingPackageName(mPackageName); diff --git a/core/java/android/view/textclassifier/TextClassifierConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java similarity index 70% rename from core/java/android/view/textclassifier/TextClassifierConstants.java rename to core/java/android/view/textclassifier/TextClassificationConstants.java index 397473be9d4b4..32a7f376c4b1e 100644 --- a/core/java/android/view/textclassifier/TextClassifierConstants.java +++ b/core/java/android/view/textclassifier/TextClassificationConstants.java @@ -30,11 +30,15 @@ import java.util.StringJoiner; * This is encoded as a key=value list, separated by commas. Ex: * *

- * smart_selection_dark_launch              (boolean)
- * smart_selection_enabled_for_edit_text    (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[])
@@ -46,20 +50,24 @@ import java.util.StringJoiner;
  *
  * Example of setting the values for testing.
  * adb shell settings put global text_classifier_constants \
- *      smart_selection_dark_launch=true,smart_selection_enabled_for_edit_text=true,\
+ *      model_dark_launch_enabled=true,smart_selection_enabled=true,\
  *      entity_list_default=phone:address
  * @hide
  */
-public final class TextClassifierConstants {
+public final class TextClassificationConstants {
 
-    private static final String LOG_TAG = "TextClassifierConstants";
+    private static final String LOG_TAG = "TextClassificationConstants";
 
-    private static final String SMART_SELECTION_DARK_LAUNCH =
-            "smart_selection_dark_launch";
-    private static final String SMART_SELECTION_ENABLED_FOR_EDIT_TEXT =
-            "smart_selection_enabled_for_edit_text";
+    private static final String MODEL_DARK_LAUNCH_ENABLED =
+            "model_dark_launch_enabled";
+    private static final String SMART_SELECTION_ENABLED =
+            "smart_selection_enabled";
+    private static final String SMART_TEXT_SHARE_ENABLED =
+            "smart_text_share_enabled";
     private static final String SMART_LINKIFY_ENABLED =
             "smart_linkify_enabled";
+    private static final String SMART_SELECT_ANIMATION_ENABLED =
+            "smart_select_animation_enabled";
     private static final String SUGGEST_SELECTION_MAX_RANGE_LENGTH =
             "suggest_selection_max_range_length";
     private static final String CLASSIFY_TEXT_MAX_RANGE_LENGTH =
@@ -75,9 +83,11 @@ public final class TextClassifierConstants {
     private static final String ENTITY_LIST_EDITABLE =
             "entity_list_editable";
 
-    private static final boolean SMART_SELECTION_DARK_LAUNCH_DEFAULT = false;
-    private static final boolean SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT = true;
+    private static final boolean MODEL_DARK_LAUNCH_ENABLED_DEFAULT = false;
+    private static final boolean SMART_SELECTION_ENABLED_DEFAULT = true;
+    private static final boolean SMART_TEXT_SHARE_ENABLED_DEFAULT = true;
     private static final boolean SMART_LINKIFY_ENABLED_DEFAULT = true;
+    private static final boolean SMART_SELECT_ANIMATION_ENABLED_DEFAULT = true;
     private static final int SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
     private static final int CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
     private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000;
@@ -92,12 +102,11 @@ public final class TextClassifierConstants {
             .add(TextClassifier.TYPE_DATE_TIME)
             .add(TextClassifier.TYPE_FLIGHT_NUMBER).toString();
 
-    /** Default settings. */
-    static final TextClassifierConstants DEFAULT = new TextClassifierConstants();
-
-    private final boolean mDarkLaunch;
-    private final boolean mSuggestSelectionEnabledForEditableText;
+    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;
@@ -106,20 +115,7 @@ public final class TextClassifierConstants {
     private final List mEntityListNotEditable;
     private final List mEntityListEditable;
 
-    private TextClassifierConstants() {
-        mDarkLaunch = SMART_SELECTION_DARK_LAUNCH_DEFAULT;
-        mSuggestSelectionEnabledForEditableText = SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT;
-        mSmartLinkifyEnabled = SMART_LINKIFY_ENABLED_DEFAULT;
-        mSuggestSelectionMaxRangeLength = SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT;
-        mClassifyTextMaxRangeLength = CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT;
-        mGenerateLinksMaxTextLength = GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT;
-        mGenerateLinksLogSampleRate = GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT;
-        mEntityListDefault = parseEntityList(ENTITY_LIST_DEFAULT_VALUE);
-        mEntityListNotEditable = mEntityListDefault;
-        mEntityListEditable = mEntityListDefault;
-    }
-
-    private TextClassifierConstants(@Nullable String settings) {
+    private TextClassificationConstants(@Nullable String settings) {
         final KeyValueListParser parser = new KeyValueListParser(',');
         try {
             parser.setString(settings);
@@ -127,15 +123,21 @@ public final class TextClassifierConstants {
             // Failed to parse the settings string, log this and move on with defaults.
             Slog.e(LOG_TAG, "Bad TextClassifier settings: " + settings);
         }
-        mDarkLaunch = parser.getBoolean(
-                SMART_SELECTION_DARK_LAUNCH,
-                SMART_SELECTION_DARK_LAUNCH_DEFAULT);
-        mSuggestSelectionEnabledForEditableText = parser.getBoolean(
-                SMART_SELECTION_ENABLED_FOR_EDIT_TEXT,
-                SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT);
+        mModelDarkLaunchEnabled = parser.getBoolean(
+                MODEL_DARK_LAUNCH_ENABLED,
+                MODEL_DARK_LAUNCH_ENABLED_DEFAULT);
+        mSmartSelectionEnabled = parser.getBoolean(
+                SMART_SELECTION_ENABLED,
+                SMART_SELECTION_ENABLED_DEFAULT);
+        mSmartTextShareEnabled = parser.getBoolean(
+            SMART_TEXT_SHARE_ENABLED,
+            SMART_TEXT_SHARE_ENABLED_DEFAULT);
         mSmartLinkifyEnabled = parser.getBoolean(
                 SMART_LINKIFY_ENABLED,
                 SMART_LINKIFY_ENABLED_DEFAULT);
+        mSmartSelectionAnimationEnabled = parser.getBoolean(
+                SMART_SELECT_ANIMATION_ENABLED,
+                SMART_SELECT_ANIMATION_ENABLED_DEFAULT);
         mSuggestSelectionMaxRangeLength = parser.getInt(
                 SUGGEST_SELECTION_MAX_RANGE_LENGTH,
                 SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT);
@@ -160,22 +162,30 @@ public final class TextClassifierConstants {
     }
 
     /** Load from a settings string. */
-    public static TextClassifierConstants loadFromString(String settings) {
-        return new TextClassifierConstants(settings);
+    public static TextClassificationConstants loadFromString(String settings) {
+        return new TextClassificationConstants(settings);
     }
 
-    public boolean isDarkLaunch() {
-        return mDarkLaunch;
+    public boolean isModelDarkLaunchEnabled() {
+        return mModelDarkLaunchEnabled;
     }
 
-    public boolean isSuggestSelectionEnabledForEditableText() {
-        return mSuggestSelectionEnabledForEditableText;
+    public boolean isSmartSelectionEnabled() {
+        return mSmartSelectionEnabled;
+    }
+
+    public boolean isSmartTextShareEnabled() {
+        return mSmartTextShareEnabled;
     }
 
     public boolean isSmartLinkifyEnabled() {
         return mSmartLinkifyEnabled;
     }
 
+    public boolean isSmartSelectionAnimationEnabled() {
+        return mSmartSelectionAnimationEnabled;
+    }
+
     public int getSuggestSelectionMaxRangeLength() {
         return mSuggestSelectionMaxRangeLength;
     }
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index 300aef2d172e4..fea932cf2a505 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -20,6 +20,7 @@ import android.annotation.Nullable;
 import android.annotation.SystemService;
 import android.content.Context;
 import android.os.ServiceManager;
+import android.provider.Settings;
 import android.service.textclassifier.TextClassifierService;
 
 import com.android.internal.util.Preconditions;
@@ -38,12 +39,15 @@ public final class TextClassificationManager {
     private final Object mLock = new Object();
 
     private final Context mContext;
+    private final TextClassificationConstants mSettings;
     private TextClassifier mTextClassifier;
     private TextClassifier mSystemTextClassifier;
 
     /** @hide */
     public TextClassificationManager(Context context) {
         mContext = Preconditions.checkNotNull(context);
+        mSettings = TextClassificationConstants.loadFromString(Settings.Global.getString(
+                context.getContentResolver(), Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
     }
 
     /**
@@ -56,14 +60,14 @@ public final class TextClassificationManager {
             if (mSystemTextClassifier == null && isSystemTextClassifierEnabled()) {
                 try {
                     Log.d(LOG_TAG, "Initialized SystemTextClassifier");
-                    mSystemTextClassifier = new SystemTextClassifier(mContext);
+                    mSystemTextClassifier = new SystemTextClassifier(mContext, mSettings);
                 } catch (ServiceManager.ServiceNotFoundException e) {
                     Log.e(LOG_TAG, "Could not initialize SystemTextClassifier", e);
                 }
             }
             if (mSystemTextClassifier == null) {
                 Log.d(LOG_TAG, "Using an in-process TextClassifier as the system default");
-                mSystemTextClassifier = new TextClassifierImpl(mContext);
+                mSystemTextClassifier = new TextClassifierImpl(mContext, mSettings);
             }
         }
         return mSystemTextClassifier;
@@ -78,7 +82,7 @@ public final class TextClassificationManager {
                 if (isSystemTextClassifierEnabled()) {
                     mTextClassifier = getSystemDefaultTextClassifier();
                 } else {
-                    mTextClassifier = new TextClassifierImpl(mContext);
+                    mTextClassifier = new TextClassifierImpl(mContext, mSettings);
                 }
             }
             return mTextClassifier;
@@ -100,4 +104,17 @@ public final class TextClassificationManager {
         return SYSTEM_TEXT_CLASSIFIER_ENABLED
                 && TextClassifierService.getServiceComponentName(mContext) != null;
     }
+
+    /** @hide */
+    public static TextClassificationConstants getSettings(Context context) {
+        Preconditions.checkNotNull(context);
+        final TextClassificationManager tcm =
+                context.getSystemService(TextClassificationManager.class);
+        if (tcm != null) {
+            return tcm.mSettings;
+        } else {
+            return TextClassificationConstants.loadFromString(Settings.Global.getString(
+                    context.getContentResolver(), Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
+        }
+    }
 }
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index d52a30bcc018d..0321bb6d62fa0 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -328,14 +328,6 @@ public interface TextClassifier {
         return Logger.DISABLED;
     }
 
-    /**
-     * Returns this TextClassifier's settings.
-     * @hide
-     */
-    default TextClassifierConstants getSettings() {
-        return TextClassifierConstants.DEFAULT;
-    }
-
     /**
      * Configuration object for specifying what entities to identify.
      *
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 5b7095b2f44fa..41f1c69a47edc 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -34,7 +34,6 @@ import android.os.UserManager;
 import android.provider.Browser;
 import android.provider.CalendarContract;
 import android.provider.ContactsContract;
-import android.provider.Settings;
 import android.view.textclassifier.logging.DefaultLogger;
 import android.view.textclassifier.logging.GenerateLinksLogger;
 import android.view.textclassifier.logging.Logger;
@@ -99,13 +98,13 @@ public final class TextClassifierImpl implements TextClassifier {
     @GuardedBy("mLoggerLock") // Do not access outside this lock.
     private Logger mLogger;  // Should never be null if mLoggerConfig.get() is not null.
 
-    private TextClassifierConstants mSettings;
+    private final TextClassificationConstants mSettings;
 
-    public TextClassifierImpl(Context context) {
+    public TextClassifierImpl(Context context, TextClassificationConstants settings) {
         mContext = Preconditions.checkNotNull(context);
         mFallback = TextClassifier.NO_OP;
-        mGenerateLinksLogger = new GenerateLinksLogger(
-                getSettings().getGenerateLinksLogSampleRate());
+        mSettings = Preconditions.checkNotNull(settings);
+        mGenerateLinksLogger = new GenerateLinksLogger(mSettings.getGenerateLinksLogSampleRate());
     }
 
     /** @inheritDoc */
@@ -117,7 +116,7 @@ public final class TextClassifierImpl implements TextClassifier {
         try {
             final int rangeLength = selectionEndIndex - selectionStartIndex;
             if (text.length() > 0
-                    && rangeLength <= getSettings().getSuggestSelectionMaxRangeLength()) {
+                    && rangeLength <= mSettings.getSuggestSelectionMaxRangeLength()) {
                 final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
                 final String localesString = concatenateLocales(locales);
                 final Calendar refTime = Calendar.getInstance();
@@ -126,7 +125,7 @@ public final class TextClassifierImpl implements TextClassifier {
                 final String string = text.toString();
                 final int start;
                 final int end;
-                if (getSettings().isDarkLaunch() && !darkLaunchAllowed) {
+                if (mSettings.isModelDarkLaunchEnabled() && !darkLaunchAllowed) {
                     start = selectionStartIndex;
                     end = selectionEndIndex;
                 } else {
@@ -179,7 +178,7 @@ public final class TextClassifierImpl implements TextClassifier {
         Utils.validate(text, startIndex, endIndex, false /* allowInMainThread */);
         try {
             final int rangeLength = endIndex - startIndex;
-            if (text.length() > 0 && rangeLength <= getSettings().getClassifyTextMaxRangeLength()) {
+            if (text.length() > 0 && rangeLength <= mSettings.getClassifyTextMaxRangeLength()) {
                 final String string = text.toString();
                 final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
                 final String localesString = concatenateLocales(locales);
@@ -214,7 +213,7 @@ public final class TextClassifierImpl implements TextClassifier {
         final String textString = text.toString();
         final TextLinks.Builder builder = new TextLinks.Builder(textString);
 
-        if (!getSettings().isSmartLinkifyEnabled()) {
+        if (!mSettings.isSmartLinkifyEnabled()) {
             return builder.build();
         }
 
@@ -226,7 +225,7 @@ public final class TextClassifierImpl implements TextClassifier {
                     options != null && options.getEntityConfig() != null
                             ? options.getEntityConfig().resolveEntityListModifications(
                                     getEntitiesForHints(options.getEntityConfig().getHints()))
-                            : getSettings().getEntityListDefault();
+                            : mSettings.getEntityListDefault();
             final TextClassifierImplNative nativeImpl =
                     getNative(defaultLocales);
             final TextClassifierImplNative.AnnotatedSpan[] annotations =
@@ -268,7 +267,7 @@ public final class TextClassifierImpl implements TextClassifier {
     /** @inheritDoc */
     @Override
     public int getMaxGenerateLinksTextLength() {
-        return getSettings().getGenerateLinksMaxTextLength();
+        return mSettings.getGenerateLinksMaxTextLength();
     }
 
     private Collection getEntitiesForHints(Collection hints) {
@@ -278,11 +277,11 @@ public final class TextClassifierImpl implements TextClassifier {
         // Use the default if there is no hint, or conflicting ones.
         final boolean useDefault = editable == notEditable;
         if (useDefault) {
-            return getSettings().getEntityListDefault();
+            return mSettings.getEntityListDefault();
         } else if (editable) {
-            return getSettings().getEntityListEditable();
+            return mSettings.getEntityListEditable();
         } else {  // notEditable
-            return getSettings().getEntityListNotEditable();
+            return mSettings.getEntityListNotEditable();
         }
     }
 
@@ -298,16 +297,6 @@ public final class TextClassifierImpl implements TextClassifier {
         }
     }
 
-    /** @hide */
-    @Override
-    public TextClassifierConstants getSettings() {
-        if (mSettings == null) {
-            mSettings = TextClassifierConstants.loadFromString(Settings.Global.getString(
-                    mContext.getContentResolver(), Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
-        }
-        return mSettings;
-    }
-
     private TextClassifierImplNative getNative(LocaleList localeList)
             throws FileNotFoundException {
         synchronized (mLock) {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 2e7b2fd65d33f..02f35ca0d4d73 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -107,6 +107,7 @@ import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
 import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextLinks;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.TextView.Drawables;
@@ -4024,7 +4025,7 @@ public class Editor {
 
         private void updateAssistMenuItems(Menu menu) {
             clearAssistMenuItems(menu);
-            if (!mTextView.isDeviceProvisioned()) {
+            if (!shouldEnableAssistMenuItems()) {
                 return;
             }
             final TextClassification textClassification =
@@ -4097,7 +4098,7 @@ public class Editor {
 
             final TextClassification textClassification =
                     getSelectionActionModeHelper().getTextClassification();
-            if (!mTextView.isDeviceProvisioned() || textClassification == null) {
+            if (!shouldEnableAssistMenuItems() || textClassification == null) {
                 // No textClassification result to handle the click. Eat the click.
                 return true;
             }
@@ -4118,6 +4119,12 @@ public class Editor {
             return true;
         }
 
+        private boolean shouldEnableAssistMenuItems() {
+            return mTextView.isDeviceProvisioned()
+                && TextClassificationManager.getSettings(mTextView.getContext())
+                        .isSmartTextShareEnabled();
+        }
+
         @Override
         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
             getSelectionActionModeHelper().onSelectionAction(item.getItemId());
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 6ab09d6cbe741..12ab0ee7d56ac 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -34,6 +34,8 @@ import android.text.TextUtils;
 import android.util.Log;
 import android.view.ActionMode;
 import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextClassificationConstants;
+import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassifier;
 import android.view.textclassifier.TextLinks;
 import android.view.textclassifier.TextSelection;
@@ -65,12 +67,10 @@ public final class SelectionActionModeHelper {
 
     private static final String LOG_TAG = "SelectActionModeHelper";
 
-    // TODO: Make this a configurable flag.
-    private static final boolean SMART_SELECT_ANIMATION_ENABLED = true;
-
     private final Editor mEditor;
     private final TextView mTextView;
     private final TextClassificationHelper mTextClassificationHelper;
+    private final TextClassificationConstants mTextClassificationSettings;
 
     private TextClassification mTextClassification;
     private AsyncTask mTextClassificationAsyncTask;
@@ -84,6 +84,7 @@ public final class SelectionActionModeHelper {
     SelectionActionModeHelper(@NonNull Editor editor) {
         mEditor = Preconditions.checkNotNull(editor);
         mTextView = mEditor.getTextView();
+        mTextClassificationSettings = TextClassificationManager.getSettings(mTextView.getContext());
         mTextClassificationHelper = new TextClassificationHelper(
                 mTextView.getContext(),
                 mTextView.getTextClassifier(),
@@ -91,7 +92,7 @@ public final class SelectionActionModeHelper {
                 0, 1, mTextView.getTextLocales());
         mSelectionTracker = new SelectionTracker(mTextView);
 
-        if (SMART_SELECT_ANIMATION_ENABLED) {
+        if (mTextClassificationSettings.isSmartSelectionAnimationEnabled()) {
             mSmartSelectSprite = new SmartSelectSprite(mTextView.getContext(),
                     editor.getTextView().mHighlightColor, mTextView::invalidate);
         } else {
@@ -104,9 +105,7 @@ public final class SelectionActionModeHelper {
      */
     public void startSelectionActionModeAsync(boolean adjustSelection) {
         // Check if the smart selection should run for editable text.
-        adjustSelection &= !mTextView.isTextEditable()
-                || mTextView.getTextClassifier().getSettings()
-                        .isSuggestSelectionEnabledForEditableText();
+        adjustSelection &= mTextClassificationSettings.isSmartSelectionEnabled();
 
         mSelectionTracker.onOriginalSelection(
                 getText(mTextView),
@@ -249,7 +248,7 @@ public final class SelectionActionModeHelper {
                     || mTextView.isTextEditable()
                     || actionMode == Editor.TextActionMode.TEXT_LINK)) {
             // Do not change the selection if TextClassifier should be dark launched.
-            if (!mTextView.getTextClassifier().getSettings().isDarkLaunch()) {
+            if (!mTextClassificationSettings.isModelDarkLaunchEnabled()) {
                 Selection.setSelection((Spannable) text, result.mStart, result.mEnd);
                 mTextView.invalidate();
             }
@@ -450,7 +449,6 @@ public final class SelectionActionModeHelper {
             selectionEnd = mTextView.getSelectionEnd();
         }
         mTextClassificationHelper.init(
-                mTextView.getContext(),
                 mTextView.getTextClassifier(),
                 getText(mTextView),
                 selectionStart, selectionEnd,
@@ -882,7 +880,8 @@ public final class SelectionActionModeHelper {
 
         private static final int TRIM_DELTA = 120;  // characters
 
-        private Context mContext;
+        private final Context mContext;
+        private final boolean mDarkLaunchEnabled;
         private TextClassifier mTextClassifier;
 
         /** The original TextView text. **/
@@ -917,13 +916,15 @@ public final class SelectionActionModeHelper {
 
         TextClassificationHelper(Context context, TextClassifier textClassifier,
                 CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
-            init(context, textClassifier, text, selectionStart, selectionEnd, locales);
+            init(textClassifier, text, selectionStart, selectionEnd, locales);
+            mContext = Preconditions.checkNotNull(context);
+            mDarkLaunchEnabled = TextClassificationManager.getSettings(mContext)
+                    .isModelDarkLaunchEnabled();
         }
 
         @UiThread
-        public void init(Context context, TextClassifier textClassifier,
-                CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
-            mContext = Preconditions.checkNotNull(context);
+        public void init(TextClassifier textClassifier, CharSequence text,
+                int selectionStart, int selectionEnd, LocaleList locales) {
             mTextClassifier = Preconditions.checkNotNull(textClassifier);
             mText = Preconditions.checkNotNull(text).toString();
             mLastClassificationText = null; // invalidate.
@@ -956,7 +957,7 @@ public final class SelectionActionModeHelper {
                         mSelectionOptions.getDefaultLocales());
             }
             // Do not classify new selection boundaries if TextClassifier should be dark launched.
-            if (!mTextClassifier.getSettings().isDarkLaunch()) {
+            if (!mDarkLaunchEnabled) {
                 mSelectionStart = Math.max(0, selection.getSelectionStartIndex() + mTrimStart);
                 mSelectionEnd = Math.min(
                         mText.length(), selection.getSelectionEndIndex() + mTrimStart);
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
new file mode 100644
index 0000000000000..7f16359ad2693
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TextClassificationConstantsTest {
+
+    @Test
+    public void testLoadFromString() {
+        final String s = "model_dark_launch_enabled=true,"
+                + "smart_selection_enabled=true,"
+                + "smart_text_share_enabled=true,"
+                + "smart_linkify_enabled=true,"
+                + "smart_select_animation_enabled=true,"
+                + "suggest_selection_max_range_length=10,"
+                + "classify_text_max_range_length=11,"
+                + "generate_links_max_text_length=12,"
+                + "generate_links_log_sample_rate=13";
+        final TextClassificationConstants constants =
+                TextClassificationConstants.loadFromString(s);
+        assertTrue("model_dark_launch_enabled", constants.isModelDarkLaunchEnabled());
+        assertTrue("smart_selection_enabled", constants.isSmartSelectionEnabled());
+        assertTrue("smart_text_share_enabled", constants.isSmartTextShareEnabled());
+        assertTrue("smart_linkify_enabled", constants.isSmartLinkifyEnabled());
+        assertTrue("smart_select_animation_enabled", constants.isSmartSelectionAnimationEnabled());
+        assertEquals("suggest_selection_max_range_length",
+                10, constants.getSuggestSelectionMaxRangeLength());
+        assertEquals("classify_text_max_range_length",
+                11, constants.getClassifyTextMaxRangeLength());
+        assertEquals("generate_links_max_text_length",
+                12, constants.getGenerateLinksMaxTextLength());
+        assertEquals("generate_links_log_sample_rate",
+                13, constants.getGenerateLinksLogSampleRate());
+    }
+
+    @Test
+    public void testLoadFromString_differentValues() {
+        final String s = "model_dark_launch_enabled=false,"
+                + "smart_selection_enabled=false,"
+                + "smart_text_share_enabled=false,"
+                + "smart_linkify_enabled=false,"
+                + "smart_select_animation_enabled=false,"
+                + "suggest_selection_max_range_length=8,"
+                + "classify_text_max_range_length=7,"
+                + "generate_links_max_text_length=6,"
+                + "generate_links_log_sample_rate=5";
+        final TextClassificationConstants constants =
+                TextClassificationConstants.loadFromString(s);
+        assertFalse("model_dark_launch_enabled", constants.isModelDarkLaunchEnabled());
+        assertFalse("smart_selection_enabled", constants.isSmartSelectionEnabled());
+        assertFalse("smart_text_share_enabled", constants.isSmartTextShareEnabled());
+        assertFalse("smart_linkify_enabled", constants.isSmartLinkifyEnabled());
+        assertFalse("smart_select_animation_enabled",
+                constants.isSmartSelectionAnimationEnabled());
+        assertEquals("suggest_selection_max_range_length",
+                8, constants.getSuggestSelectionMaxRangeLength());
+        assertEquals("classify_text_max_range_length",
+                7, constants.getClassifyTextMaxRangeLength());
+        assertEquals("generate_links_max_text_length",
+                6, constants.getGenerateLinksMaxTextLength());
+        assertEquals("generate_links_log_sample_rate",
+                5, constants.getGenerateLinksLogSampleRate());
+    }
+
+    @Test
+    public void testEntityListParsing() {
+        final TextClassificationConstants constants = TextClassificationConstants.loadFromString(
+                "entity_list_default=phone,"
+                        + "entity_list_not_editable=address:flight,"
+                        + "entity_list_editable=date:datetime");
+        assertEquals(1, constants.getEntityListDefault().size());
+        assertEquals("phone", constants.getEntityListDefault().get(0));
+        assertEquals(2, constants.getEntityListNotEditable().size());
+        assertEquals("address", constants.getEntityListNotEditable().get(0));
+        assertEquals("flight", constants.getEntityListNotEditable().get(1));
+        assertEquals(2, constants.getEntityListEditable().size());
+        assertEquals("date", constants.getEntityListEditable().get(0));
+        assertEquals("datetime", constants.getEntityListEditable().get(1));
+    }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierConstantsTest.java
deleted file mode 100644
index 984eede556843..0000000000000
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierConstantsTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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 static org.junit.Assert.assertEquals;
-
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class TextClassifierConstantsTest {
-
-    @Test
-    public void testEntityListParsing() {
-        final TextClassifierConstants constants = TextClassifierConstants.loadFromString(
-                "entity_list_default=phone,"
-                        + "entity_list_not_editable=address:flight,"
-                        + "entity_list_editable=date:datetime");
-        assertEquals(1, constants.getEntityListDefault().size());
-        assertEquals("phone", constants.getEntityListDefault().get(0));
-        assertEquals(2, constants.getEntityListNotEditable().size());
-        assertEquals("address", constants.getEntityListNotEditable().get(0));
-        assertEquals("flight", constants.getEntityListNotEditable().get(1));
-        assertEquals(2, constants.getEntityListEditable().size());
-        assertEquals("date", constants.getEntityListEditable().get(0));
-        assertEquals("datetime", constants.getEntityListEditable().get(1));
-    }
-}