Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(922)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEngineAdapter.java

Issue 2367373003: [Android] Allow setting recently visited search engines as default search engine (Closed)
Patch Set: Update based on Dan's comments. Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chrome.browser.preferences; 5 package org.chromium.chrome.browser.preferences;
6 6
7 import android.content.Context; 7 import android.content.Context;
8 import android.content.Intent; 8 import android.content.Intent;
9 import android.content.SharedPreferences; 9 import android.content.SharedPreferences;
10 import android.content.res.Resources; 10 import android.content.res.Resources;
11 import android.os.Build; 11 import android.os.Build;
12 import android.os.Bundle; 12 import android.os.Bundle;
13 import android.text.SpannableString; 13 import android.text.SpannableString;
14 import android.text.style.ForegroundColorSpan; 14 import android.text.style.ForegroundColorSpan;
15 import android.view.LayoutInflater; 15 import android.view.LayoutInflater;
16 import android.view.View; 16 import android.view.View;
17 import android.view.View.AccessibilityDelegate; 17 import android.view.View.AccessibilityDelegate;
18 import android.view.View.OnClickListener; 18 import android.view.View.OnClickListener;
19 import android.view.ViewGroup; 19 import android.view.ViewGroup;
20 import android.view.accessibility.AccessibilityEvent; 20 import android.view.accessibility.AccessibilityEvent;
21 import android.view.accessibility.AccessibilityNodeInfo; 21 import android.view.accessibility.AccessibilityNodeInfo;
22 import android.widget.BaseAdapter; 22 import android.widget.BaseAdapter;
23 import android.widget.RadioButton; 23 import android.widget.RadioButton;
24 import android.widget.TextView; 24 import android.widget.TextView;
25 25
26 import org.chromium.base.ApiCompatibilityUtils; 26 import org.chromium.base.ApiCompatibilityUtils;
27 import org.chromium.base.ContextUtils; 27 import org.chromium.base.ContextUtils;
28 import org.chromium.base.metrics.RecordUserAction;
28 import org.chromium.chrome.R; 29 import org.chromium.chrome.R;
30 import org.chromium.chrome.browser.locale.LocaleManager;
29 import org.chromium.chrome.browser.omnibox.geo.GeolocationHeader; 31 import org.chromium.chrome.browser.omnibox.geo.GeolocationHeader;
30 import org.chromium.chrome.browser.preferences.website.ContentSetting; 32 import org.chromium.chrome.browser.preferences.website.ContentSetting;
31 import org.chromium.chrome.browser.preferences.website.GeolocationInfo; 33 import org.chromium.chrome.browser.preferences.website.GeolocationInfo;
32 import org.chromium.chrome.browser.preferences.website.SingleWebsitePreferences; 34 import org.chromium.chrome.browser.preferences.website.SingleWebsitePreferences;
33 import org.chromium.chrome.browser.preferences.website.WebsitePreferenceBridge; 35 import org.chromium.chrome.browser.preferences.website.WebsitePreferenceBridge;
34 import org.chromium.chrome.browser.search_engines.TemplateUrlService; 36 import org.chromium.chrome.browser.search_engines.TemplateUrlService;
35 import org.chromium.chrome.browser.search_engines.TemplateUrlService.LoadListene r; 37 import org.chromium.chrome.browser.search_engines.TemplateUrlService.LoadListene r;
36 import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrl ; 38 import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrl ;
37 import org.chromium.components.location.LocationUtils; 39 import org.chromium.components.location.LocationUtils;
38 import org.chromium.ui.text.SpanApplier; 40 import org.chromium.ui.text.SpanApplier;
39 import org.chromium.ui.text.SpanApplier.SpanInfo; 41 import org.chromium.ui.text.SpanApplier.SpanInfo;
40 42
43 import java.util.ArrayList;
41 import java.util.List; 44 import java.util.List;
42 45
43 /** 46 /**
44 * A custom adapter for listing search engines. 47 * A custom adapter for listing search engines.
45 */ 48 */
46 public class SearchEngineAdapter extends BaseAdapter implements LoadListener, On ClickListener { 49 public class SearchEngineAdapter extends BaseAdapter implements LoadListener, On ClickListener {
47 /** 50 private static final int VIEW_TYPE_ITEM = 0;
48 * A callback for reporting progress to the owner. 51 private static final int VIEW_TYPE_DIVIDER = 1;
49 */
50 public interface SelectSearchEngineCallback {
51 /**
52 * Called when the search engine data has loaded and we've determined th e currently active
53 * one.
54 * @param name Provides the name of it (with a simplified URL in parenth esis).
55 */
56 void currentSearchEngineDetermined(int selectedIndex);
57 }
58 52
59 // The current context. 53 // The current context.
60 private Context mContext; 54 private Context mContext;
61 55
62 // The layout inflater to use for the custom views. 56 // The layout inflater to use for the custom views.
63 private LayoutInflater mLayoutInflater; 57 private LayoutInflater mLayoutInflater;
64 58
65 // The callback to use for notifying caller of progress. 59 // The list of prepopluated and default search engines.
66 private SelectSearchEngineCallback mCallback; 60 private List<TemplateUrl> mPrepopulatedSearchEngines = new ArrayList<Templat eUrl>();
67 61
68 // The list of available search engines. 62 // The list of recently visited search engines.
69 private List<TemplateUrl> mSearchEngines; 63 private List<TemplateUrl> mRecentSearchEngines = new ArrayList<TemplateUrl>( );
70 // The position (index into mSearchEngines) of the currently selected search engine. Can be -1 64
71 // if current search engine is managed and set to something other than the p re-populated values. 65 // The position (index into mPrepopulatedSearchEngines) of the currently sel ected search engine.
66 // Can be -1 if current search engine is managed and set to something other than the
67 // pre-populated values.
72 private int mSelectedSearchEnginePosition = -1; 68 private int mSelectedSearchEnginePosition = -1;
73 69
74 // The position of the default search engine before user's action. 70 // The position of the default search engine before user's action.
75 private int mInitialEnginePosition = -1; 71 private int mInitialEnginePosition = -1;
76 72
77 /** 73 /**
78 * Construct a SearchEngineAdapter. 74 * Construct a SearchEngineAdapter.
79 * @param context The current context. 75 * @param context The current context.
80 * @param callback The callback to use to communicate back.
81 */ 76 */
82 public SearchEngineAdapter(Context context, SelectSearchEngineCallback callb ack) { 77 public SearchEngineAdapter(Context context) {
83 mContext = context; 78 mContext = context;
84 mLayoutInflater = (LayoutInflater) mContext.getSystemService( 79 mLayoutInflater = (LayoutInflater) mContext.getSystemService(
85 Context.LAYOUT_INFLATER_SERVICE); 80 Context.LAYOUT_INFLATER_SERVICE);
86 mCallback = callback;
87 81
88 initEntries(); 82 initEntries();
89 } 83 }
90 84
91 /**
92 * @return The index of the selected engine before user's action.
93 */
94 public int getInitialSearchEnginePosition() {
95 return mInitialEnginePosition;
96 }
97
98 // Used for testing. 85 // Used for testing.
99 86
100 String getValueForTesting() { 87 String getValueForTesting() {
101 return Integer.toString(mSelectedSearchEnginePosition); 88 return Integer.toString(mSelectedSearchEnginePosition);
102 } 89 }
103 90
104 void setValueForTesting(String value) { 91 String setValueForTesting(String value) {
105 searchEngineSelected(Integer.parseInt(value)); 92 return searchEngineSelected(Integer.parseInt(value));
93 }
94
95 String getKeywordForTesting(int index) {
96 return toKeyword(index);
106 } 97 }
107 98
108 /** 99 /**
109 * Initialize the search engine list. 100 * Initialize the search engine list.
110 */ 101 */
111 private void initEntries() { 102 private void initEntries() {
112 TemplateUrlService templateUrlService = TemplateUrlService.getInstance() ; 103 TemplateUrlService templateUrlService = TemplateUrlService.getInstance() ;
113 if (!templateUrlService.isLoaded()) { 104 if (!templateUrlService.isLoaded()) {
114 templateUrlService.registerLoadListener(this); 105 templateUrlService.registerLoadListener(this);
115 templateUrlService.load(); 106 templateUrlService.load();
116 return; // Flow continues in onTemplateUrlServiceLoaded below. 107 return; // Flow continues in onTemplateUrlServiceLoaded below.
117 } 108 }
118 109
119 // Fetch all the search engine info and the currently active one. 110 int defaultSearchEngineIndex = templateUrlService.getDefaultSearchEngine Index();
120 mSearchEngines = templateUrlService.getLocalizedSearchEngines(); 111 for (TemplateUrl templateUrl : templateUrlService.getSearchEngines()) {
121 int searchEngineIndex = templateUrlService.getDefaultSearchEngineIndex() ; 112 if (templateUrl.getType() == TemplateUrlService.TYPE_PREPOPULATED
122 // Convert the TemplateUrl index into an index into mSearchEngines. 113 || templateUrl.getType() == TemplateUrlService.TYPE_DEFAULT) {
114 mPrepopulatedSearchEngines.add(templateUrl);
115 } else {
116 mRecentSearchEngines.add(templateUrl);
117 }
118 }
119
120 // Convert the TemplateUrl index into an index of mSearchEngines.
123 mSelectedSearchEnginePosition = -1; 121 mSelectedSearchEnginePosition = -1;
124 for (int i = 0; i < mSearchEngines.size(); ++i) { 122 for (int i = 0; i < mPrepopulatedSearchEngines.size(); ++i) {
125 if (mSearchEngines.get(i).getIndex() == searchEngineIndex) { 123 if (mPrepopulatedSearchEngines.get(i).getIndex() == defaultSearchEng ineIndex) {
126 mSelectedSearchEnginePosition = i; 124 mSelectedSearchEnginePosition = i;
127 } 125 }
128 } 126 }
127
128 for (int i = 0; i < mRecentSearchEngines.size(); ++i) {
129 if (mRecentSearchEngines.get(i).getIndex() == defaultSearchEngineInd ex) {
130 // Add one to offset the title for recent search engine list
131 mSelectedSearchEnginePosition = i + mPrepopulatedSearchEngines.s ize() + 1;
132 }
133 }
134
129 mInitialEnginePosition = mSelectedSearchEnginePosition; 135 mInitialEnginePosition = mSelectedSearchEnginePosition;
130 136
131 // Report back what is selected. 137 TemplateUrlService.getInstance().setSearchEngine(toKeyword(mSelectedSear chEnginePosition));
132 mCallback.currentSearchEngineDetermined(toIndex(mSelectedSearchEnginePos ition));
133 } 138 }
134 139
135 private int toIndex(int position) { 140 private String toKeyword(int position) {
136 return mSearchEngines.get(position).getIndex(); 141 if (position < mPrepopulatedSearchEngines.size()) {
142 return mPrepopulatedSearchEngines.get(position).getKeyword();
143 } else {
144 position -= mPrepopulatedSearchEngines.size() + 1;
145 return mRecentSearchEngines.get(position).getKeyword();
146 }
137 } 147 }
138 148
139 // BaseAdapter: 149 // BaseAdapter:
140 150
141 @Override 151 @Override
142 public int getCount() { 152 public int getCount() {
143 return mSearchEngines == null ? 0 : mSearchEngines.size(); 153 return mPrepopulatedSearchEngines == null
154 ? 0
155 : mPrepopulatedSearchEngines.size() + mRecentSearchEngines.size( ) + 1;
144 } 156 }
145 157
146 @Override 158 @Override
147 public Object getItem(int pos) { 159 public Object getItem(int pos) {
148 TemplateUrl templateUrl = mSearchEngines.get(pos); 160 if (pos < mPrepopulatedSearchEngines.size()) {
149 return templateUrl.getShortName(); 161 return mPrepopulatedSearchEngines.get(pos);
162 } else if (pos > mPrepopulatedSearchEngines.size()) {
163 pos -= mPrepopulatedSearchEngines.size() + 1;
164 return mRecentSearchEngines.get(pos);
165 }
166 return null;
150 } 167 }
151 168
152 @Override 169 @Override
153 public long getItemId(int position) { 170 public long getItemId(int position) {
154 return position; 171 return position;
155 } 172 }
156 173
157 @Override 174 @Override
175 public int getItemViewType(int position) {
176 if (position == mPrepopulatedSearchEngines.size()) {
177 return VIEW_TYPE_DIVIDER;
178 } else {
179 return VIEW_TYPE_ITEM;
180 }
181 }
182
183 @Override
158 public View getView(int position, View convertView, ViewGroup parent) { 184 public View getView(int position, View convertView, ViewGroup parent) {
159 View view = convertView; 185 View view = convertView;
186 TemplateUrl templateUrl = (TemplateUrl) getItem(position);
187 int itemViewType = getItemViewType(position);
160 if (convertView == null) { 188 if (convertView == null) {
161 view = mLayoutInflater.inflate(R.layout.search_engine, null); 189 if (itemViewType == VIEW_TYPE_DIVIDER) {
gone 2016/11/01 21:30:29 I'm not sure this'll work: you're inflating two di
ltian 2016/11/01 22:59:36 The data is only populated in the constructor and
gone 2016/11/01 23:20:26 Doesn't matter if the order changes -- if the user
190 view = mLayoutInflater.inflate(R.layout.search_engine_recent_tit le, null);
191 } else {
192 view = mLayoutInflater.inflate(R.layout.search_engine, null);
193 }
194 }
195 if (itemViewType == VIEW_TYPE_DIVIDER) {
196 return view;
162 } 197 }
163 198
164 view.setOnClickListener(this); 199 view.setOnClickListener(this);
165 view.setTag(position); 200 view.setTag(position);
166 201
167 // TODO(finnur): There's a tinting bug in the AppCompat lib (see http:// crbug.com/474695), 202 // TODO(finnur): There's a tinting bug in the AppCompat lib (see http:// crbug.com/474695),
168 // which causes the first radiobox to always appear selected, even if it is not. It is being 203 // which causes the first radiobox to always appear selected, even if it is not. It is being
169 // addressed, but in the meantime we should use the native RadioButton i nstead. 204 // addressed, but in the meantime we should use the native RadioButton i nstead.
170 RadioButton radioButton = (RadioButton) view.findViewById(R.id.radiobutt on); 205 RadioButton radioButton = (RadioButton) view.findViewById(R.id.radiobutt on);
171 // On Lollipop this removes the redundant animation ring on selection bu t on older versions 206 // On Lollipop this removes the redundant animation ring on selection bu t on older versions
172 // it would cause the radio button to disappear. 207 // it would cause the radio button to disappear.
173 // TODO(finnur): Remove the encompassing if statement once we go back to using the AppCompat 208 // TODO(finnur): Remove the encompassing if statement once we go back to using the AppCompat
174 // control. 209 // control.
175 final boolean selected = position == mSelectedSearchEnginePosition; 210 final boolean selected = position == mSelectedSearchEnginePosition;
176 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 211 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
177 radioButton.setBackgroundResource(0); 212 radioButton.setBackgroundResource(0);
178 } 213 }
179 radioButton.setChecked(selected); 214 radioButton.setChecked(selected);
180 215
181 TextView description = (TextView) view.findViewById(R.id.description); 216 TextView description = (TextView) view.findViewById(R.id.description);
182 TemplateUrl templateUrl = mSearchEngines.get(position);
183 Resources resources = mContext.getResources(); 217 Resources resources = mContext.getResources();
184 description.setText(templateUrl.getShortName()); 218 description.setText(templateUrl.getShortName());
185 219
220 TextView url = (TextView) view.findViewById(R.id.url);
221 url.setText(templateUrl.getUrl());
222 if (templateUrl.getType() == TemplateUrlService.TYPE_PREPOPULATED
223 || templateUrl.getType() == TemplateUrlService.TYPE_DEFAULT
224 || templateUrl.getUrl().length() == 0) {
225 url.setVisibility(View.GONE);
226 }
227
186 // To improve the explore-by-touch experience, the radio button is hidde n from accessibility 228 // To improve the explore-by-touch experience, the radio button is hidde n from accessibility
187 // and instead, "checked" or "not checked" is read along with the search engine's name, e.g. 229 // and instead, "checked" or "not checked" is read along with the search engine's name, e.g.
188 // "google.com checked" or "google.com not checked". 230 // "google.com checked" or "google.com not checked".
189 radioButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILIT Y_NO); 231 radioButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILIT Y_NO);
190 description.setAccessibilityDelegate(new AccessibilityDelegate() { 232 description.setAccessibilityDelegate(new AccessibilityDelegate() {
191 @Override 233 @Override
192 public void onInitializeAccessibilityEvent(View host, AccessibilityE vent event) { 234 public void onInitializeAccessibilityEvent(View host, AccessibilityE vent event) {
193 super.onInitializeAccessibilityEvent(host, event); 235 super.onInitializeAccessibilityEvent(host, event);
194 event.setChecked(selected); 236 event.setChecked(selected);
195 } 237 }
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
240 282
241 @Override 283 @Override
242 public void onClick(View view) { 284 public void onClick(View view) {
243 if (view.getTag() == null) { 285 if (view.getTag() == null) {
244 onLocationLinkClicked(); 286 onLocationLinkClicked();
245 } else { 287 } else {
246 searchEngineSelected((int) view.getTag()); 288 searchEngineSelected((int) view.getTag());
247 } 289 }
248 } 290 }
249 291
250 private void searchEngineSelected(int position) { 292 private String searchEngineSelected(int position) {
251 // First clean up any automatically added permissions (if any) for the p reviously selected 293 // First clean up any automatically added permissions (if any) for the p reviously selected
252 // search engine. 294 // search engine.
253 SharedPreferences sharedPreferences = 295 SharedPreferences sharedPreferences =
254 ContextUtils.getAppSharedPreferences(); 296 ContextUtils.getAppSharedPreferences();
255 if (sharedPreferences.getBoolean(PrefServiceBridge.LOCATION_AUTO_ALLOWED , false)) { 297 if (sharedPreferences.getBoolean(PrefServiceBridge.LOCATION_AUTO_ALLOWED , false)) {
256 if (locationEnabled(mSelectedSearchEnginePosition, false)) { 298 if (locationEnabled(mSelectedSearchEnginePosition, false)) {
257 String url = TemplateUrlService.getInstance().getSearchEngineUrl FromTemplateUrl( 299 String url = TemplateUrlService.getInstance().getSearchEngineUrl FromTemplateUrl(
258 toIndex(mSelectedSearchEnginePosition)); 300 toKeyword(mSelectedSearchEnginePosition));
259 WebsitePreferenceBridge.nativeSetGeolocationSettingForOrigin( 301 WebsitePreferenceBridge.nativeSetGeolocationSettingForOrigin(
260 url, url, ContentSetting.DEFAULT.toInt(), false); 302 url, url, ContentSetting.DEFAULT.toInt(), false);
261 } 303 }
262 sharedPreferences.edit().remove(PrefServiceBridge.LOCATION_AUTO_ALLO WED).apply(); 304 sharedPreferences.edit().remove(PrefServiceBridge.LOCATION_AUTO_ALLO WED).apply();
263 } 305 }
264 306
265 // Record the change in search engine. 307 // Record the change in search engine.
266 mSelectedSearchEnginePosition = position; 308 mSelectedSearchEnginePosition = position;
267 309
268 // Report the change back. 310 String keyword = toKeyword(mSelectedSearchEnginePosition);
269 mCallback.currentSearchEngineDetermined(toIndex(mSelectedSearchEnginePos ition)); 311 TemplateUrlService.getInstance().setSearchEngine(keyword);
270 312
313 // If the user has manually set the default search engine, disable auto switching.
314 boolean manualSwitch = mSelectedSearchEnginePosition != mInitialEnginePo sition;
315 if (manualSwitch) {
316 RecordUserAction.record("SearchEngine_ManualChange");
317 LocaleManager.getInstance().setSearchEngineAutoSwitch(false);
318 }
271 notifyDataSetChanged(); 319 notifyDataSetChanged();
320 return keyword;
272 } 321 }
273 322
274 private void onLocationLinkClicked() { 323 private void onLocationLinkClicked() {
275 if (!LocationUtils.getInstance().isSystemLocationSettingEnabled()) { 324 if (!LocationUtils.getInstance().isSystemLocationSettingEnabled()) {
276 mContext.startActivity(LocationUtils.getInstance().getSystemLocation SettingsIntent()); 325 mContext.startActivity(LocationUtils.getInstance().getSystemLocation SettingsIntent());
277 } else { 326 } else {
278 Intent settingsIntent = PreferencesLauncher.createIntentForSettingsP age( 327 Intent settingsIntent = PreferencesLauncher.createIntentForSettingsP age(
279 mContext, SingleWebsitePreferences.class.getName()); 328 mContext, SingleWebsitePreferences.class.getName());
280 String url = TemplateUrlService.getInstance().getSearchEngineUrlFrom TemplateUrl( 329 String url = TemplateUrlService.getInstance().getSearchEngineUrlFrom TemplateUrl(
281 toIndex(mSelectedSearchEnginePosition)); 330 toKeyword(mSelectedSearchEnginePosition));
282 Bundle fragmentArgs = SingleWebsitePreferences.createFragmentArgsFor Site(url); 331 Bundle fragmentArgs = SingleWebsitePreferences.createFragmentArgsFor Site(url);
283 fragmentArgs.putBoolean(SingleWebsitePreferences.EXTRA_LOCATION, 332 fragmentArgs.putBoolean(SingleWebsitePreferences.EXTRA_LOCATION,
284 locationEnabled(mSelectedSearchEnginePosition, true)); 333 locationEnabled(mSelectedSearchEnginePosition, true));
285 settingsIntent.putExtra(Preferences.EXTRA_SHOW_FRAGMENT_ARGUMENTS, f ragmentArgs); 334 settingsIntent.putExtra(Preferences.EXTRA_SHOW_FRAGMENT_ARGUMENTS, f ragmentArgs);
286 mContext.startActivity(settingsIntent); 335 mContext.startActivity(settingsIntent);
287 } 336 }
288 } 337 }
289 338
290 private boolean locationEnabled(int position, boolean checkGeoHeader) { 339 private boolean locationEnabled(int position, boolean checkGeoHeader) {
291 if (position == -1) return false; 340 if (position == -1) return false;
292 341
293 String url = TemplateUrlService.getInstance().getSearchEngineUrlFromTemp lateUrl( 342 String url = TemplateUrlService.getInstance().getSearchEngineUrlFromTemp lateUrl(
294 toIndex(position)); 343 toKeyword(position));
295 GeolocationInfo locationSettings = new GeolocationInfo(url, null, false) ; 344 GeolocationInfo locationSettings = new GeolocationInfo(url, null, false) ;
296 ContentSetting locationPermission = locationSettings.getContentSetting() ; 345 ContentSetting locationPermission = locationSettings.getContentSetting() ;
297 // Handle the case where the geoHeader being sent when no permission has been specified. 346 // Handle the case where the geoHeader being sent when no permission has been specified.
298 if (locationPermission == ContentSetting.ASK && checkGeoHeader) { 347 if (locationPermission == ContentSetting.ASK && checkGeoHeader) {
299 return GeolocationHeader.isGeoHeaderEnabledForUrl(mContext, url, fal se); 348 return GeolocationHeader.isGeoHeaderEnabledForUrl(mContext, url, fal se);
300 } 349 }
301 return locationPermission == ContentSetting.ALLOW; 350 return locationPermission == ContentSetting.ALLOW;
302 } 351 }
303 } 352 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698