Commit 182e8759 authored by newt's avatar newt Committed by Commit bot

Upstream save passwords and search engine preferences.

BUG=428869

Review URL: https://codereview.chromium.org/797903002

Cr-Commit-Position: refs/heads/master@{#308147}
parent 80a68ab7
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2014 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. -->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:title="@string/password_entry_editor_title" >
<TextView
android:id="@+id/password_entry_editor_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:gravity="center_vertical"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@+id/password_entry_editor_url"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:gravity="center_vertical"
android:textAppearance="?android:attr/textAppearanceMedium" />
<!-- Spacer to move the buttons to the bottom -->
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"
android:minHeight="5dp" />
<!-- Top border for the buttons -->
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/dividerHorizontal" />
<!-- Save/Cancel/Delete buttons -->
<LinearLayout
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="?android:attr/dividerVertical"
android:dividerPadding="0dp"
android:orientation="horizontal"
android:showDividers="middle" >
<Button
android:id="@+id/password_entry_editor_delete"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:focusable="true"
android:text="@string/delete" />
<Button
android:id="@+id/password_entry_editor_cancel"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:focusable="true"
android:text="@string/cancel" />
</LinearLayout>
</LinearLayout>
</ScrollView>
\ No newline at end of file
// Copyright 2014 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.preferences;
import android.content.Context;
import android.preference.ListPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
/**
* Contains the basic functionality that should be shared by all ListPreference in Chrome.
*/
public class ChromeBaseListPreference extends ListPreference {
private ManagedPreferenceDelegate mManagedPrefDelegate;
/**
* Constructor for inflating from XML.
*/
public ChromeBaseListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* Sets the ManagedPreferenceDelegate which will determine whether this preference is managed.
*/
public void setManagedPreferenceDelegate(ManagedPreferenceDelegate delegate) {
mManagedPrefDelegate = delegate;
if (mManagedPrefDelegate != null) mManagedPrefDelegate.initPreference(this);
}
@Override
protected void onBindView(View view) {
super.onBindView(view);
((TextView) view.findViewById(android.R.id.title)).setSingleLine(false);
if (mManagedPrefDelegate != null) mManagedPrefDelegate.onBindViewToPreference(this, view);
}
@Override
protected void onClick() {
if (mManagedPrefDelegate != null && mManagedPrefDelegate.onClickPreference(this)) return;
super.onClick();
}
}
// Copyright 2014 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.preferences;
import android.content.Context;
import android.preference.Preference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
/**
* A preference that supports some Chrome-specific customizations:
*
* 1. This preference supports being managed. If this preference is managed (as determined by its
* ManagedPreferenceDelegate), it updates its appearance and behavior appropriately: shows an
* enterprise icon, disables clicks, etc.
*
* 2. This preference can have a multiline title.
*/
public class ChromeBasePreference extends Preference {
private ManagedPreferenceDelegate mManagedPrefDelegate;
/**
* Constructor for use in Java.
*/
public ChromeBasePreference(Context context) {
super(context);
}
/**
* Constructor for inflating from XML.
*/
public ChromeBasePreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* Sets the ManagedPreferenceDelegate which will determine whether this preference is managed.
*/
public void setManagedPreferenceDelegate(ManagedPreferenceDelegate delegate) {
mManagedPrefDelegate = delegate;
if (mManagedPrefDelegate != null) mManagedPrefDelegate.initPreference(this);
}
@Override
protected void onBindView(View view) {
super.onBindView(view);
((TextView) view.findViewById(android.R.id.title)).setSingleLine(false);
if (mManagedPrefDelegate != null) mManagedPrefDelegate.onBindViewToPreference(this, view);
}
@Override
protected void onClick() {
if (mManagedPrefDelegate != null && mManagedPrefDelegate.onClickPreference(this)) return;
super.onClick();
}
}
// Copyright 2014 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.preferences;
import android.content.Context;
import android.content.res.Resources;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.util.AttributeSet;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.search_engines.TemplateUrlService;
import org.chromium.chrome.browser.search_engines.TemplateUrlService.LoadListener;
import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrl;
import java.util.List;
/**
* Preference that allows the user to choose a search engine.
*/
public class SearchEnginePreference extends ChromeBaseListPreference implements LoadListener,
OnPreferenceChangeListener {
static final String PREF_SEARCH_ENGINE = "search_engine";
/**
* Constructor for inflating from XML.
*/
public SearchEnginePreference(Context context, AttributeSet attrs) {
super(context, attrs);
setPersistent(false);
initEntries();
setManagedPreferenceDelegate(new ManagedPreferenceDelegate() {
@Override
public boolean isPreferenceControlledByPolicy(Preference preference) {
return TemplateUrlService.getInstance().isSearchProviderManaged();
}
});
}
/**
* @return The name of the search engine followed by the domain, e.g. "Google (google.co.uk)".
*/
private static String getSearchEngineNameAndDomain(Resources res, TemplateUrl searchEngine) {
String title = searchEngine.getShortName();
if (!searchEngine.getKeyword().isEmpty()) {
title = res.getString(R.string.search_engine_name_and_domain, title,
searchEngine.getKeyword());
}
return title;
}
private void initEntries() {
TemplateUrlService templateUrlService = TemplateUrlService.getInstance();
if (!templateUrlService.isLoaded()) {
setEnabled(false);
templateUrlService.registerLoadListener(this);
templateUrlService.load();
return;
}
List<TemplateUrl> searchEngines = templateUrlService.getLocalizedSearchEngines();
int currentSearchEngineIndex = templateUrlService.getDefaultSearchEngineIndex();
Resources resources = getContext().getResources();
CharSequence[] entries = new CharSequence[searchEngines.size()];
CharSequence[] entryValues = new CharSequence[searchEngines.size()];
for (int i = 0; i < entries.length; i++) {
TemplateUrl templateUrl = searchEngines.get(i);
entries[i] = getSearchEngineNameAndDomain(resources, templateUrl);
entryValues[i] = Integer.toString(templateUrl.getIndex());
}
setEntries(entries);
setEntryValues(entryValues);
setValueIndex(currentSearchEngineIndex);
setOnPreferenceChangeListener(this);
}
@Override
public CharSequence getSummary() {
// Show the currently selected value as the summary.
return getEntry();
}
// OnPreferenceChangeListener
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
int newSearchEngineIndex = Integer.parseInt((String) newValue);
TemplateUrlService.getInstance().setSearchEngine(newSearchEngineIndex);
return true;
}
// TemplateUrlService.LoadListener
@Override
public void onTemplateUrlServiceLoaded() {
TemplateUrlService.getInstance().unregisterLoadListener(this);
setEnabled(true);
initEntries();
}
}
// Copyright 2014 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.preferences.password;
import android.app.Fragment;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import org.chromium.chrome.R;
/**
* Password entry editor that allows to view and delete passwords stored in Chrome.
*/
public class PasswordEntryEditor extends Fragment {
// ID of this name/password or exception.
private int mID;
// If true this is an exception site (never save here).
// If false this represents a saved name/password.
private boolean mException;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View v = inflater.inflate(R.layout.password_entry_editor, container, false);
getActivity().setTitle(R.string.password_entry_editor_title);
// Extras are set on this intent in class ManageSavedPasswordsPreferences.
Bundle extras = getArguments();
assert extras != null;
mID = extras.getInt(ManageSavedPasswordsPreferences.PASSWORD_LIST_ID);
String name = null;
if (extras.containsKey(ManageSavedPasswordsPreferences.PASSWORD_LIST_NAME)) {
name = extras.getString(ManageSavedPasswordsPreferences.PASSWORD_LIST_NAME);
}
TextView nameView = (TextView) v.findViewById(R.id.password_entry_editor_name);
if (name != null) {
nameView.setText(name);
} else {
nameView.setText(R.string.section_saved_passwords_exceptions);
mException = true;
}
String url = extras.getString(ManageSavedPasswordsPreferences.PASSWORD_LIST_URL);
TextView urlView = (TextView) v.findViewById(R.id.password_entry_editor_url);
urlView.setText(url);
hookupCancelDeleteButtons(v);
return v;
}
// Delete was clicked.
private void removeItem() {
Intent data = new Intent();
data.putExtra(ManageSavedPasswordsPreferences.PASSWORD_LIST_DELETED_ID, mID);
data.putExtra(ManageSavedPasswordsPreferences.DELETED_ITEM_IS_EXCEPTION, mException);
getActivity().setResult(ManageSavedPasswordsPreferences.RESULT_DELETE_PASSWORD, data);
}
private void hookupCancelDeleteButtons(View v) {
Button button = (Button) v.findViewById(R.id.password_entry_editor_delete);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
removeItem();
getActivity().finish();
}
});
button = (Button) v.findViewById(R.id.password_entry_editor_cancel);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getActivity().finish();
}
});
}
}
......@@ -125,6 +125,14 @@
Managed by your administrator
</message>
<!-- Search engine preferences -->
<message name="IDS_PREFS_SEARCH_ENGINE" desc="Title for Search Engine preferences. [CHAR-LIMIT=32]">
Search engine
</message>
<message name="IDS_SEARCH_ENGINE_NAME_AND_DOMAIN" desc="Label for a search engine, that includes its name and its domain, e.g. Google (google.co.uk).">
<ph name="SEARCH_ENGINE_NAME">%1$s<ex>Google</ex></ph> (<ph name="SEARCH_ENGINE_DOMAIN">%2$s<ex>google.co.uk</ex></ph>)
</message>
<!-- Autofill preferences -->
<message name="IDS_PREFS_AUTOFILL" desc="Title for 'Autofill forms' settings, which control what personal data can be automatically filled into web page forms. [CHAR-LIMIT=32]">
Autofill forms
......@@ -169,6 +177,26 @@
Card expiration year
</message>
<!-- Save passwords preferences -->
<message name="IDS_PREFS_SAVED_PASSWORDS" desc="Title for the Saved Passwords preferences. [CHAR-LIMIT=32]">
Save passwords
</message>
<message name="IDS_SECTION_SAVED_PASSWORDS" desc="Header for the list of passwords that have been saved in Chrome. [CHAR-LIMIT=32]">
Passwords
</message>
<message name="IDS_SECTION_SAVED_PASSWORDS_EXCEPTIONS" desc="Header for the list of websites for which user selected to never save passwords. [CHAR-LIMIT=32]">
Never saved
</message>
<message name="IDS_MANAGE_PASSWORDS_TEXT" desc="Text for link to manage passwords on Account Central.">
Manage saved passwords in your <ph name="BEGIN_LINK">&lt;link&gt;</ph>Google Account<ph name="END_LINK">&lt;/link&gt;</ph>
</message>
<message name="IDS_SAVED_PASSWORDS_NONE_TEXT" desc="Text when there are no saved passwords/exceptions.">
Saved passwords will appear here.
</message>
<message name="IDS_PASSWORD_ENTRY_EDITOR_TITLE" desc='Title of the "edit a name/password" screen.'>
Edit saved name/password or exception
</message>
<!-- Accessibility preferences -->
<message name="IDS_PREFS_ACCESSIBILITY" desc="Title of Accessibility settings, which allows the user to change webpage font sizes. [CHAR-LIMIT=32]">
Accessibility
......
......@@ -8,14 +8,22 @@ import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.test.suitebuilder.annotation.SmallTest;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.accessibility.FontSizePrefs;
import org.chromium.chrome.browser.search_engines.TemplateUrlService;
import org.chromium.chrome.browser.search_engines.TemplateUrlService.LoadListener;
import org.chromium.chrome.shell.ChromeShellTestBase;
import org.chromium.chrome.shell.preferences.ChromeShellMainPreferences;
import org.chromium.content.browser.test.util.CallbackHelper;
import org.chromium.content.browser.test.util.UiUtils;
import java.lang.reflect.Method;
import java.text.NumberFormat;
/**
......@@ -42,6 +50,74 @@ public class PreferencesTest extends ChromeShellTestBase {
return (Preferences) activity;
}
public static void clickPreference(PreferenceFragment fragment, Preference preference) {
try {
Method performClick = Preference.class.getDeclaredMethod("performClick",
PreferenceScreen.class);
performClick.invoke(preference, fragment.getPreferenceScreen());
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
/**
* Change search engine and make sure it works correctly.
*/
@SmallTest
@Feature({"Preferences"})
public void testSearchEnginePreference() throws Exception {
// Make sure the template_url_service is loaded.
final CallbackHelper onTemplateUrlServiceLoadedHelper = new CallbackHelper();
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
if (TemplateUrlService.getInstance().isLoaded()) {
onTemplateUrlServiceLoadedHelper.notifyCalled();
} else {
TemplateUrlService.getInstance().registerLoadListener(new LoadListener() {
@Override
public void onTemplateUrlServiceLoaded() {
onTemplateUrlServiceLoadedHelper.notifyCalled();
}
});
TemplateUrlService.getInstance().load();
}
}
});
onTemplateUrlServiceLoadedHelper.waitForCallback(0);
// Set the second search engine as the default using TemplateUrlService.
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
TemplateUrlService.getInstance().setSearchEngine(1);
}
});
final Preferences prefActivity = startPreferences(getInstrumentation(),
ChromeShellMainPreferences.class.getName());
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
// Ensure that the second search engine in the list is selected.
PreferenceFragment fragment = (PreferenceFragment)
prefActivity.getFragmentForTest();
SearchEnginePreference pref = (SearchEnginePreference)
fragment.findPreference(SearchEnginePreference.PREF_SEARCH_ENGINE);
assertNotNull(pref);
assertEquals("1", pref.getValue());
// Simulate selecting the third search engine and ensure that TemplateUrlService
// is updated.
if (pref.getOnPreferenceChangeListener().onPreferenceChange(pref, "2")) {
pref.setValue("2");
}
assertEquals(2, TemplateUrlService.getInstance().getDefaultSearchEngineIndex());
}
});
}
/**
* Tests setting FontScaleFactor and ForceEnableZoom in AccessibilityPreferences and ensures
* that ForceEnableZoom changes corresponding to FontScaleFactor.
......
// Copyright 2014 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.preferences.password;
import android.test.suitebuilder.annotation.SmallTest;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.preferences.ChromeSwitchPreference;
import org.chromium.chrome.browser.preferences.PrefServiceBridge;
import org.chromium.chrome.browser.preferences.Preferences;
import org.chromium.chrome.browser.preferences.PreferencesTest;
import org.chromium.chrome.shell.ChromeShellTestBase;
/**
* Tests for the "Save Passwords" settings screen.
*/
public class SavedPasswordsPreferencesTest extends ChromeShellTestBase {
@Override
protected void setUp() throws Exception {
super.setUp();
startChromeBrowserProcessSync(getInstrumentation().getTargetContext());
}
/**
* Ensure that the on/off switch in "Save Passwords" settings actually enables and disables
* password saving.
*/
@SmallTest
@Feature({"Preferences"})
public void testSavePasswordsSwitch() throws Exception {
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
PrefServiceBridge.getInstance().setRememberPasswordsEnabled(true);
}
});
final Preferences preferences = PreferencesTest.startPreferences(getInstrumentation(),
ManageSavedPasswordsPreferences.class.getName());
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
ManageSavedPasswordsPreferences savedPasswordPrefs =
(ManageSavedPasswordsPreferences) preferences.getFragmentForTest();
ChromeSwitchPreference onOffSwitch = (ChromeSwitchPreference)
savedPasswordPrefs.findPreference(
ManageSavedPasswordsPreferences.PREF_SAVE_PASSWORDS_SWITCH);
assertTrue(onOffSwitch.isChecked());
PreferencesTest.clickPreference(savedPasswordPrefs, onOffSwitch);
assertFalse(PrefServiceBridge.getInstance().isRememberPasswordsEnabled());
PreferencesTest.clickPreference(savedPasswordPrefs, onOffSwitch);
assertTrue(PrefServiceBridge.getInstance().isRememberPasswordsEnabled());
preferences.finish();
PrefServiceBridge.getInstance().setRememberPasswordsEnabled(false);
}
});
final Preferences preferences2 = PreferencesTest.startPreferences(getInstrumentation(),
ManageSavedPasswordsPreferences.class.getName());
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
ManageSavedPasswordsPreferences savedPasswordPrefs =
(ManageSavedPasswordsPreferences) preferences2.getFragmentForTest();
ChromeSwitchPreference onOffSwitch = (ChromeSwitchPreference)
savedPasswordPrefs.findPreference(
ManageSavedPasswordsPreferences.PREF_SAVE_PASSWORDS_SWITCH);
assertFalse(onOffSwitch.isChecked());
}
});
}
}
......@@ -4,9 +4,15 @@
found in the LICENSE file. -->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<org.chromium.chrome.browser.preferences.SearchEnginePreference
android:key="search_engine"
android:title="@string/prefs_search_engine"/>
<Preference
android:fragment="org.chromium.chrome.browser.preferences.autofill.AutofillPreferences"
android:title="@string/prefs_autofill" />
<Preference
android:fragment="org.chromium.chrome.browser.preferences.password.ManageSavedPasswordsPreferences"
android:title="@string/prefs_saved_passwords" />
<Preference
android:fragment="org.chromium.chrome.browser.preferences.AccessibilityPreferences"
android:title="@string/prefs_accessibility" />
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment