Commit a6c8b740 authored by Natalie Chouinard's avatar Natalie Chouinard Committed by Commit Bot

Migrate Developer Settings

Migrate the Developer Settings page from deprecated Framework
preferences to the Preferences Support Library.

This is the first fragment migrated that includes a ChromeBasePreference
so some base classes and methods have been duplicated here to support
simultaneous compatibility between Framework and Support Library
Preferences.

Bug: 966244
Change-Id: I2bd8919361207576231f99d799a5a7e95819d81e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1632557
Commit-Queue: Natalie Chouinard <chouinard@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#666356}
parent 46b49e8a
......@@ -1216,6 +1216,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/preferences/ChromeBaseCheckBoxPreference.java",
"java/src/org/chromium/chrome/browser/preferences/ChromeBaseListPreference.java",
"java/src/org/chromium/chrome/browser/preferences/ChromeBasePreference.java",
"java/src/org/chromium/chrome/browser/preferences/ChromeBasePreferenceCompat.java",
"java/src/org/chromium/chrome/browser/preferences/ChromeImageViewPreference.java",
"java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java",
"java/src/org/chromium/chrome/browser/preferences/ChromeSwitchPreference.java",
......@@ -1229,6 +1230,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/preferences/LocationSettings.java",
"java/src/org/chromium/chrome/browser/preferences/MainPreferences.java",
"java/src/org/chromium/chrome/browser/preferences/ManagedPreferenceDelegate.java",
"java/src/org/chromium/chrome/browser/preferences/ManagedPreferenceDelegateCompat.java",
"java/src/org/chromium/chrome/browser/preferences/ManagedPreferencesUtils.java",
"java/src/org/chromium/chrome/browser/preferences/ManageSyncPreferences.java",
"java/src/org/chromium/chrome/browser/preferences/NotificationsPreferences.java",
......@@ -1251,6 +1253,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/preferences/SyncedAccountPreference.java",
"java/src/org/chromium/chrome/browser/preferences/TextAndButtonPreference.java",
"java/src/org/chromium/chrome/browser/preferences/TextMessagePreference.java",
"java/src/org/chromium/chrome/browser/preferences/TextMessagePreferenceCompat.java",
"java/src/org/chromium/chrome/browser/preferences/TextScalePreference.java",
"java/src/org/chromium/chrome/browser/preferences/autofill/AndroidPaymentAppPreference.java",
"java/src/org/chromium/chrome/browser/preferences/autofill/AndroidPaymentAppsFragment.java",
......
......@@ -260,6 +260,11 @@
<item name="android:paddingEnd">@dimen/pref_list_padding_kitkat</item>
</style>
<style name="Theme.Chromium.PreferenceItemNoDividers">
<item name="allowDividerAbove">false</item>
<item name="allowDividerBelow">false</item>
</style>
<style name="PreferenceActionBarModern" parent="@style/Widget.AppCompat.Light.ActionBar.Solid">
<item name="titleTextStyle">@style/TextAppearance.BlackHeadline</item>
</style>
......
......@@ -3,15 +3,17 @@
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:orderingFromXml="true">
<Preference
<android.support.v7.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:orderingFromXml="true">
<android.support.v7.preference.Preference
android:fragment="org.chromium.chrome.browser.preferences.developer.TracingPreferences"
android:key="tracing"
android:title="Tracing"/>
<org.chromium.chrome.browser.preferences.TextMessagePreference
android:key="beta_stable_hint"
android:title="Hint: You can also enable Developer options on Beta/Stable channels by tapping the Chrome version in &quot;Settings > About Chrome&quot; multiple times."
android:enabled="false"
android:title="Tracing" />
<org.chromium.chrome.browser.preferences.TextMessagePreferenceCompat
style="@style/Theme.Chromium.PreferenceItemNoDividers"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</PreferenceScreen>
android:layout_height="wrap_content"
android:enabled="false"
android:key="beta_stable_hint"
android:title="Hint: You can also enable Developer options on Beta/Stable channels by tapping the Chrome version in &quot;Settings > About Chrome&quot; multiple times." />
</android.support.v7.preference.PreferenceScreen>
// Copyright 2019 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.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;
import android.util.AttributeSet;
import android.widget.TextView;
import org.chromium.chrome.R;
/**
* 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.
* 3. This preference can set an icon color in XML through app:iconTint. Note that if a
* ColorStateList is set, only the default color will be used.
*
* TODO(crbug.com/967022): This class is analogous to {@link ChromeBasePreference}, but extends the
* Preference Support Library rather than the deprecated Framework preferences. Once all {@link
* ChromeBasePreference}s have been migrated to the Support Library, {@link ChromeBasePreference}
* will be removed in favor of {@link ChromeBasePreferenceCompat}.
*/
public class ChromeBasePreferenceCompat extends Preference {
private ColorStateList mIconTint;
private ManagedPreferenceDelegateCompat mManagedPrefDelegate;
/**
* Constructor for use in Java.
*/
public ChromeBasePreferenceCompat(Context context) {
this(context, null);
}
/**
* Constructor for inflating from XML.
*/
public ChromeBasePreferenceCompat(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ChromeBasePreference);
mIconTint = a.getColorStateList(R.styleable.ChromeBasePreference_iconTint);
a.recycle();
}
/**
* Sets the ManagedPreferenceDelegate which will determine whether this preference is managed.
*/
public void setManagedPreferenceDelegate(ManagedPreferenceDelegateCompat delegate) {
mManagedPrefDelegate = delegate;
ManagedPreferencesUtils.initPreference(mManagedPrefDelegate, this);
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
((TextView) holder.findViewById(android.R.id.title)).setSingleLine(false);
Drawable icon = getIcon();
if (icon != null && mIconTint != null) {
icon.setColorFilter(mIconTint.getDefaultColor(), PorterDuff.Mode.SRC_IN);
}
ManagedPreferencesUtils.onBindViewToPreference(mManagedPrefDelegate, this, holder.itemView);
}
@Override
protected void onClick() {
if (ManagedPreferencesUtils.onClickPreference(mManagedPrefDelegate, this)) return;
super.onClick();
}
}
// Copyright 2019 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.support.v7.preference.Preference;
/**
* A delegate that determines whether a Preference is managed by enterprise policy. This is used
* in various Preference subclasses (e.g. ChromeSwitchPreference) to determine whether to show
* an enterprise icon next to the Preference and whether to disable clicks on the Preference.
*
* An implementation of this delegate should override isPreferenceControlledByPolicy() and,
* optionally, isPreferenceClickDisabledByPolicy(). Example:
*
* class RocketManagedPreferenceDelegate extends ManagedPreferenceDelegate {
* @Override
* public boolean isPreferenceControlledByPolicy(Preference preference) {
* if ("enable_rockets".equals(preference.getKey())) {
* return RocketUtils.isEnableRocketsManaged();
* }
* return false;
* }
* }
*
* ChromeSwitchPreference enableRocketsPref = ...;
* enableRocketsPref.setManagedPreferenceDelegate(new RocketManagedPreferenceDelegate());
*
* TODO(crbug.com/967022): This class is analogous to {@link ManagedPreferenceDelegate}, but is
* implemented for Support Library preferences rather than the deprecated Framework preferences.
* Once all managed preferences have been migrated to the Support Library, {@link
* ManagedPreferenceDelegate} will be removed in favor of this class.
*/
public interface ManagedPreferenceDelegateCompat {
/**
* Returns whether the given Preference is controlled by an enterprise policy.
* @param preference the {@link Preference} under consideration.
* @return whether the given Preference is controlled by an enterprise policy.
*/
boolean isPreferenceControlledByPolicy(Preference preference);
/**
* Returns whether the given Preference is controlled by the supervised user's custodian.
* @param preference the {@link Preference} under consideration.
* @return whether the given Preference is controlled by the supervised user's custodian.
*/
default boolean isPreferenceControlledByCustodian(Preference preference) {
return false;
}
/**
* Returns whether clicking on the given Preference is disabled due to a policy. The default
* implementation just returns whether the preference is not modifiable by the user.
* However, some preferences that are controlled by policy may still be clicked to show an
* informational subscreen, in which case this method needs a custom implementation.
*/
// TODO(bauerb): Rename to isPreferenceClickDisabled.
default boolean isPreferenceClickDisabledByPolicy(Preference preference) {
return isPreferenceControlledByPolicy(preference)
|| isPreferenceControlledByCustodian(preference);
}
}
\ No newline at end of file
......@@ -103,6 +103,10 @@ public class ManagedPreferencesUtils {
*
* This should be called once, before the preference is displayed.
*
* TODO(crbug.com/967022): Remove this method once all fragments are migrated to the Support
* Library in favor of {@link #initPreference(ManagedPreferenceDelegateCompat,
* android.support.v7.preference.Preference)}.
*
* @param delegate The delegate that controls whether the preference is managed. May be null,
* then this method does nothing.
* @param preference The Preference that is being initialized
......@@ -126,6 +130,41 @@ public class ManagedPreferencesUtils {
}
}
/**
* Initializes the Preference based on the state of any policies that may affect it,
* e.g. by showing a managed icon or disabling clicks on the preference. If |preference| is an
* instance of ChromeImageViewPreference, the icon is not set since the ImageView widget will
* display the managed icons.
*
* This should be called once, before the preference is displayed.
*
* @param delegate The delegate that controls whether the preference is managed. May be null,
* then this method does nothing.
* @param preference The Preference that is being initialized
*/
public static void initPreference(@Nullable ManagedPreferenceDelegateCompat delegate,
android.support.v7.preference.Preference preference) {
if (delegate == null) return;
// TODO(chouinard): A compat version of ChromeImageViewPreference hasn't been created yet.
// Once it is, uncomment this section.
/*
if (!(preference instanceof ChromeImageViewPreference)) {
preference.setIcon(getManagedIconDrawable(delegate, preference));
}
*/
if (delegate.isPreferenceClickDisabledByPolicy(preference)) {
// Disable the views and prevent the Preference from mucking with the enabled state.
preference.setShouldDisableView(false);
// Prevent default click behavior.
preference.setFragment(null);
preference.setIntent(null);
preference.setOnPreferenceClickListener(null);
}
}
/**
* Disables the Preference's views if the preference is not clickable.
*
......@@ -134,6 +173,10 @@ public class ManagedPreferencesUtils {
*
* This should be called from the Preference's onBindView() method.
*
* TODO(crbug.com/967022): Remove this method once all fragments are migrated to the Support
* Library in favor of {@link #onBindViewToPreference(ManagedPreferenceDelegateCompat,
* android.support.v7.preference.Preference, View)}.
*
* @param delegate The delegate that controls whether the preference is managed. May be null,
* then this method does nothing.
* @param preference The Preference that owns the view
......@@ -158,6 +201,38 @@ public class ManagedPreferencesUtils {
}
}
/**
* Disables the Preference's views if the preference is not clickable.
*
* Note: this disables the View instead of disabling the Preference, so that the Preference
* still receives click events, which will trigger a "Managed by your administrator" toast.
*
* This should be called from the Preference's onBindView() method.
*
* @param delegate The delegate that controls whether the preference is managed. May be null,
* then this method does nothing.
* @param preference The Preference that owns the view
* @param view The View that was bound to the Preference
*/
public static void onBindViewToPreference(@Nullable ManagedPreferenceDelegateCompat delegate,
android.support.v7.preference.Preference preference, View view) {
if (delegate == null) return;
if (delegate.isPreferenceClickDisabledByPolicy(preference)) {
ViewUtils.setEnabledRecursive(view, false);
}
// Append managed information to summary if necessary.
TextView summaryView = view.findViewById(android.R.id.summary);
CharSequence summary =
ManagedPreferencesUtils.getSummaryWithManagedInfo(delegate, preference,
summaryView.getVisibility() == View.VISIBLE ? summaryView.getText() : null);
if (!TextUtils.isEmpty(summary)) {
summaryView.setText(summary);
summaryView.setVisibility(View.VISIBLE);
}
}
/**
* Calls onBindViewToPreference() above. Then, if the ChromeImageViewPreference is managed, the
* widget ImageView is set to the appropriate managed icon, and its onClick listener is set to
......@@ -197,6 +272,10 @@ public class ManagedPreferencesUtils {
*
* This should be called from the Preference's onClick() method.
*
* TODO(crbug.com/967022): Remove this method once all fragments are migrated to the Support
* Library in favor of {@link #onClickPreference(ManagedPreferenceDelegateCompat,
* android.support.v7.preference.Preference)}.
*
* @param delegate The delegate that controls whether the preference is managed. May be null,
* then this method does nothing and returns false.
* @param preference The Preference that was clicked.
......@@ -222,6 +301,39 @@ public class ManagedPreferencesUtils {
}
/**
* Intercepts the click event if the given Preference is managed and shows a toast in that case.
*
* This should be called from the Preference's onClick() method.
*
* @param delegate The delegate that controls whether the preference is managed. May be null,
* then this method does nothing and returns false.
* @param preference The Preference that was clicked.
* @return true if the click event was handled by this helper and shouldn't be further
* propagated; false otherwise.
*/
public static boolean onClickPreference(@Nullable ManagedPreferenceDelegateCompat delegate,
android.support.v7.preference.Preference preference) {
if (delegate == null || !delegate.isPreferenceClickDisabledByPolicy(preference)) {
return false;
}
if (delegate.isPreferenceControlledByPolicy(preference)) {
showManagedByAdministratorToast(preference.getContext());
} else if (delegate.isPreferenceControlledByCustodian(preference)) {
showManagedByParentToast(preference.getContext());
} else {
// If the preference is disabled, it should be either because it's managed by enterprise
// policy or by the custodian.
assert false;
}
return true;
}
/**
* TODO(crbug.com/967022): Remove this method once all fragments are migrated to the Support
* Library in favor of {@link #getSummaryWithManagedInfo(ManagedPreferenceDelegateCompat,
* android.support.v7.preference.Preference, CharSequence)}.
*
* @param delegate The {@link ManagedPreferenceDelegate} that controls whether the preference is
* managed.
* @param preference The {@link Preference} that the summary should be used for.
......@@ -246,6 +358,33 @@ public class ManagedPreferencesUtils {
return String.format(Locale.getDefault(), "%s\n%s", summary, extraSummary);
}
/**
* @param delegate The {@link ManagedPreferenceDelegateCompat} that controls whether the
* preference is
* managed.
* @param preference The {@link android.support.v7.preference.Preference} that the summary
* should be used for.
* @param summary The original summary without the managed information.
* @return The summary appended with information about whether the specified preference is
* managed.
*/
private static CharSequence getSummaryWithManagedInfo(
@Nullable ManagedPreferenceDelegateCompat delegate,
android.support.v7.preference.Preference preference, @Nullable CharSequence summary) {
if (delegate == null) return summary;
String extraSummary = null;
if (delegate.isPreferenceControlledByPolicy(preference)) {
extraSummary = preference.getContext().getString(R.string.managed_by_your_organization);
} else if (delegate.isPreferenceControlledByCustodian(preference)) {
extraSummary = preference.getContext().getString(getManagedByParentStringRes());
}
if (TextUtils.isEmpty(extraSummary)) return summary;
if (TextUtils.isEmpty(summary)) return extraSummary;
return String.format(Locale.getDefault(), "%s\n%s", summary, extraSummary);
}
private static @StringRes int getManagedByParentStringRes() {
boolean singleParentIsManager =
PrefServiceBridge.getInstance().getSupervisedUserSecondCustodianName().isEmpty();
......
// Copyright 2019 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.support.v7.preference.PreferenceViewHolder;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
/**
* A preference that displays informational text.
*
* TODO(crbug.com/967022): This class is analogous to {@link TextMessagePreference}, but extends
* {@link ChromeBasePreferenceCompat} rather than {@link ChromeBasePreference}. Once all {@link
* TextMessagePreference}-containing fragments have been migrated to the Support Library, remove
* {@link TextMessagePreference} in favor of this class.
*/
public class TextMessagePreferenceCompat extends ChromeBasePreferenceCompat {
/**
* Constructor for inflating from XML.
*/
public TextMessagePreferenceCompat(Context context, AttributeSet attrs) {
super(context, attrs);
setSelectable(false);
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
TextView titleView = (TextView) holder.findViewById(android.R.id.title);
if (!TextUtils.isEmpty(getTitle())) {
titleView.setVisibility(View.VISIBLE);
titleView.setSingleLine(false);
titleView.setMaxLines(Integer.MAX_VALUE);
titleView.setMovementMethod(LinkMovementMethod.getInstance());
} else {
titleView.setVisibility(View.GONE);
}
TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
// No need to manually toggle visibility for summary - it is done in super.onBindView.
summaryView.setMovementMethod(LinkMovementMethod.getInstance());
}
}
......@@ -5,7 +5,7 @@
package org.chromium.chrome.browser.preferences.developer;
import android.os.Bundle;
import android.preference.PreferenceFragment;
import android.support.v7.preference.PreferenceFragmentCompat;
import org.chromium.base.ContextUtils;
import org.chromium.chrome.R;
......@@ -17,7 +17,7 @@ import org.chromium.components.version_info.VersionConstants;
/**
* Settings fragment containing preferences aimed at Chrome and web developers.
*/
public class DeveloperPreferences extends PreferenceFragment {
public class DeveloperPreferences extends PreferenceFragmentCompat {
private static final String UI_PREF_BETA_STABLE_HINT = "beta_stable_hint";
private static final String PREF_DEVELOPER_ENABLED = "developer";
......@@ -39,8 +39,7 @@ public class DeveloperPreferences extends PreferenceFragment {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
public void onCreatePreferences(Bundle savedInstanceState, String s) {
getActivity().setTitle(MSG_DEVELOPER_OPTIONS_TITLE);
PreferenceUtils.addPreferencesFromResource(this, R.xml.developer_preferences);
......
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