diff --git a/CHANGELOG.md b/CHANGELOG.md index 11204958..214cdd4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,13 @@ +# 99.0.4844.77 +* fix missing adaptive icon for updates +* do not close adblock filters editor when tapping reset button +* change text for 'Never' in history days to keep setting +* bring back dictionary hints in address bar + # 99.0.4844.58 * remove contextual search (fixes https://github.com/bromite/bromite/issues/1750) * remove global JIT settings -* add privacy setting for how many number of days of history to keep (thanks to @uazo) +* add privacy setting for how many number of days of history to keep (thanks to @uazo, fixes https://github.com/bromite/bromite/issues/1870) * disable UA full version (thanks to @uazo) * reintroduce patch for Save-Data header * updated zh_CN translations (thanks to @zhmars) diff --git a/build/LASTCHANGE b/build/LASTCHANGE new file mode 100644 index 00000000..1598696f --- /dev/null +++ b/build/LASTCHANGE @@ -0,0 +1 @@ +ffc51d2508554ada1582051f7917a6a2830d8376- \ No newline at end of file diff --git a/build/RELEASE b/build/RELEASE index f275bf35..e29aae56 100644 --- a/build/RELEASE +++ b/build/RELEASE @@ -1 +1 @@ -99.0.4844.58 \ No newline at end of file +99.0.4844.77 \ No newline at end of file diff --git a/build/RELEASE_COMMIT b/build/RELEASE_COMMIT new file mode 100644 index 00000000..82283783 --- /dev/null +++ b/build/RELEASE_COMMIT @@ -0,0 +1 @@ +005c250c47b142587053167a94f3576b257f482b \ No newline at end of file diff --git a/build/bromite_patches_list.txt b/build/bromite_patches_list.txt index b20e7363..53f47e9d 100644 --- a/build/bromite_patches_list.txt +++ b/build/bromite_patches_list.txt @@ -188,3 +188,4 @@ History-number-of-days-privacy-setting.patch Add-flag-for-save-data-header.patch Disable-UA-full-version.patch Automated-domain-substitution.patch +Revert-Disable-Dictionary-suggestions-for-the-Omnibox.patch diff --git a/build/patches/Add-custom-tab-intents-privacy-option.patch b/build/patches/Add-custom-tab-intents-privacy-option.patch index 1f4869ec..de550314 100644 --- a/build/patches/Add-custom-tab-intents-privacy-option.patch +++ b/build/patches/Add-custom-tab-intents-privacy-option.patch @@ -123,7 +123,7 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/Inco diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java --- a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java -@@ -96,6 +96,9 @@ public class PrivacySettings +@@ -92,6 +92,9 @@ public class PrivacySettings private ViewGroup mDialogContainer; private BottomSheetController mBottomSheetController; @@ -133,7 +133,7 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/setting @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { PrivacyPreferencesManagerImpl privacyPrefManager = -@@ -204,6 +207,9 @@ public class PrivacySettings +@@ -188,6 +191,9 @@ public class PrivacySettings updatePreferences(); } @@ -143,7 +143,7 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/setting @Override public boolean onPreferenceChange(Preference preference, Object newValue) { String key = preference.getKey(); -@@ -217,6 +223,14 @@ public class PrivacySettings +@@ -201,6 +207,14 @@ public class PrivacySettings } else if (PREF_SEARCH_SUGGESTIONS.equals(key)) { UserPrefs.get(Profile.getLastUsedRegularProfile()) .setBoolean(Pref.SEARCH_SUGGEST_ENABLED, (boolean) newValue); @@ -155,10 +155,10 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/setting + SharedPreferences.Editor sharedPreferencesEditor = ContextUtils.getAppSharedPreferences().edit(); + sharedPreferencesEditor.putBoolean(PREF_OPEN_EXTERNAL_LINKS_INCOGNITO, (boolean)newValue); + sharedPreferencesEditor.apply(); - } else if (PREF_AUTOFILL_ASSISTANT.equals(key)) { - AutofillAssistantPreferencesUtil.setAssistantEnabledPreference( (boolean) newValue); } else if (PREF_ALWAYS_INCOGNITO.equals(key)) { -@@ -253,6 +267,16 @@ public class PrivacySettings + AlwaysIncognitoLinkInterceptor.setAlwaysIncognito((boolean) newValue); + if (!mSnackbarManager.isShowing()) +@@ -235,6 +249,16 @@ public class PrivacySettings canMakePaymentPref.setChecked(prefService.getBoolean(Pref.CAN_MAKE_PAYMENT_ENABLED)); } diff --git a/build/patches/Automated-domain-substitution.patch b/build/patches/Automated-domain-substitution.patch index 46e69439..b8525058 100644 --- a/build/patches/Automated-domain-substitution.patch +++ b/build/patches/Automated-domain-substitution.patch @@ -9133,7 +9133,7 @@ diff --git a/chrome/browser/ui/quick_answers/quick_answers_access_token_fetcher. diff --git a/chrome/browser/ui/quick_answers/quick_answers_ui_controller.cc b/chrome/browser/ui/quick_answers/quick_answers_ui_controller.cc --- a/chrome/browser/ui/quick_answers/quick_answers_ui_controller.cc +++ b/chrome/browser/ui/quick_answers/quick_answers_ui_controller.cc -@@ -24,7 +24,7 @@ namespace { +@@ -22,7 +22,7 @@ namespace { using quick_answers::QuickAnswer; using quick_answers::QuickAnswersExitPoint; diff --git a/build/patches/Bromite-AdBlockUpdaterService.patch b/build/patches/Bromite-AdBlockUpdaterService.patch index c61b4417..9da443b0 100644 --- a/build/patches/Bromite-AdBlockUpdaterService.patch +++ b/build/patches/Bromite-AdBlockUpdaterService.patch @@ -16,7 +16,7 @@ Fix RestoreForeignSessionTab by recreating the tab (issue #681) chrome/android/java/res/values/values.xml | 2 + .../java/res/xml/adblock_preferences.xml | 25 ++ .../android/java/res/xml/main_preferences.xml | 5 + - .../browser/settings/AdBlockEditor.java | 92 ++++++ + .../browser/settings/AdBlockEditor.java | 91 ++++++ .../browser/settings/AdBlockPreferences.java | 61 ++++ .../chrome/browser/tabmodel/TabModelImpl.java | 2 +- chrome/app/generated_resources.grd | 10 + @@ -45,7 +45,7 @@ Fix RestoreForeignSessionTab by recreating the tab (issue #681) .../browser/subresource_filter_features.cc | 113 +------- .../core/common/indexed_ruleset.cc | 5 +- .../navigation_throttle_runner.cc | 5 - - 36 files changed, 1158 insertions(+), 124 deletions(-) + 36 files changed, 1157 insertions(+), 124 deletions(-) create mode 100644 chrome/android/java/res/layout/adblock_editor.xml create mode 100644 chrome/android/java/res/xml/adblock_preferences.xml create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/settings/AdBlockEditor.java @@ -247,7 +247,7 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/AdBloc new file mode 100644 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/AdBlockEditor.java -@@ -0,0 +1,92 @@ +@@ -0,0 +1,91 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. @@ -316,7 +316,6 @@ new file mode 100644 + @Override + public void onClick(View v) { + mAdBlockFiltersUrlEdit.setText(CachedFeatureFlags.getAdBlockFiltersURL()); -+ getActivity().finish(); + } + }); + diff --git a/build/patches/Bromite-auto-updater.patch b/build/patches/Bromite-auto-updater.patch index 5329da21..e45e9926 100644 --- a/build/patches/Bromite-auto-updater.patch +++ b/build/patches/Bromite-auto-updater.patch @@ -11,7 +11,7 @@ Enable checking for new versions, with notifications and proxy support .../about_settings/AboutChromeSettings.java | 28 +- .../chrome/browser/omaha/OmahaBase.java | 57 +++- .../chrome/browser/omaha/UpdateConfigs.java | 30 ++- - .../browser/omaha/UpdateMenuItemHelper.java | 81 +++++- + .../browser/omaha/UpdateMenuItemHelper.java | 82 +++++- .../browser/omaha/UpdateStatusProvider.java | 161 +++++++++--- .../browser/omaha/VersionNumberGetter.java | 3 +- .../inline/BromiteInlineUpdateController.java | 243 ++++++++++++++++++ @@ -26,7 +26,7 @@ Enable checking for new versions, with notifications and proxy support .../flags/android/chrome_feature_list.h | 1 + .../browser/flags/ChromeFeatureList.java | 1 + .../strings/android_chrome_strings.grd | 23 +- - 22 files changed, 897 insertions(+), 55 deletions(-) + 22 files changed, 898 insertions(+), 55 deletions(-) create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/BromiteInlineUpdateController.java create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateController.java create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateControllerFactory.java @@ -344,7 +344,7 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMen // The badge is hidden if the update menu item has been clicked until there is an // even newer version of Chrome available. showBadge |= !TextUtils.equals( -@@ -270,6 +284,71 @@ public class UpdateMenuItemHelper { +@@ -270,6 +284,72 @@ public class UpdateMenuItemHelper { mMenuUiState.itemState.icon = R.drawable.ic_error_24dp_filled; mMenuUiState.itemState.enabled = false; break; @@ -374,6 +374,7 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMen + mMenuUiState.buttonState.menuContentDescription = R.string.accessibility_toolbar_btn_menu_update; + mMenuUiState.buttonState.darkBadgeIcon = R.drawable.badge_update_dark; + mMenuUiState.buttonState.lightBadgeIcon = R.drawable.badge_update_light; ++ mMenuUiState.buttonState.adaptiveBadgeIcon = R.drawable.badge_update; + } + } + diff --git a/build/patches/Disable-FLoC-and-privacy-sandbox.patch b/build/patches/Disable-FLoC-and-privacy-sandbox.patch index fb172bb1..33c52cc7 100644 --- a/build/patches/Disable-FLoC-and-privacy-sandbox.patch +++ b/build/patches/Disable-FLoC-and-privacy-sandbox.patch @@ -57,7 +57,7 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/setting import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate; import org.chromium.chrome.browser.settings.SettingsLauncherImpl; -@@ -74,7 +72,6 @@ public class PrivacySettings +@@ -72,7 +70,6 @@ public class PrivacySettings private static final String PREF_SECURE_DNS = "secure_dns"; private static final String PREF_DO_NOT_TRACK = "do_not_track"; private static final String PREF_CLEAR_BROWSING_DATA = "clear_browsing_data"; @@ -65,7 +65,7 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/setting private static final String PREF_PRIVACY_REVIEW = "privacy_review"; private static final String PREF_INCOGNITO_LOCK = "incognito_lock"; private static final String PREF_PHONE_AS_A_SECURITY_KEY = "phone_as_a_security_key"; -@@ -106,18 +103,6 @@ public class PrivacySettings +@@ -102,18 +99,6 @@ public class PrivacySettings SettingsUtils.addPreferencesFromResource(this, R.xml.privacy_preferences); getActivity().setTitle(R.string.prefs_privacy_security); @@ -84,7 +84,7 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/setting Preference privacyReviewPreference = findPreference(PREF_PRIVACY_REVIEW); if (!ChromeFeatureList.isEnabled(ChromeFeatureList.PRIVACY_REVIEW)) { getPreferenceScreen().removePreference(privacyReviewPreference); -@@ -301,12 +286,6 @@ public class PrivacySettings +@@ -283,12 +268,6 @@ public class PrivacySettings secureDnsPref.setSummary(SecureDnsSettings.getSummary(getContext())); } diff --git a/build/patches/History-number-of-days-privacy-setting.patch b/build/patches/History-number-of-days-privacy-setting.patch index 5d212752..ea1680e3 100644 --- a/build/patches/History-number-of-days-privacy-setting.patch +++ b/build/patches/History-number-of-days-privacy-setting.patch @@ -89,15 +89,15 @@ diff --git a/chrome/android/java/res/xml/privacy_preferences.xml b/chrome/androi diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java --- a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java -@@ -41,6 +41,7 @@ import org.chromium.chrome.browser.autofill_assistant.AssistantFeatures; - import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantPreferencesUtil; +@@ -39,6 +39,7 @@ import org.chromium.chrome.browser.ApplicationLifetime; + import org.chromium.chrome.browser.signin.services.IdentityServicesProvider; import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; import org.chromium.components.browser_ui.settings.ChromeSwitchPreference; +import org.chromium.components.browser_ui.settings.SpinnerPreference; import org.chromium.components.browser_ui.settings.ManagedPreferenceDelegate; import org.chromium.components.browser_ui.settings.SettingsLauncher; import org.chromium.components.browser_ui.settings.SettingsUtils; -@@ -55,6 +56,9 @@ import androidx.preference.PreferenceCategory; +@@ -53,6 +54,9 @@ import androidx.preference.PreferenceCategory; import org.chromium.chrome.browser.preferences.SharedPreferencesManager; import org.chromium.components.browser_ui.settings.ChromeSwitchPreference; @@ -107,7 +107,7 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/setting /** * Fragment to keep track of the all the privacy related preferences. */ -@@ -96,6 +100,8 @@ public class PrivacySettings +@@ -92,6 +96,8 @@ public class PrivacySettings private ChromeSwitchPreference allowCustomTabIntentsPref; private ChromeSwitchPreference openExternalLinksPref; @@ -116,7 +116,7 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/setting @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { PrivacyPreferencesManagerImpl privacyPrefManager = -@@ -180,6 +186,38 @@ public class PrivacySettings +@@ -164,6 +170,38 @@ public class PrivacySettings Preference secureDnsPref = findPreference(PREF_SECURE_DNS); secureDnsPref.setVisible(SecureDnsSettings.isUiEnabled()); @@ -155,7 +155,7 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/setting Preference phoneAsASecurityKey = findPreference(PREF_PHONE_AS_A_SECURITY_KEY); phoneAsASecurityKey.setVisible( ChromeFeatureList.isEnabled(ChromeFeatureList.WEB_AUTH_PHONE_SUPPORT)); -@@ -300,6 +338,29 @@ public class PrivacySettings +@@ -282,6 +320,29 @@ public class PrivacySettings }; } @@ -182,9 +182,9 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/setting + } + } + - /** - * This checks whether Autofill Assistant is enabled and was shown at least once (only then - * will the AA switch be assigned a value). + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + menu.clear(); diff --git a/chrome/browser/history/history_service_factory.cc b/chrome/browser/history/history_service_factory.cc --- a/chrome/browser/history/history_service_factory.cc +++ b/chrome/browser/history/history_service_factory.cc @@ -230,7 +230,7 @@ diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chro + Expire history days threshold + + -+ Never ++ No history + + + Number of days to keep navigation history diff --git a/build/patches/Move-some-account-settings-back-to-privacy-settings.patch b/build/patches/Move-some-account-settings-back-to-privacy-settings.patch index b7717ca4..fa9f4ad7 100644 --- a/build/patches/Move-some-account-settings-back-to-privacy-settings.patch +++ b/build/patches/Move-some-account-settings-back-to-privacy-settings.patch @@ -4,15 +4,15 @@ Subject: Move some account settings back to privacy settings Search suggestions, autofill assistant and contextual search --- - .../java/res/xml/privacy_preferences.xml | 18 ++++++ - .../privacy/settings/PrivacySettings.java | 57 ++++++++++++++++++- - .../strings/android_chrome_strings.grd | 4 ++ - 3 files changed, 78 insertions(+), 1 deletion(-) + .../java/res/xml/privacy_preferences.xml | 13 ++++++++ + .../privacy/settings/PrivacySettings.java | 31 ++++++++++++++++++- + .../strings/android_chrome_strings.grd | 4 +++ + 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/chrome/android/java/res/xml/privacy_preferences.xml b/chrome/android/java/res/xml/privacy_preferences.xml --- a/chrome/android/java/res/xml/privacy_preferences.xml +++ b/chrome/android/java/res/xml/privacy_preferences.xml -@@ -64,6 +64,24 @@ +@@ -64,6 +64,19 @@ android:key="privacy_sandbox" android:title="@string/prefs_privacy_sandbox" android:fragment="org.chromium.chrome.browser.privacy_sandbox.PrivacySandboxSettingsFragment"/> @@ -24,11 +24,6 @@ diff --git a/chrome/android/java/res/xml/privacy_preferences.xml b/chrome/androi + android:title="@string/autocomplete_searches_and_urls_title" + android:summary="@string/autocomplete_searches_and_urls_summary" + android:persistent="false"/> -+ + - +Date: Sat, 19 Mar 2022 10:53:24 +0100 +Subject: Revert "Disable Dictionary suggestions for the Omnibox" + +This reverts commit d3ec2b2d25066ec089f8351a44b919ded4270e83. +--- + chrome/android/java/res/layout/url_bar.xml | 1 + + .../chrome/browser/omnibox/UrlBar.java | 931 ++++++++++++++++++ + .../chrome/browser/omnibox/UrlBar.java | 3 +- + 3 files changed, 933 insertions(+), 2 deletions(-) + create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java + +diff --git a/chrome/android/java/res/layout/url_bar.xml b/chrome/android/java/res/layout/url_bar.xml +--- a/chrome/android/java/res/layout/url_bar.xml ++++ b/chrome/android/java/res/layout/url_bar.xml +@@ -13,5 +13,6 @@ + android:singleLine="true" + android:imeOptions="actionGo|flagNoExtractUi|flagNoFullscreen" + android:textSize="@dimen/location_bar_url_text_size" ++ android:inputType="textUri" + android:hint="@string/search_or_type_web_address" + android:importantForAutofill="no" /> +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java +new file mode 100644 +--- /dev/null ++++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java +@@ -0,0 +1,931 @@ ++// Copyright 2015 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++package org.chromium.chrome.browser.omnibox; ++ ++import android.content.Context; ++import android.graphics.Canvas; ++import android.graphics.Paint; ++import android.graphics.Rect; ++import android.os.Build; ++import android.provider.Settings; ++import android.text.Editable; ++import android.text.InputType; ++import android.text.Layout; ++import android.text.Selection; ++import android.text.SpannableStringBuilder; ++import android.text.TextUtils; ++import android.text.TextWatcher; ++import android.text.style.ReplacementSpan; ++import android.util.AttributeSet; ++import android.view.GestureDetector; ++import android.view.KeyEvent; ++import android.view.MotionEvent; ++import android.view.View; ++import android.view.inputmethod.EditorInfo; ++import android.view.inputmethod.InputConnection; ++import android.widget.TextView; ++ ++import androidx.annotation.IntDef; ++import androidx.annotation.NonNull; ++import androidx.annotation.Nullable; ++import androidx.core.text.BidiFormatter; ++import androidx.core.util.ObjectsCompat; ++import androidx.core.view.inputmethod.EditorInfoCompat; ++ ++import org.chromium.base.ApiCompatibilityUtils; ++import org.chromium.base.Callback; ++import org.chromium.base.Log; ++import org.chromium.base.SysUtils; ++import org.chromium.base.ThreadUtils; ++import org.chromium.base.compat.ApiHelperForO; ++import org.chromium.base.metrics.RecordUserAction; ++import org.chromium.ui.KeyboardVisibilityDelegate; ++import org.chromium.ui.base.WindowDelegate; ++ ++import java.lang.annotation.Retention; ++import java.lang.annotation.RetentionPolicy; ++ ++/** ++ * The URL text entry view for the Omnibox. ++ */ ++public abstract class UrlBar extends AutocompleteEditText { ++ private static final String TAG = "UrlBar"; ++ ++ private static final boolean DEBUG = false; ++ ++ // TextView becomes very slow on long strings, so we limit maximum length ++ // of what is displayed to the user, see limitDisplayableLength(). ++ private static final int MAX_DISPLAYABLE_LENGTH = 4000; ++ private static final int MAX_DISPLAYABLE_LENGTH_LOW_END = 1000; ++ ++ private boolean mFirstDrawComplete; ++ ++ /** ++ * The text direction of the URL or query: LAYOUT_DIRECTION_LOCALE, LAYOUT_DIRECTION_LTR, or ++ * LAYOUT_DIRECTION_RTL. ++ * */ ++ private int mUrlDirection; ++ ++ private UrlBarDelegate mUrlBarDelegate; ++ private UrlTextChangeListener mUrlTextChangeListener; ++ private TextWatcher mTextChangedListener; ++ private UrlBarTextContextMenuDelegate mTextContextMenuDelegate; ++ private Callback mUrlDirectionListener; ++ ++ /** ++ * The gesture detector is used to detect long presses. Long presses require special treatment ++ * because the URL bar has custom touch event handling. See: {@link #onTouchEvent}. ++ */ ++ private final GestureDetector mGestureDetector; ++ ++ private final KeyboardHideHelper mKeyboardHideHelper; ++ ++ private boolean mFocused; ++ private boolean mSuppressingTouchMoveEventsForThisTouch; ++ private MotionEvent mSuppressedTouchDownEvent; ++ private boolean mAllowFocus = true; ++ ++ private boolean mPendingScroll; ++ private int mPreviousWidth; ++ ++ @ScrollType ++ private int mPreviousScrollType; ++ private String mPreviousScrollText; ++ private int mPreviousScrollViewWidth; ++ private int mPreviousScrollResultXPosition; ++ private float mPreviousScrollFontSize; ++ private boolean mPreviousScrollWasRtl; ++ ++ // Used as a hint to indicate the text may contain an ellipsize span. This will be true if an ++ // ellispize span was applied the last time the text changed. A true value here does not ++ // guarantee that the text does contain the span currently as newly set text may have cleared ++ // this (and it the value will only be recalculated after the text has been changed). ++ private boolean mDidEllipsizeTextHint; ++ ++ /** A cached point for getting this view's location in the window. */ ++ private final int[] mCachedLocation = new int[2]; ++ ++ /** The location of this view on the last ACTION_DOWN event. */ ++ private float mDownEventViewTop; ++ ++ /** ++ * The character index in the displayed text where the origin ends. This is required to ++ * ensure that the end of the origin is not scrolled out of view for long hostnames. ++ */ ++ private int mOriginEndIndex; ++ ++ @ScrollType ++ private int mScrollType; ++ ++ /** What scrolling action should be taken after the URL bar text changes. **/ ++ @IntDef({ScrollType.NO_SCROLL, ScrollType.SCROLL_TO_TLD, ScrollType.SCROLL_TO_BEGINNING}) ++ @Retention(RetentionPolicy.SOURCE) ++ public @interface ScrollType { ++ int NO_SCROLL = 0; ++ int SCROLL_TO_TLD = 1; ++ int SCROLL_TO_BEGINNING = 2; ++ } ++ ++ /** ++ * An optional string to use with AccessibilityNodeInfo to report text content. ++ * This is particularly important for auto-fill applications, such as password managers, that ++ * rely on AccessibilityNodeInfo data to apply related form-fill data. ++ */ ++ private CharSequence mTextForAutofillServices; ++ protected boolean mRequestingAutofillStructure; ++ ++ /** ++ * Delegate used to communicate with the content side and the parent layout. ++ */ ++ public interface UrlBarDelegate { ++ /** ++ * @return The view to be focused on a backward focus traversal. ++ */ ++ @Nullable ++ View getViewForUrlBackFocus(); ++ ++ /** ++ * @return Whether the keyboard should be allowed to learn from the user input. ++ */ ++ boolean allowKeyboardLearning(); ++ ++ /** ++ * Called to notify that back key has been pressed while the URL bar has focus. ++ */ ++ void backKeyPressed(); ++ ++ /** ++ * Called to notify that a tap or long press gesture has been detected. ++ * @param isLongPress Whether or not is a long press gesture. ++ */ ++ void gestureDetected(boolean isLongPress); ++ } ++ ++ /** Provides updates about the URL text changes. */ ++ public interface UrlTextChangeListener { ++ /** ++ * Called when the text state has changed. ++ * @param textWithoutAutocomplete The url bar text without autocompletion. ++ * @param textWithAutocomplete The url bar text with autocompletion. ++ */ ++ // TODO(crbug.com/1003080): Consider splitting these into two different callbacks. ++ void onTextChanged(String textWithoutAutocomplete, String textWithAutocomplete); ++ } ++ ++ /** Delegate that provides the additional functionality to the textual context menus. */ ++ interface UrlBarTextContextMenuDelegate { ++ /** @return The text to be pasted into the UrlBar. */ ++ @NonNull ++ String getTextToPaste(); ++ ++ /** ++ * Gets potential replacement text to be used instead of the current selected text for ++ * cut/copy actions. If null is returned, the existing text will be cut or copied. ++ * ++ * @param currentText The current displayed text. ++ * @param selectionStart The selection start in the display text. ++ * @param selectionEnd The selection end in the display text. ++ * @return The text to be cut/copied instead of the currently selected text. ++ */ ++ @Nullable ++ String getReplacementCutCopyText(String currentText, int selectionStart, int selectionEnd); ++ } ++ ++ public UrlBar(Context context, AttributeSet attrs) { ++ super(context, attrs); ++ mUrlDirection = LAYOUT_DIRECTION_LOCALE; ++ ++ // The URL Bar is derived from an text edit class, and as such is focusable by ++ // default. This means that if it is created before the first draw of the UI it ++ // will (as the only focusable element of the UI) get focus on the first draw. ++ // We react to this by greying out the tab area and bringing up the keyboard, ++ // which we don't want to do at startup. Prevent this by disabling focus until ++ // the first draw. ++ setFocusable(false); ++ setFocusableInTouchMode(false); ++ ++ // The HTC Sense IME will attempt to autocomplete words in the Omnibox when Prediction is ++ // enabled. We want to disable this feature and rely on the Omnibox's implementation. ++ // Their IME does not respect ~TYPE_TEXT_FLAG_AUTO_COMPLETE nor any of the other InputType ++ // options I tried, but setting the filter variation prevents it. Sadly, it also removes ++ // the .com button, but the prediction was buggy as it would autocomplete words even when ++ // typing at the beginning of the omnibox text when other content was present (messing up ++ // what was previously there). See bug: http://b/issue?id=6200071 ++ String defaultIme = Settings.Secure.getString( ++ getContext().getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); ++ if (defaultIme != null && defaultIme.contains("com.htc.android.htcime")) { ++ setInputType(getInputType() | InputType.TYPE_TEXT_VARIATION_FILTER); ++ } ++ ++ mGestureDetector = ++ new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() { ++ @Override ++ public void onLongPress(MotionEvent e) { ++ if (mUrlBarDelegate == null) return; ++ mUrlBarDelegate.gestureDetected(true); ++ performLongClick(); ++ } ++ ++ @Override ++ public boolean onSingleTapUp(MotionEvent e) { ++ if (mUrlBarDelegate == null) return true; ++ requestFocus(); ++ mUrlBarDelegate.gestureDetected(false); ++ return true; ++ } ++ }, ThreadUtils.getUiThreadHandler()); ++ mGestureDetector.setOnDoubleTapListener(null); ++ mKeyboardHideHelper = new KeyboardHideHelper(this, new Runnable() { ++ @Override ++ public void run() { ++ if (mUrlBarDelegate != null) mUrlBarDelegate.backKeyPressed(); ++ } ++ }); ++ ++ ApiCompatibilityUtils.disableSmartSelectionTextClassifier(this); ++ } ++ ++ public void destroy() { ++ setAllowFocus(false); ++ mUrlBarDelegate = null; ++ setOnFocusChangeListener(null); ++ mTextContextMenuDelegate = null; ++ mUrlTextChangeListener = null; ++ mTextChangedListener = null; ++ } ++ ++ /** ++ * Initialize the delegate that allows interaction with the Window. ++ */ ++ public void setWindowDelegate(WindowDelegate windowDelegate) { ++ mKeyboardHideHelper.setWindowDelegate(windowDelegate); ++ } ++ ++ /** ++ * Set the delegate to be used for text context menu actions. ++ */ ++ public void setTextContextMenuDelegate(UrlBarTextContextMenuDelegate delegate) { ++ mTextContextMenuDelegate = delegate; ++ } ++ ++ @Override ++ public boolean onKeyPreIme(int keyCode, KeyEvent event) { ++ if (KeyEvent.KEYCODE_BACK == keyCode && event.getAction() == KeyEvent.ACTION_UP) { ++ mKeyboardHideHelper.monitorForKeyboardHidden(); ++ } ++ return super.onKeyPreIme(keyCode, event); ++ } ++ ++ /** ++ * See {@link AutocompleteEditText#setIgnoreTextChangesForAutocomplete(boolean)}. ++ *

++ * {@link #setDelegate(UrlBarDelegate)} must be called with a non-null instance prior to ++ * enabling autocomplete. ++ */ ++ @Override ++ public void setIgnoreTextChangesForAutocomplete(boolean ignoreAutocomplete) { ++ assert mUrlBarDelegate != null; ++ super.setIgnoreTextChangesForAutocomplete(ignoreAutocomplete); ++ } ++ ++ @Override ++ protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { ++ mFocused = focused; ++ super.onFocusChanged(focused, direction, previouslyFocusedRect); ++ ++ if (focused) { ++ mPendingScroll = false; ++ } ++ fixupTextDirection(); ++ } ++ ++ /** ++ * Sets whether this {@link UrlBar} should be focusable. ++ */ ++ public void setAllowFocus(boolean allowFocus) { ++ mAllowFocus = allowFocus; ++ setFocusable(allowFocus); ++ setFocusableInTouchMode(allowFocus); ++ } ++ ++ /** ++ * Sets the {@link UrlBar}'s text direction based on focus and contents. ++ * ++ * Should be called whenever focus or text contents change. ++ */ ++ private void fixupTextDirection() { ++ // When unfocused, force left-to-right rendering at the paragraph level (which is desired ++ // for URLs). Right-to-left runs are still rendered RTL, but will not flip the whole URL ++ // around. This is consistent with OmniboxViewViews on desktop. When focused, render text ++ // normally (to allow users to make non-URL searches and to avoid showing Android's split ++ // insertion point when an RTL user enters RTL text). Also render text normally when the ++ // text field is empty (because then it displays an instruction that is not a URL). ++ if (mFocused || length() == 0) { ++ setTextDirection(TEXT_DIRECTION_INHERIT); ++ } else { ++ setTextDirection(TEXT_DIRECTION_LTR); ++ } ++ // Always align to the same as the paragraph direction (LTR = left, RTL = right). ++ setTextAlignment(TEXT_ALIGNMENT_TEXT_START); ++ } ++ ++ @Override ++ public void onWindowFocusChanged(boolean hasWindowFocus) { ++ super.onWindowFocusChanged(hasWindowFocus); ++ if (DEBUG) Log.i(TAG, "onWindowFocusChanged: " + hasWindowFocus); ++ if (hasWindowFocus) { ++ if (isFocused()) { ++ // Without the call to post(..), the keyboard was not getting shown when the ++ // window regained focus despite this being the final call in the view system ++ // flow. ++ post(new Runnable() { ++ @Override ++ public void run() { ++ KeyboardVisibilityDelegate.getInstance().showKeyboard(UrlBar.this); ++ } ++ }); ++ } ++ } ++ } ++ ++ @Override ++ public View focusSearch(int direction) { ++ if (mUrlBarDelegate != null && direction == View.FOCUS_BACKWARD ++ && mUrlBarDelegate.getViewForUrlBackFocus() != null) { ++ return mUrlBarDelegate.getViewForUrlBackFocus(); ++ } else { ++ return super.focusSearch(direction); ++ } ++ } ++ ++ @Override ++ protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { ++ super.onTextChanged(text, start, lengthBefore, lengthAfter); ++ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { ++ // Due to crbug.com/1103555, Autofill had to be disabled on the UrlBar to work around ++ // an issue on Android Q+. With Autofill disabled, the Autofill compat mode no longer ++ // learns of changes to the UrlBar, which prevents it from cancelling the session if ++ // the domain changes. We restore this behavior by mimicking the relevant part of ++ // TextView.notifyListeningManagersAfterTextChanged(). ++ // https://cs.android.com/android/platform/superproject/+/5d123b67756dffcfdebdb936ab2de2b29c799321:frameworks/base/core/java/android/widget/TextView.java;l=10618;drc=master;bpv=0 ++ ApiHelperForO.notifyValueChangedForAutofill(this); ++ } ++ } ++ ++ @Override ++ public boolean onTouchEvent(MotionEvent event) { ++ // This method contains special logic to enable long presses to be handled correctly. ++ ++ // One piece of the logic is to suppress all ACTION_DOWN events received while the UrlBar is ++ // not focused, and only pass them to super.onTouchEvent() if it turns out we're about to ++ // perform a long press. Long pressing will not behave properly without sending this event, ++ // but if we always send it immediately, it will cause the keyboard to show immediately, ++ // whereas we want to wait to show it until after the URL focus animation finishes, to avoid ++ // performance issues on slow devices. ++ ++ // The other piece of the logic is to suppress ACTION_MOVE events received after an ++ // ACTION_DOWN received while the UrlBar is not focused. This is because the UrlBar moves to ++ // the side as it's focusing, and a finger held still on the screen would therefore be ++ // interpreted as a drag selection. ++ ++ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { ++ getLocationInWindow(mCachedLocation); ++ mDownEventViewTop = mCachedLocation[1]; ++ mSuppressingTouchMoveEventsForThisTouch = !mFocused; ++ } ++ ++ if (!mFocused) { ++ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { ++ mSuppressedTouchDownEvent = MotionEvent.obtain(event); ++ } ++ mGestureDetector.onTouchEvent(event); ++ return true; ++ } ++ ++ if (event.getActionMasked() == MotionEvent.ACTION_UP ++ || event.getActionMasked() == MotionEvent.ACTION_CANCEL) { ++ // Minor optimization to avoid unnecessarily holding onto a MotionEvent after the touch ++ // finishes. ++ mSuppressedTouchDownEvent = null; ++ } ++ ++ if (mSuppressingTouchMoveEventsForThisTouch ++ && event.getActionMasked() == MotionEvent.ACTION_MOVE) { ++ return true; ++ } ++ ++ try { ++ return super.onTouchEvent(event); ++ } catch (NullPointerException e) { ++ // Working around a platform bug (b/25562038) that was fixed in N that can throw an ++ // exception during text selection. We just swallow the exception. The outcome is that ++ // the text selection handle doesn't show. ++ ++ // If this happens on N or later, there's a different issue here that we might want to ++ // know about. ++ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) throw e; ++ ++ Log.w(TAG, "Ignoring NPE in UrlBar#onTouchEvent.", e); ++ return true; ++ } catch (IndexOutOfBoundsException e) { ++ // Work around crash of unknown origin (https://crbug.com/837419). ++ Log.w(TAG, "Ignoring IndexOutOfBoundsException in UrlBar#onTouchEvent.", e); ++ return true; ++ } ++ } ++ ++ @Override ++ public boolean performLongClick() { ++ if (!shouldPerformLongClick()) return false; ++ ++ releaseSuppressedTouchDownEvent(); ++ return super.performLongClick(); ++ } ++ ++ /** ++ * @return Whether or not a long click should be performed. ++ */ ++ private boolean shouldPerformLongClick() { ++ getLocationInWindow(mCachedLocation); ++ ++ // If the view moved between the last down event, block the long-press. ++ return mDownEventViewTop == mCachedLocation[1]; ++ } ++ ++ private void releaseSuppressedTouchDownEvent() { ++ if (mSuppressedTouchDownEvent != null) { ++ super.onTouchEvent(mSuppressedTouchDownEvent); ++ mSuppressedTouchDownEvent = null; ++ } ++ } ++ ++ @Override ++ public void onDraw(Canvas canvas) { ++ super.onDraw(canvas); ++ ++ if (!mFirstDrawComplete) { ++ mFirstDrawComplete = true; ++ ++ // We have now avoided the first draw problem (see the comment in ++ // the constructor) so we want to make the URL bar focusable so that ++ // touches etc. activate it. ++ setFocusable(mAllowFocus); ++ setFocusableInTouchMode(mAllowFocus); ++ } ++ ++ // Notify listeners if the URL's direction has changed. ++ updateUrlDirection(); ++ } ++ ++ /** ++ * If the direction of the URL has changed, update mUrlDirection and notify the ++ * UrlDirectionListeners. ++ */ ++ private void updateUrlDirection() { ++ Layout layout = getLayout(); ++ if (layout == null) return; ++ ++ int urlDirection; ++ if (length() == 0) { ++ urlDirection = LAYOUT_DIRECTION_LOCALE; ++ } else if (layout.getParagraphDirection(0) == Layout.DIR_LEFT_TO_RIGHT) { ++ urlDirection = LAYOUT_DIRECTION_LTR; ++ } else { ++ urlDirection = LAYOUT_DIRECTION_RTL; ++ } ++ ++ if (urlDirection != mUrlDirection) { ++ mUrlDirection = urlDirection; ++ if (mUrlDirectionListener != null) { ++ mUrlDirectionListener.onResult(urlDirection); ++ } ++ ++ // Ensure the display text is visible after updating the URL direction. ++ scrollDisplayText(); ++ } ++ } ++ ++ /** ++ * @return The text direction of the URL, e.g. LAYOUT_DIRECTION_LTR. ++ */ ++ public int getUrlDirection() { ++ return mUrlDirection; ++ } ++ ++ /** ++ * Sets the listener for changes in the url bar's layout direction. Also calls ++ * onUrlDirectionChanged() immediately on the listener. ++ * ++ * @param listener The UrlDirectionListener to receive callbacks when the url direction changes, ++ * or null to unregister any previously registered listener. ++ */ ++ public void setUrlDirectionListener(Callback listener) { ++ mUrlDirectionListener = listener; ++ if (mUrlDirectionListener != null) { ++ mUrlDirectionListener.onResult(mUrlDirection); ++ } ++ } ++ ++ /** ++ * Set the url delegate to handle communication from the {@link UrlBar} to the rest of the UI. ++ * @param delegate The {@link UrlBarDelegate} to be used. ++ */ ++ public void setDelegate(UrlBarDelegate delegate) { ++ mUrlBarDelegate = delegate; ++ } ++ ++ /** ++ * Set the listener to be notified when the URL text has changed. ++ * @param listener The listener to be notified. ++ */ ++ public void setUrlTextChangeListener(UrlTextChangeListener listener) { ++ mUrlTextChangeListener = listener; ++ } ++ ++ /** ++ * Set the listener to be notified when the view's text has changed. ++ * @param textChangedListener The listener to be notified. ++ */ ++ public void setTextChangedListener(TextWatcher textChangedListener) { ++ if (ObjectsCompat.equals(mTextChangedListener, textChangedListener)) { ++ return; ++ } else if (mTextChangedListener != null) { ++ removeTextChangedListener(mTextChangedListener); ++ } ++ ++ mTextChangedListener = textChangedListener; ++ addTextChangedListener(mTextChangedListener); ++ } ++ ++ /** ++ * Set the text to report to Autofill services upon call to onProvideAutofillStructure. ++ */ ++ public void setTextForAutofillServices(CharSequence text) { ++ mTextForAutofillServices = text; ++ } ++ ++ @Override ++ public boolean onTextContextMenuItem(int id) { ++ if (mTextContextMenuDelegate == null) return super.onTextContextMenuItem(id); ++ ++ if (id == android.R.id.paste) { ++ String pasteString = mTextContextMenuDelegate.getTextToPaste(); ++ if (pasteString != null) { ++ int min = 0; ++ int max = getText().length(); ++ ++ if (isFocused()) { ++ final int selStart = getSelectionStart(); ++ final int selEnd = getSelectionEnd(); ++ ++ min = Math.max(0, Math.min(selStart, selEnd)); ++ max = Math.max(0, Math.max(selStart, selEnd)); ++ } ++ ++ Selection.setSelection(getText(), max); ++ getText().replace(min, max, pasteString); ++ onPaste(); ++ } ++ return true; ++ } ++ ++ if ((id == android.R.id.cut || id == android.R.id.copy)) { ++ if (id == android.R.id.cut) { ++ RecordUserAction.record("Omnibox.LongPress.Cut"); ++ } else { ++ RecordUserAction.record("Omnibox.LongPress.Copy"); ++ } ++ String currentText = getText().toString(); ++ String replacementCutCopyText = mTextContextMenuDelegate.getReplacementCutCopyText( ++ currentText, getSelectionStart(), getSelectionEnd()); ++ if (replacementCutCopyText == null) return super.onTextContextMenuItem(id); ++ ++ setIgnoreTextChangesForAutocomplete(true); ++ setText(replacementCutCopyText); ++ setSelection(0, replacementCutCopyText.length()); ++ setIgnoreTextChangesForAutocomplete(false); ++ ++ boolean retVal = super.onTextContextMenuItem(id); ++ ++ if (TextUtils.equals(getText(), replacementCutCopyText)) { ++ // Restore the old text if the operation did modify the text. ++ setIgnoreTextChangesForAutocomplete(true); ++ setText(currentText); ++ ++ // Move the cursor to the end. ++ setSelection(getText().length()); ++ setIgnoreTextChangesForAutocomplete(false); ++ } ++ ++ return retVal; ++ } ++ ++ if (id == android.R.id.shareText) { ++ RecordUserAction.record("Omnibox.LongPress.Share"); ++ } ++ ++ return super.onTextContextMenuItem(id); ++ } ++ ++ /** ++ * Specified how text should be scrolled within the UrlBar. ++ * ++ * @param scrollType What type of scroll should be applied to the text. ++ * @param scrollToIndex The index that should be scrolled to, which only applies to ++ * {@link ScrollType#SCROLL_TO_TLD}. ++ */ ++ public void setScrollState(@ScrollType int scrollType, int scrollToIndex) { ++ if (scrollType == ScrollType.SCROLL_TO_TLD) { ++ mOriginEndIndex = scrollToIndex; ++ } else { ++ mOriginEndIndex = 0; ++ } ++ mScrollType = scrollType; ++ scrollDisplayText(); ++ } ++ ++ /** ++ * Scrolls the omnibox text to a position determined by the current scroll type. ++ * ++ * @see #setScrollState(int, int) ++ */ ++ private void scrollDisplayText() { ++ if (isLayoutRequested()) { ++ mPendingScroll = mScrollType != ScrollType.NO_SCROLL; ++ return; ++ } ++ scrollDisplayTextInternal(mScrollType); ++ } ++ ++ /** ++ * Scrolls the omnibox text to the position specified, based on the {@link ScrollType}. ++ * ++ * @param scrollType What type of scroll to perform. ++ * SCROLL_TO_TLD: Scrolls the omnibox text to bring the TLD into view. ++ * SCROLL_TO_BEGINNING: Scrolls text that's too long to fit in the omnibox ++ * to the beginning so we can see the first character. ++ */ ++ private void scrollDisplayTextInternal(@ScrollType int scrollType) { ++ mPendingScroll = false; ++ ++ if (mFocused) return; ++ ++ Editable text = getText(); ++ if (TextUtils.isEmpty(text)) scrollType = ScrollType.SCROLL_TO_BEGINNING; ++ ++ // Ensure any selection from the focus state is cleared. ++ setSelection(0); ++ ++ float currentTextSize = getTextSize(); ++ boolean currentIsRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; ++ ++ int measuredWidth = getMeasuredWidth() - (getPaddingLeft() + getPaddingRight()); ++ if (scrollType == mPreviousScrollType && TextUtils.equals(text, mPreviousScrollText) ++ && measuredWidth == mPreviousScrollViewWidth ++ // Font size is float but it changes in discrete range (eg small font, big font), ++ // therefore false negative using regular equality is unlikely. ++ && currentTextSize == mPreviousScrollFontSize ++ && currentIsRtl == mPreviousScrollWasRtl) { ++ scrollTo(mPreviousScrollResultXPosition, getScrollY()); ++ return; ++ } ++ ++ switch (scrollType) { ++ case ScrollType.SCROLL_TO_TLD: ++ scrollToTLD(); ++ break; ++ case ScrollType.SCROLL_TO_BEGINNING: ++ scrollToBeginning(); ++ break; ++ default: ++ // Intentional return to avoid clearing scroll state when no scroll was applied. ++ return; ++ } ++ ++ mPreviousScrollType = scrollType; ++ mPreviousScrollText = text.toString(); ++ mPreviousScrollViewWidth = measuredWidth; ++ mPreviousScrollFontSize = currentTextSize; ++ mPreviousScrollResultXPosition = getScrollX(); ++ mPreviousScrollWasRtl = currentIsRtl; ++ } ++ ++ /** ++ * Scrolls the omnibox text to show the very beginning of the text entered. ++ */ ++ private void scrollToBeginning() { ++ Editable text = getText(); ++ float scrollPos = 0f; ++ if (TextUtils.isEmpty(text)) { ++ if (getLayoutDirection() == LAYOUT_DIRECTION_RTL ++ && BidiFormatter.getInstance().isRtl(getHint())) { ++ // Compared to below that uses getPrimaryHorizontal(1) due to 0 returning an ++ // invalid value, if the text is empty, getPrimaryHorizontal(0) returns the actual ++ // max scroll amount. ++ scrollPos = (int) getLayout().getPrimaryHorizontal(0) - getMeasuredWidth(); ++ } ++ } else if (BidiFormatter.getInstance().isRtl(text)) { ++ // RTL. ++ float endPointX = getLayout().getPrimaryHorizontal(text.length()); ++ int measuredWidth = getMeasuredWidth(); ++ float width = getLayout().getPaint().measureText(text.toString()); ++ scrollPos = Math.max(0, endPointX - measuredWidth + width); ++ } ++ scrollTo((int) scrollPos, getScrollY()); ++ } ++ ++ /** ++ * Scrolls the omnibox text to bring the TLD into view. ++ */ ++ private void scrollToTLD() { ++ Editable url = getText(); ++ int measuredWidth = getMeasuredWidth() - (getPaddingLeft() + getPaddingRight()); ++ ++ Layout textLayout = getLayout(); ++ assert getLayout().getLineCount() == 1; ++ final int originEndIndex = Math.min(mOriginEndIndex, url.length()); ++ if (mOriginEndIndex > url.length()) { ++ // If discovered locally, please update crbug.com/859219 with the steps to reproduce. ++ assert false : "Attempting to scroll past the end of the URL: " + url + ", end index: " ++ + mOriginEndIndex; ++ } ++ float endPointX = textLayout.getPrimaryHorizontal(originEndIndex); ++ // Compare the position offset of the last character and the character prior to determine ++ // the LTR-ness of the final component of the URL. ++ float priorToEndPointX = url.length() == 1 ++ ? 0 ++ : textLayout.getPrimaryHorizontal(Math.max(0, originEndIndex - 1)); ++ ++ float scrollPos; ++ if (priorToEndPointX < endPointX) { ++ // LTR ++ scrollPos = Math.max(0, endPointX - measuredWidth); ++ } else { ++ // RTL ++ ++ // To handle BiDirectional text, search backward from the two existing offsets to find ++ // the first LTR character. Ensure the final RTL component of the domain is visible ++ // above any of the prior LTR pieces. ++ int rtlStartIndexForEndingRun = originEndIndex - 1; ++ for (int i = originEndIndex - 2; i >= 0; i--) { ++ float indexOffsetDrawPosition = textLayout.getPrimaryHorizontal(i); ++ if (indexOffsetDrawPosition > endPointX) { ++ rtlStartIndexForEndingRun = i; ++ } else { ++ // getPrimaryHorizontal determines the index position for the next character ++ // based on the previous characters. In bi-directional text, the first RTL ++ // character following LTR text will have an LTR-appearing horizontal offset ++ // as it is based on the preceding LTR text. Thus, the start of the RTL ++ // character run will be after and including the first LTR horizontal index. ++ rtlStartIndexForEndingRun = Math.max(0, rtlStartIndexForEndingRun - 1); ++ break; ++ } ++ } ++ float width = textLayout.getPaint().measureText( ++ url.subSequence(rtlStartIndexForEndingRun, originEndIndex).toString()); ++ if (width < measuredWidth) { ++ scrollPos = Math.max(0, endPointX + width - measuredWidth); ++ } else { ++ scrollPos = endPointX + measuredWidth; ++ } ++ } ++ scrollTo((int) scrollPos, getScrollY()); ++ } ++ ++ @Override ++ protected void onLayout(boolean changed, int left, int top, int right, int bottom) { ++ super.onLayout(changed, left, top, right, bottom); ++ ++ if (mPendingScroll) { ++ scrollDisplayTextInternal(mScrollType); ++ } else if (mPreviousWidth != (right - left)) { ++ scrollDisplayTextInternal(mScrollType); ++ mPreviousWidth = right - left; ++ } ++ } ++ ++ @Override ++ public boolean bringPointIntoView(int offset) { ++ // TextView internally attempts to keep the selection visible, but in the unfocused state ++ // this class ensures that the TLD is visible. ++ if (!mFocused) return false; ++ assert !mPendingScroll; ++ ++ return super.bringPointIntoView(offset); ++ } ++ ++ @Override ++ public InputConnection onCreateInputConnection(EditorInfo outAttrs) { ++ InputConnection connection = super.onCreateInputConnection(outAttrs); ++ if (mUrlBarDelegate == null || !mUrlBarDelegate.allowKeyboardLearning()) { ++ outAttrs.imeOptions |= EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING; ++ } ++ return connection; ++ } ++ ++ @Override ++ public void setText(CharSequence text, BufferType type) { ++ if (DEBUG) Log.i(TAG, "setText -- text: %s", text); ++ super.setText(text, type); ++ fixupTextDirection(); ++ } ++ ++ private void limitDisplayableLength() { ++ // To limit displayable length we replace middle portion of the string with ellipsis. ++ // That affects only presentation of the text, and doesn't affect other aspects like ++ // copying to the clipboard, getting text with getText(), etc. ++ final int maxLength = ++ SysUtils.isLowEndDevice() ? MAX_DISPLAYABLE_LENGTH_LOW_END : MAX_DISPLAYABLE_LENGTH; ++ ++ Editable text = getText(); ++ int textLength = text.length(); ++ if (textLength <= maxLength) { ++ if (mDidEllipsizeTextHint) { ++ EllipsisSpan[] spans = text.getSpans(0, textLength, EllipsisSpan.class); ++ if (spans != null && spans.length > 0) { ++ assert spans.length == 1 : "Should never apply more than a single EllipsisSpan"; ++ for (int i = 0; i < spans.length; i++) { ++ text.removeSpan(spans[i]); ++ } ++ } ++ } ++ mDidEllipsizeTextHint = false; ++ return; ++ } ++ ++ mDidEllipsizeTextHint = true; ++ ++ int spanLeft = text.nextSpanTransition(0, textLength, EllipsisSpan.class); ++ if (spanLeft != textLength) return; ++ ++ spanLeft = maxLength / 2; ++ text.setSpan(EllipsisSpan.INSTANCE, spanLeft, textLength - spanLeft, ++ Editable.SPAN_INCLUSIVE_EXCLUSIVE); ++ } ++ ++ @Override ++ public Editable getText() { ++ if (mRequestingAutofillStructure) { ++ // crbug.com/1109186: mTextForAutofillServices must not be null here, but Autofill ++ // requests can be triggered before it is initialized. ++ return new SpannableStringBuilder( ++ mTextForAutofillServices != null ? mTextForAutofillServices : ""); ++ } ++ return super.getText(); ++ } ++ ++ @Override ++ public CharSequence getAccessibilityClassName() { ++ // When UrlBar is used as a read-only TextView, force Talkback to pronounce it like ++ // TextView. Otherwise Talkback will say "Edit box, http://...". crbug.com/636988 ++ if (isEnabled()) { ++ return super.getAccessibilityClassName(); ++ } else { ++ return TextView.class.getName(); ++ } ++ } ++ ++ @Override ++ public void replaceAllTextFromAutocomplete(String text) { ++ if (DEBUG) Log.i(TAG, "replaceAllTextFromAutocomplete: " + text); ++ setText(text); ++ } ++ ++ @Override ++ public void onAutocompleteTextStateChanged(boolean updateDisplay) { ++ if (DEBUG) { ++ Log.i(TAG, "onAutocompleteTextStateChanged: DIS[%b]", updateDisplay); ++ } ++ if (mUrlTextChangeListener == null) return; ++ if (updateDisplay) limitDisplayableLength(); ++ // crbug.com/764749 ++ Log.w(TAG, "Text change observed, triggering autocomplete."); ++ ++ mUrlTextChangeListener.onTextChanged( ++ getTextWithoutAutocomplete(), getTextWithAutocomplete()); ++ } ++ ++ /** ++ * Span that displays ellipsis instead of the text. Used to hide portion of ++ * very large string to get decent performance from TextView. ++ */ ++ private static class EllipsisSpan extends ReplacementSpan { ++ private static final String ELLIPSIS = "..."; ++ ++ public static final EllipsisSpan INSTANCE = new EllipsisSpan(); ++ ++ @Override ++ public int getSize( ++ Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) { ++ return (int) paint.measureText(ELLIPSIS); ++ } ++ ++ @Override ++ public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, ++ int y, int bottom, Paint paint) { ++ canvas.drawText(ELLIPSIS, x, y, paint); ++ } ++ } ++} +diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java +--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java ++++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java +@@ -206,8 +206,7 @@ public abstract class UrlBar extends AutocompleteEditText { + // the first draw. + setFocusable(false); + setFocusableInTouchMode(false); +- setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI +- | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); ++ setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI); + + // The HTC Sense IME will attempt to autocomplete words in the Omnibox when Prediction is + // enabled. We want to disable this feature and rely on the Omnibox's implementation. +-- +2.25.1