diff options
6 files changed, 148 insertions, 11 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index c04e016bf4d5..a248bf79f2a6 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9530,6 +9530,22 @@ public final class Settings { public static final String DEVICE_POLICY_CONSTANTS = "device_policy_constants"; /** + * TextClassifier specific settings. + * This is encoded as a key=value list, separated by commas. Ex: + * + * <pre> + * smart_selection_dark_launch (boolean) + * smart_selection_enabled_for_edit_text (boolean) + * </pre> + * + * <p> + * Type: string + * @hide + * see also android.view.textclassifier.TextClassifierConstants + */ + public static final String TEXT_CLASSIFIER_CONSTANTS = "text_classifier_constants"; + + /** * Get the key that retrieves a bluetooth headset's priority. * @hide */ diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java index bb1e693fbf43..c3601d9d32be 100644 --- a/core/java/android/view/textclassifier/TextClassifier.java +++ b/core/java/android/view/textclassifier/TextClassifier.java @@ -152,4 +152,12 @@ public interface TextClassifier { */ @WorkerThread default void logEvent(String source, String event) {} + + /** + * Returns this TextClassifier's settings. + * @hide + */ + default TextClassifierConstants getSettings() { + return TextClassifierConstants.DEFAULT; + } } diff --git a/core/java/android/view/textclassifier/TextClassifierConstants.java b/core/java/android/view/textclassifier/TextClassifierConstants.java new file mode 100644 index 000000000000..51e6168e9aa5 --- /dev/null +++ b/core/java/android/view/textclassifier/TextClassifierConstants.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.textclassifier; + +import android.annotation.Nullable; +import android.util.KeyValueListParser; +import android.util.Slog; + +/** + * TextClassifier specific settings. + * This is encoded as a key=value list, separated by commas. Ex: + * + * <pre> + * smart_selection_dark_launch (boolean) + * smart_selection_enabled_for_edit_text (boolean) + * </pre> + * + * <p> + * Type: string + * see also android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS + * + * 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 + * @hide + */ +public final class TextClassifierConstants { + + private static final String LOG_TAG = "TextClassifierConstants"; + + 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 boolean SMART_SELECTION_DARK_LAUNCH_DEFAULT = false; + private static final boolean SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT = true; + + /** Default settings. */ + static final TextClassifierConstants DEFAULT = new TextClassifierConstants(); + + private final boolean mDarkLaunch; + private final boolean mSuggestSelectionEnabledForEditableText; + + private TextClassifierConstants() { + mDarkLaunch = SMART_SELECTION_DARK_LAUNCH_DEFAULT; + mSuggestSelectionEnabledForEditableText = SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT; + } + + private TextClassifierConstants(@Nullable String settings) { + final KeyValueListParser parser = new KeyValueListParser(','); + try { + parser.setString(settings); + } catch (IllegalArgumentException e) { + // 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); + } + + static TextClassifierConstants loadFromString(String settings) { + return new TextClassifierConstants(settings); + } + + public boolean isDarkLaunch() { + return mDarkLaunch; + } + + public boolean isSuggestSelectionEnabledForEditableText() { + return mSuggestSelectionEnabledForEditableText; + } +} diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java index 7e93b78c4809..2e41404dde0a 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -24,11 +24,11 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; -import android.icu.text.BreakIterator; import android.net.Uri; import android.os.LocaleList; import android.os.ParcelFileDescriptor; import android.provider.Browser; +import android.provider.Settings; import android.text.Spannable; import android.text.TextUtils; import android.text.method.WordIterator; @@ -46,6 +46,7 @@ import com.android.internal.util.Preconditions; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.text.BreakIterator; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -90,6 +91,8 @@ final class TextClassifierImpl implements TextClassifier { @GuardedBy("mSmartSelectionLock") // Do not access outside this lock. private SmartSelection mSmartSelection; + private TextClassifierConstants mSettings; + TextClassifierImpl(Context context) { mContext = Preconditions.checkNotNull(context); } @@ -188,6 +191,15 @@ final class TextClassifierImpl implements TextClassifier { } } + @Override + public TextClassifierConstants getSettings() { + if (mSettings == null) { + mSettings = TextClassifierConstants.loadFromString(Settings.Global.getString( + mContext.getContentResolver(), Settings.Global.TEXT_CLASSIFIER_CONSTANTS)); + } + return mSettings; + } + private SmartSelection getSmartSelection(LocaleList localeList) throws FileNotFoundException { synchronized (mSmartSelectionLock) { localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList; diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index 513a18cdaf30..384e254e205f 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -71,11 +71,15 @@ final class SelectionActionModeHelper { } public void startActionModeAsync(boolean adjustSelection) { + // Check if the smart selection should run for editable text. + adjustSelection &= !mTextView.isTextEditable() + || mTextView.getTextClassifier().getSettings() + .isSuggestSelectionEnabledForEditableText(); + mSelectionTracker.onOriginalSelection( getText(mTextView), mTextView.getSelectionStart(), - mTextView.getSelectionEnd(), - mTextView.isTextEditable()); + mTextView.getSelectionEnd()); cancelAsyncTask(); if (skipTextClassification()) { startActionMode(null); @@ -163,7 +167,10 @@ final class SelectionActionModeHelper { private void startActionMode(@Nullable SelectionResult result) { final CharSequence text = getText(mTextView); if (result != null && text instanceof Spannable) { - Selection.setSelection((Spannable) text, result.mStart, result.mEnd); + // Do not change the selection if TextClassifier should be dark launched. + if (!mTextView.getTextClassifier().getSettings().isDarkLaunch()) { + Selection.setSelection((Spannable) text, result.mStart, result.mEnd); + } mTextClassification = result.mClassification; } else { mTextClassification = null; @@ -193,7 +200,7 @@ final class SelectionActionModeHelper { } private void resetTextClassificationHelper() { - mTextClassificationHelper.reset( + mTextClassificationHelper.init( mTextView.getTextClassifier(), getText(mTextView), mTextView.getSelectionStart(), mTextView.getSelectionEnd(), @@ -225,8 +232,7 @@ final class SelectionActionModeHelper { /** * Called when the original selection happens, before smart selection is triggered. */ - public void onOriginalSelection( - CharSequence text, int selectionStart, int selectionEnd, boolean editableText) { + public void onOriginalSelection(CharSequence text, int selectionStart, int selectionEnd) { // If we abandoned a selection and created a new one very shortly after, we may still // have a pending request to log ABANDON, which we flush here. mDelayedLogAbandon.flush(); @@ -622,11 +628,11 @@ final class SelectionActionModeHelper { TextClassificationHelper(TextClassifier textClassifier, CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) { - reset(textClassifier, text, selectionStart, selectionEnd, locales); + init(textClassifier, text, selectionStart, selectionEnd, locales); } @UiThread - public void reset(TextClassifier textClassifier, + public void init(TextClassifier textClassifier, CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) { mTextClassifier = Preconditions.checkNotNull(textClassifier); mText = Preconditions.checkNotNull(text).toString(); @@ -649,8 +655,12 @@ final class SelectionActionModeHelper { trimText(); final TextSelection selection = mTextClassifier.suggestSelection( mTrimmedText, mRelativeStart, mRelativeEnd, mLocales); - mSelectionStart = Math.max(0, selection.getSelectionStartIndex() + mTrimStart); - mSelectionEnd = Math.min(mText.length(), selection.getSelectionEndIndex() + mTrimStart); + // Do not classify new selection boundaries if TextClassifier should be dark launched. + if (!mTextClassifier.getSettings().isDarkLaunch()) { + mSelectionStart = Math.max(0, selection.getSelectionStartIndex() + mTrimStart); + mSelectionEnd = Math.min( + mText.length(), selection.getSelectionEndIndex() + mTrimStart); + } return performClassification(selection); } diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 7d9c50dc432e..662f20e58a73 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -344,6 +344,7 @@ public class SettingsBackupTest { Settings.Global.TETHER_DUN_REQUIRED, Settings.Global.TETHER_OFFLOAD_DISABLED, Settings.Global.TETHER_SUPPORTED, + Settings.Global.TEXT_CLASSIFIER_CONSTANTS, Settings.Global.THEATER_MODE_ON, Settings.Global.TRANSITION_ANIMATION_SCALE, Settings.Global.TRUSTED_SOUND, |