Merge "Cleanup of the PackageInstaller API - Settings" into nyc-dev
diff --git a/res/layout/condition_card.xml b/res/layout/condition_card.xml
index c1224b5..4ddb084 100644
--- a/res/layout/condition_card.xml
+++ b/res/layout/condition_card.xml
@@ -104,7 +104,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="0dp"
- android:alpha=".7"
+ android:alpha=".8"
android:textColor="?android:attr/textColorPrimaryInverse"
style="?android:attr/buttonBarButtonStyle" />
@@ -112,7 +112,7 @@
android:id="@+id/second_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:alpha=".7"
+ android:alpha=".8"
android:textColor="?android:attr/textColorPrimaryInverse"
style="?android:attr/buttonBarButtonStyle" />
diff --git a/res/layout/confirm_lock_password_base.xml b/res/layout/confirm_lock_password_base.xml
index 536617d..8e9a87b 100644
--- a/res/layout/confirm_lock_password_base.xml
+++ b/res/layout/confirm_lock_password_base.xml
@@ -59,15 +59,9 @@
android:layout_marginEnd="16dp"
android:layout_marginTop="16dp"/>
- <TextView
- android:id="@+id/strongAuthRequiredText"
- style="@style/TextAppearance.ConfirmDeviceCredentialsStrongAuthRequiredText"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:gravity="center"/>
+ <View android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
<EditText
android:id="@+id/password_entry"
diff --git a/res/layout/confirm_lock_pattern_base.xml b/res/layout/confirm_lock_pattern_base.xml
index 117c499..7c960a2 100644
--- a/res/layout/confirm_lock_pattern_base.xml
+++ b/res/layout/confirm_lock_pattern_base.xml
@@ -59,15 +59,9 @@
android:layout_marginEnd="16dp"
android:layout_marginTop="16dp"/>
- <TextView
- android:id="@+id/strongAuthRequiredText"
- style="@style/TextAppearance.ConfirmDeviceCredentialsStrongAuthRequiredText"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="0.5"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:gravity="center"/>
+ <View android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="0.5"/>
<com.android.internal.widget.LockPatternView
android:id="@+id/lockPattern"
diff --git a/res/layout/locale_drag_cell.xml b/res/layout/locale_drag_cell.xml
index 522f284..db9454a 100644
--- a/res/layout/locale_drag_cell.xml
+++ b/res/layout/locale_drag_cell.xml
@@ -68,6 +68,7 @@
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:layout_gravity="center_vertical"
android:src="@drawable/drag_handle"
+ android:importantForAccessibility="no"
android:layout_alignParentEnd="true"
android:layout_alignTop="@id/checkbox"
android:layout_alignBottom="@id/checkbox"/>
diff --git a/res/values/ids.xml b/res/values/ids.xml
index f9334e4..dcf279a 100644
--- a/res/values/ids.xml
+++ b/res/values/ids.xml
@@ -24,4 +24,11 @@
<item type="id" name="lock_password" />
<item type="id" name="encrypt_dont_require_password" />
+
+ <!-- Used for custom accessibility actions in the Drag-and-Drop locale list -->
+ <item type="id" name="action_drag_move_up" />
+ <item type="id" name="action_drag_move_down" />
+ <item type="id" name="action_drag_move_top" />
+ <item type="id" name="action_drag_move_bottom" />
+ <item type="id" name="action_drag_remove" />
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 35d92f2..99404e1 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -530,6 +530,17 @@
<!-- This text shows in the language picker when the system is not translated into that languages [CHAR LIMIT=80] -->
<string name="locale_not_translated">May not be available in some apps</string>
+ <!-- Label for an accessibility action that moves a language up in the ordered language list [CHAR LIMIT=20] -->
+ <string name="action_drag_label_move_up">Move up</string>
+ <!-- Label for an accessibility action that moves a language down in the ordered language list [CHAR LIMIT=20] -->
+ <string name="action_drag_label_move_down">Move down</string>
+ <!-- Label for an accessibility action that moves a language to the top of the ordered language list [CHAR LIMIT=20] -->
+ <string name="action_drag_label_move_top">Move to top</string>
+ <!-- Label for an accessibility action that moves a language to the bottom of the ordered language list [CHAR LIMIT=20] -->
+ <string name="action_drag_label_move_bottom">Move to bottom</string>
+ <!-- Label for an accessibility action that removes the currently selected language from the the ordered language list [CHAR LIMIT=20] -->
+ <string name="action_drag_label_remove">Remove language</string>
+
<!-- The title of the dialog to pick an activity. This is shown when there are multiple activities that can do a particular action. For example, suppose you click on the "Share" menu item in the Browser. Since you can share the webpage URL via many communication methods, this dialog would come up with choices like "Email", "IM", etc. This is a generic message, and the previous example is a single possible scenario (so please don't assume it's for the browser or anything :) ). -->
<string name="activity_picker_label">Choose activity</string>
<!-- Do not translate. -->
@@ -735,6 +746,10 @@
<string name="security_settings_fingerprint_enroll_introduction_cancel">Cancel</string>
<!-- Button text to continue to the next screen from the introduction [CHAR LIMIT=22] -->
<string name="security_settings_fingerprint_enroll_introduction_continue">Continue</string>
+ <!-- Button text to cancel enrollment from the introduction (this string variant is used while in setup wizard) [CHAR LIMIT=22] -->
+ <string name="security_settings_fingerprint_enroll_introduction_cancel_setup">Skip</string>
+ <!-- Button text to continue to the next screen from the introduction (this string variant is used while in setup wizard) [CHAR LIMIT=22] -->
+ <string name="security_settings_fingerprint_enroll_introduction_continue_setup">Add fingerprint</string>
<!-- Dialog text shown when the user tries to skip setting up a screen lock, warning them of potential consequences of not doing so, including loss of factory reset protection. (tablet) [CHAR LIMIT=NONE] -->
<string name="lock_screen_intro_skip_dialog_text_frp" product="tablet">Device protection features will not be activated. You won\u2019t be able to prevent others from using this tablet if it\u2019s lost, stolen or wiped.</string>
<!-- Dialog text shown when the user tries to skip setting up a screen lock, warning them of potential consequences of not doing so, including loss of factory reset protection. (device) [CHAR LIMIT=NONE] -->
@@ -2138,7 +2153,7 @@
<!-- Dream settings screen, caption for when dreams are disabled -->
<string name="screensaver_settings_disabled_prompt">To control what happens when the phone is docked and/or sleeping, turn screen saver on.</string>
<!-- Dream settings screen, action label, when to dream -->
- <string name="screensaver_settings_when_to_dream">When to turn screen saver on</string>
+ <string name="screensaver_settings_when_to_dream">When to start screen saver</string>
<!-- Dream settings screen, button label to start dreaming -->
<string name="screensaver_settings_dream_start">Start now</string>
<!-- Dream settings screen, button label for settings for a specific screensaver -->
@@ -2816,6 +2831,8 @@
<string name="tether_settings_title_usb_bluetooth">Tethering</string>
<!-- Tethering controls, item title to go into the tethering settings when USB, Bluetooth and Wifi tethering are available [CHAR LIMIT=25]-->
<string name="tether_settings_title_all">Tethering & portable hotspot</string>
+ <!-- Tethering controls, footer note displayed when tethering is disabled because Data Saver mode is on [CHAR LIMIT=none]-->
+ <string name="tether_settings_disabled_on_data_saver">"Can\u2019t tether or use portable hotspots while Data Saver is on"</string>
<!-- USB Tethering options -->
<string name="usb_title">USB</string>
@@ -3073,13 +3090,20 @@
<!-- Message to be used to explain the user that he needs to enter his work password to continue a
particular operation. [CHAR LIMIT=70]-->
<string name="lockpassword_confirm_your_password_generic_profile">Enter your work password to continue</string>
- <!-- An explanation text that the pattern needs to be solved since the device has just been restarted. [CHAR LIMIT=80] -->
- <string name="lockpassword_strong_auth_required_reason_restart_pattern">Pattern required after device restarts</string>
- <!-- An explanation text that the pin needs to be entered since the device has just been restarted. [CHAR LIMIT=80] -->
- <string name="lockpassword_strong_auth_required_reason_restart_pin">PIN required after device restarts</string>
+ <!-- An explanation text that the pattern needs to be solved since the device has just been restarted. [CHAR LIMIT=80] -->
+ <string name="lockpassword_strong_auth_required_reason_restart_device_pattern">Use your device pattern to continue. It\'s required after the device restarts.</string>
+ <!-- An explanation text that the pin needs to be entered since the device has just been restarted. [CHAR LIMIT=80] -->
+ <string name="lockpassword_strong_auth_required_reason_restart_device_pin">Enter your device PIN to continue. It\'s required after the device restarts.</string>
<!-- An explanation text that the password needs to be entered since the device has just been restarted. [CHAR LIMIT=80] -->
- <string name="lockpassword_strong_auth_required_reason_restart_password">Password required after device restarts</string>
+ <string name="lockpassword_strong_auth_required_reason_restart_device_password">Enter your device password to continue. It\'s required after the device restarts.</string>
+
+ <!-- An explanation text that the pattern needs to be solved since the device has just been restarted. [CHAR LIMIT=80] -->
+ <string name="lockpassword_strong_auth_required_reason_restart_work_pattern">Use your work pattern to continue. It\'s required after the device restarts.</string>
+ <!-- An explanation text that the pin needs to be entered since the device has just been restarted. [CHAR LIMIT=80] -->
+ <string name="lockpassword_strong_auth_required_reason_restart_work_pin">Enter your work PIN to continue. It\'s required after the device restarts.</string>
+ <!-- An explanation text that the password needs to be entered since the device has just been restarted. [CHAR LIMIT=80] -->
+ <string name="lockpassword_strong_auth_required_reason_restart_work_password">Enter your work password to continue. It\'s required after the device restarts.</string>
<!-- Security & location settings screen, change security method screen instruction if user
enters incorrect PIN [CHAR LIMIT=30] -->
@@ -5169,8 +5193,8 @@
<string name="vpn_save">Save</string>
<!-- Button label to connect to a VPN profile. [CHAR LIMIT=40] -->
<string name="vpn_connect">Connect</string>
- <!-- Button label to continue changing a VPN profile. [CHAR LIMIT=40] -->
- <string name="vpn_continue">Continue</string>
+ <!-- Button label to connect a VPN profile, replacing the current one. [CHAR LIMIT=40] -->
+ <string name="vpn_replace">Replace</string>
<!-- Dialog title to edit a VPN profile. [CHAR LIMIT=40] -->
<string name="vpn_edit">Edit VPN profile</string>
<!-- Button label to forget a VPN profile. [CHAR LIMIT=40] -->
@@ -5188,12 +5212,11 @@
<!-- Dialog message title to set another VPN app to be always-on [CHAR LIMIT=40] -->
<string name="vpn_replace_always_on_vpn_title">Replace existing VPN?</string>
<!-- Dialog message body to set another VPN app to be always-on [CHAR LIMIT=NONE] -->
- <string name="vpn_replace_always_on_vpn_message">You already have a VPN connected to this profile. If you connected to one, your existing VPN will be replaced.</string>
- <!-- Notification title when the user can't connect an always-on vpn [CHAR LIMIT=NONE] -->
- <string name="vpn_cant_connect_notification_title"><xliff:g id="vpn_name" example="OpenVPN">%1$s</xliff:g> can\'t connect</string>
- <!-- Notification subtitle when the user can't connect an always-on vpn [CHAR LIMIT=NONE] -->
- <string name="vpn_tap_for_vpn_settings">Tap for VPN settings</string>
-
+ <string name="vpn_replace_always_on_vpn_message">You\'re already connected to a VPN. If you connect to a different one, your existing VPN will be replaced.</string>
+ <!-- Dialog mesage title when the user can't connect an always-on vpn [CHAR LIMIT=NONE] -->
+ <string name="vpn_cant_connect_title"><xliff:g id="vpn_name" example="OpenVPN">%1$s</xliff:g> can\'t connect</string>
+ <!-- Dialog message subtitle when the user can't connect an always-on vpn [CHAR LIMIT=NONE] -->
+ <string name="vpn_cant_connect_message">This app doesn\'t support always-on VPN.</string>
<!-- Preference title for VPN settings. [CHAR LIMIT=40] -->
<string name="vpn_title">VPN</string>
<!-- Preference title to create a new VPN profile. [CHAR LIMIT=40] -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 0eb566c..45d2c11 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -357,11 +357,6 @@
<item name="android:textColor">@color/warning</item>
</style>
- <style name="TextAppearance.ConfirmDeviceCredentialsStrongAuthRequiredText"
- parent="android:TextAppearance.Material.Body1">
- <item name="android:textColor">?android:attr/textColorSecondary</item>
- </style>
-
<style name="TextAppearance.Small.SwitchBar">
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textStyle">normal</item>
diff --git a/res/xml/tether_prefs.xml b/res/xml/tether_prefs.xml
index 3a39d8a..22a4747 100644
--- a/res/xml/tether_prefs.xml
+++ b/res/xml/tether_prefs.xml
@@ -14,7 +14,8 @@
limitations under the License.
-->
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto">
<SwitchPreference
android:key="usb_tether_settings"
@@ -33,4 +34,9 @@
android:key="enable_bluetooth_tethering"
android:title="@string/bluetooth_tether_checkbox_text" />
+ <com.android.settings.DividerPreference
+ android:key="disabled_on_data_saver"
+ android:summary="@string/tether_settings_disabled_on_data_saver"
+ android:selectable="false"
+ settings:allowDividerAbove="true" />
</PreferenceScreen>
diff --git a/src/com/android/settings/ApnSettings.java b/src/com/android/settings/ApnSettings.java
index a6cd9b2..870c5aa 100644
--- a/src/com/android/settings/ApnSettings.java
+++ b/src/com/android/settings/ApnSettings.java
@@ -49,6 +49,7 @@
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
+import android.view.MotionEvent;
import android.widget.TextView;
import android.widget.Toast;
@@ -484,7 +485,11 @@
@Override
public Dialog onCreateDialog(int id) {
if (id == DIALOG_RESTORE_DEFAULTAPN) {
- ProgressDialog dialog = new ProgressDialog(getActivity());
+ ProgressDialog dialog = new ProgressDialog(getActivity()) {
+ public boolean onTouchEvent(MotionEvent event) {
+ return true;
+ }
+ };
dialog.setMessage(getResources().getString(R.string.restore_default_apn));
dialog.setCancelable(false);
return dialog;
diff --git a/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java b/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java
index 0572bcf..ae321e8 100644
--- a/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java
+++ b/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java
@@ -66,7 +66,7 @@
PACKAGE + ".ConfirmCredentials.showWhenLocked";
private FingerprintUiHelper mFingerprintHelper;
- private boolean mIsStrongAuthRequired;
+ protected boolean mIsStrongAuthRequired;
private boolean mAllowFpAuthentication;
protected Button mCancelButton;
protected ImageView mFingerprintIcon;
@@ -74,7 +74,6 @@
protected int mUserId;
protected LockPatternUtils mLockPatternUtils;
protected TextView mErrorTextView;
- protected TextView mStrongAuthRequiredTextView;
protected final Handler mHandler = new Handler();
@Override
@@ -97,11 +96,6 @@
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mCancelButton = (Button) view.findViewById(R.id.cancelButton);
- if (mStrongAuthRequiredTextView != null) {
- // INIVISIBLE instead of GONE because it also acts as a weighted spacer
- mStrongAuthRequiredTextView.setVisibility(
- mIsStrongAuthRequired ? View.VISIBLE : View.INVISIBLE);
- }
mFingerprintIcon = (ImageView) view.findViewById(R.id.fingerprintIcon);
mFingerprintHelper = new FingerprintUiHelper(
mFingerprintIcon,
diff --git a/src/com/android/settings/ConfirmLockPassword.java b/src/com/android/settings/ConfirmLockPassword.java
index a1bc3dd..61cf2ae 100644
--- a/src/com/android/settings/ConfirmLockPassword.java
+++ b/src/com/android/settings/ConfirmLockPassword.java
@@ -50,6 +50,18 @@
public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
+ // The index of the array is isStrongAuth << 2 + isProfile << 1 + isAlpha.
+ private static final int[] DETAIL_TEXTS = new int[] {
+ R.string.lockpassword_confirm_your_pin_generic,
+ R.string.lockpassword_confirm_your_password_generic,
+ R.string.lockpassword_confirm_your_pin_generic_profile,
+ R.string.lockpassword_confirm_your_password_generic_profile,
+ R.string.lockpassword_strong_auth_required_reason_restart_device_pin,
+ R.string.lockpassword_strong_auth_required_reason_restart_device_password,
+ R.string.lockpassword_strong_auth_required_reason_restart_work_pin,
+ R.string.lockpassword_strong_auth_required_reason_restart_work_password,
+ };
+
public static class InternalActivity extends ConfirmLockPassword {
}
@@ -124,18 +136,6 @@
|| DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_MANAGED == storedQuality;
- // Strong auth is required when the user is locked.
- // Currently a user does not get locked again until the device restarts. Show the
- // hint text as "device has just been restarted".
- mStrongAuthRequiredTextView = (TextView) view.findViewById(R.id.strongAuthRequiredText);
- if (mIsAlpha) {
- mStrongAuthRequiredTextView.setText(
- R.string.lockpassword_strong_auth_required_reason_restart_password);
- } else {
- mStrongAuthRequiredTextView.setText(
- R.string.lockpassword_strong_auth_required_reason_restart_pin);
- }
-
mImm = (InputMethodManager) getActivity().getSystemService(
Context.INPUT_METHOD_SERVICE);
@@ -186,13 +186,10 @@
private int getDefaultDetails() {
boolean isProfile = Utils.isManagedProfile(
UserManager.get(getActivity()), mEffectiveUserId);
- if (mIsAlpha) {
- return isProfile ? R.string.lockpassword_confirm_your_password_generic_profile
- : R.string.lockpassword_confirm_your_password_generic;
- } else {
- return isProfile ? R.string.lockpassword_confirm_your_pin_generic_profile
- : R.string.lockpassword_confirm_your_pin_generic;
- }
+ // Map boolean flags to an index by isStrongAuth << 2 + isProfile << 1 + isAlpha.
+ int index = ((mIsStrongAuthRequired ? 1 : 0) << 2) + ((isProfile ? 1 : 0 ) << 1)
+ + (mIsAlpha ? 1 : 0);
+ return DETAIL_TEXTS[index];
}
private int getErrorMessage() {
diff --git a/src/com/android/settings/ConfirmLockPattern.java b/src/com/android/settings/ConfirmLockPattern.java
index 875fcb7..262cdc8 100644
--- a/src/com/android/settings/ConfirmLockPattern.java
+++ b/src/com/android/settings/ConfirmLockPattern.java
@@ -118,7 +118,6 @@
mHeaderTextView = (TextView) view.findViewById(R.id.headerText);
mLockPatternView = (LockPatternView) view.findViewById(R.id.lockPattern);
mDetailsTextView = (TextView) view.findViewById(R.id.detailsText);
- mStrongAuthRequiredTextView = (TextView) view.findViewById(R.id.strongAuthRequiredText);
mErrorTextView = (TextView) view.findViewById(R.id.errorText);
mLeftSpacerLandscape = view.findViewById(R.id.leftSpacer);
mRightSpacerLandscape = view.findViewById(R.id.rightSpacer);
@@ -176,13 +175,6 @@
getFragmentManager().beginTransaction().add(mCredentialCheckResultTracker,
FRAGMENT_TAG_CHECK_LOCK_RESULT).commit();
}
-
- // Strong auth is required when the user is locked.
- // Currently a user does not get locked again until the device restarts. Show the
- // hint text as "device has just been restarted".
- mStrongAuthRequiredTextView.setText(
- R.string.lockpassword_strong_auth_required_reason_restart_pattern);
-
return view;
}
@@ -237,6 +229,20 @@
mFingerprintIcon.setAlpha(0f);
}
+ private int getDefaultDetails() {
+ boolean isProfile = Utils.isManagedProfile(
+ UserManager.get(getActivity()), mEffectiveUserId);
+ if (isProfile) {
+ return mIsStrongAuthRequired
+ ? R.string.lockpassword_strong_auth_required_reason_restart_work_pattern
+ : R.string.lockpassword_confirm_your_pattern_generic_profile;
+ } else {
+ return mIsStrongAuthRequired
+ ? R.string.lockpassword_strong_auth_required_reason_restart_device_pattern
+ : R.string.lockpassword_confirm_your_pattern_generic;
+ }
+ }
+
private Object[][] getActiveViews() {
ArrayList<ArrayList<Object>> result = new ArrayList<>();
result.add(new ArrayList<Object>(Collections.singletonList(mHeaderTextView)));
@@ -282,13 +288,8 @@
}
if (mDetailsText != null) {
mDetailsTextView.setText(mDetailsText);
- } else if (!Utils.isManagedProfile(
- UserManager.get(getActivity()), mEffectiveUserId)) {
- mDetailsTextView.setText(
- R.string.lockpassword_confirm_your_pattern_generic);
} else {
- mDetailsTextView.setText(
- R.string.lockpassword_confirm_your_pattern_generic_profile);
+ mDetailsTextView.setText(getDefaultDetails());
}
mErrorTextView.setText("");
if (isProfileChallenge()) {
diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java
index 1a3c850..a992897 100644
--- a/src/com/android/settings/SettingsPreferenceFragment.java
+++ b/src/com/android/settings/SettingsPreferenceFragment.java
@@ -399,6 +399,10 @@
}
}
+ protected int getCachedCount() {
+ return mPreferenceCache.size();
+ }
+
private void highlightPreference(String key) {
final int position = canUseListViewForHighLighting(key);
if (position >= 0) {
diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java
index fa9f545..86fb1d3 100644
--- a/src/com/android/settings/TetherSettings.java
+++ b/src/com/android/settings/TetherSettings.java
@@ -39,7 +39,9 @@
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.util.Log;
+
import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.settings.datausage.DataSaverBackend;
import com.android.settings.wifi.WifiApDialog;
import com.android.settings.wifi.WifiApEnabler;
import com.android.settingslib.TetherUtil;
@@ -56,12 +58,14 @@
* Displays preferences for Tethering.
*/
public class TetherSettings extends RestrictedSettingsFragment
- implements DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener {
+ implements DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener,
+ DataSaverBackend.Listener {
private static final String USB_TETHER_SETTINGS = "usb_tether_settings";
private static final String ENABLE_WIFI_AP = "enable_wifi_ap";
private static final String ENABLE_BLUETOOTH_TETHERING = "enable_bluetooth_tethering";
private static final String TETHER_CHOICE = "TETHER_TYPE";
+ private static final String DATA_SAVER_FOOTER = "disabled_on_data_saver";
private static final int DIALOG_AP_SETTINGS = 1;
@@ -110,6 +114,10 @@
private boolean mUnavailable;
+ private DataSaverBackend mDataSaverBackend;
+ private boolean mDataSaverEnabled;
+ private Preference mDataSaverFooter;
+
@Override
protected int getMetricsCategory() {
return MetricsEvent.TETHER;
@@ -125,6 +133,10 @@
addPreferencesFromResource(R.xml.tether_prefs);
+ mDataSaverBackend = new DataSaverBackend(getContext());
+ mDataSaverEnabled = mDataSaverBackend.isDataSaverEnabled();
+ mDataSaverFooter = findPreference(DATA_SAVER_FOOTER);
+
setIfOnlyAvailableForAdmins(true);
if (isUiRestricted()) {
mUnavailable = true;
@@ -145,6 +157,8 @@
mUsbTether = (SwitchPreference) findPreference(USB_TETHER_SETTINGS);
mBluetoothTether = (SwitchPreference) findPreference(ENABLE_BLUETOOTH_TETHERING);
+ mDataSaverBackend.addListener(this);
+
mCm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
@@ -161,7 +175,7 @@
}
if (wifiAvailable && !Utils.isMonkeyRunning()) {
- mWifiApEnabler = new WifiApEnabler(activity, mEnableWifiAp);
+ mWifiApEnabler = new WifiApEnabler(activity, mDataSaverBackend, mEnableWifiAp);
initWifiTethering();
} else {
getPreferenceScreen().removePreference(mEnableWifiAp);
@@ -178,6 +192,31 @@
mBluetoothTether.setChecked(false);
}
}
+ // Set initial state based on Data Saver mode.
+ onDataSaverChanged(mDataSaverBackend.isDataSaverEnabled());
+ }
+
+ @Override
+ public void onDestroy() {
+ mDataSaverBackend.remListener(this);
+ super.onDestroy();
+ }
+
+ @Override
+ public void onDataSaverChanged(boolean isDataSaving) {
+ mDataSaverEnabled = isDataSaving;
+ mEnableWifiAp.setEnabled(!mDataSaverEnabled);
+ mUsbTether.setEnabled(!mDataSaverEnabled);
+ mBluetoothTether.setEnabled(!mDataSaverEnabled);
+ mDataSaverFooter.setVisible(mDataSaverEnabled);
+ }
+
+ @Override
+ public void onWhitelistStatusChanged(int uid, boolean isWhitelisted) {
+ }
+
+ @Override
+ public void onBlacklistStatusChanged(int uid, boolean isBlacklisted) {
}
private void initWifiTethering() {
@@ -368,7 +407,7 @@
if (usbTethered) {
mUsbTether.setSummary(R.string.usb_tethering_active_subtext);
- mUsbTether.setEnabled(true);
+ mUsbTether.setEnabled(!mDataSaverEnabled);
mUsbTether.setChecked(true);
} else if (usbAvailable) {
if (usbError == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
@@ -376,7 +415,7 @@
} else {
mUsbTether.setSummary(R.string.usb_tethering_errored_subtext);
}
- mUsbTether.setEnabled(true);
+ mUsbTether.setEnabled(!mDataSaverEnabled);
mUsbTether.setChecked(false);
} else if (usbErrored) {
mUsbTether.setSummary(R.string.usb_tethering_errored_subtext);
@@ -418,7 +457,7 @@
if (btState == BluetoothAdapter.STATE_ON && bluetoothPan != null
&& bluetoothPan.isTetheringOn()) {
mBluetoothTether.setChecked(true);
- mBluetoothTether.setEnabled(true);
+ mBluetoothTether.setEnabled(!mDataSaverEnabled);
int bluetoothTethered = bluetoothPan.getConnectedDevices().size();
if (bluetoothTethered > 1) {
String summary = getString(
@@ -434,7 +473,7 @@
mBluetoothTether.setSummary(R.string.bluetooth_tethering_available_subtext);
}
} else {
- mBluetoothTether.setEnabled(true);
+ mBluetoothTether.setEnabled(!mDataSaverEnabled);
mBluetoothTether.setChecked(false);
mBluetoothTether.setSummary(R.string.bluetooth_tethering_off_subtext);
}
diff --git a/src/com/android/settings/TrustedCredentialsDialogBuilder.java b/src/com/android/settings/TrustedCredentialsDialogBuilder.java
index 22dc936..ed2ab09 100644
--- a/src/com/android/settings/TrustedCredentialsDialogBuilder.java
+++ b/src/com/android/settings/TrustedCredentialsDialogBuilder.java
@@ -20,6 +20,7 @@
import android.app.AlertDialog;
import android.app.admin.DevicePolicyManager;
import android.content.DialogInterface;
+import android.content.pm.UserInfo;
import android.net.http.SslCertificate;
import android.os.UserHandle;
import android.os.UserManager;
@@ -31,6 +32,7 @@
import android.widget.LinearLayout;
import android.widget.Spinner;
+import com.android.internal.widget.LockPatternUtils;
import com.android.settings.TrustedCredentialsSettings.CertHolder;
import java.security.cert.X509Certificate;
@@ -184,10 +186,26 @@
updateNegativeButton();
}
+ /**
+ * @return true if current user or parent user is guarded by screenlock
+ */
+ private boolean isUserSecure(int userId) {
+ final LockPatternUtils lockPatternUtils = new LockPatternUtils(mActivity);
+ if (lockPatternUtils.isSecure(userId)) {
+ return true;
+ }
+ UserInfo parentUser = mUserManager.getProfileParent(userId);
+ if (parentUser == null) {
+ return false;
+ }
+ return lockPatternUtils.isSecure(parentUser.id);
+ }
+
private void updatePositiveButton() {
final CertHolder certHolder = getCurrentCertInfo();
- mNeedsApproval = !certHolder.isSystemCert() &&
- !mDpm.isCaCertApproved(certHolder.getAlias(), certHolder.getUserId());
+ mNeedsApproval = !certHolder.isSystemCert()
+ && isUserSecure(certHolder.getUserId())
+ && !mDpm.isCaCertApproved(certHolder.getAlias(), certHolder.getUserId());
// The ok button is optional. User can still dismiss the dialog by other means.
// Display it only when trust button is not displayed, because we want users to
diff --git a/src/com/android/settings/applications/AppStatePowerBridge.java b/src/com/android/settings/applications/AppStatePowerBridge.java
index e25b9c9..16de600 100644
--- a/src/com/android/settings/applications/AppStatePowerBridge.java
+++ b/src/com/android/settings/applications/AppStatePowerBridge.java
@@ -56,7 +56,7 @@
}
public static final AppFilter FILTER_POWER_WHITELISTED = new CompoundFilter(
- ApplicationsState.FILTER_PERSONAL_WITHOUT_DISABLED_UNTIL_USED, new AppFilter() {
+ ApplicationsState.FILTER_WITHOUT_DISABLED_UNTIL_USED, new AppFilter() {
@Override
public void init() {
}
diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
index 9de3f7a..7936cb9 100644
--- a/src/com/android/settings/applications/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.icu.text.AlphabeticIndex;
import android.os.*;
@@ -138,7 +139,7 @@
// This is the string labels for the filter modes above, the order must be kept in sync.
public static final int[] FILTER_LABELS = new int[]{
R.string.high_power_filter_on, // High power whitelist, on
- R.string.filter_all_apps, // All apps label, but personal filter (for high power);
+ R.string.filter_all_apps, // Without disabled until used
R.string.filter_all_apps, // All apps
R.string.filter_enabled_apps, // Enabled
R.string.filter_apps_disabled, // Disabled
@@ -159,8 +160,8 @@
public static final AppFilter[] FILTERS = new AppFilter[]{
new CompoundFilter(AppStatePowerBridge.FILTER_POWER_WHITELISTED,
ApplicationsState.FILTER_ALL_ENABLED), // High power whitelist, on
- new CompoundFilter(ApplicationsState.FILTER_PERSONAL_WITHOUT_DISABLED_UNTIL_USED,
- ApplicationsState.FILTER_ALL_ENABLED), // All apps label, but personal filter
+ new CompoundFilter(ApplicationsState.FILTER_WITHOUT_DISABLED_UNTIL_USED,
+ ApplicationsState.FILTER_ALL_ENABLED), // Without disabled until used
ApplicationsState.FILTER_EVERYTHING, // All apps
ApplicationsState.FILTER_ALL_ENABLED, // Enabled
ApplicationsState.FILTER_DISABLED, // Disabled
@@ -901,9 +902,46 @@
// Don't have new list yet, but can continue using the old one.
return;
}
+ if (mFilterMode == FILTER_APPS_POWER_WHITELIST ||
+ mFilterMode == FILTER_APPS_POWER_WHITELIST_ALL) {
+ entries = removeDuplicateIgnoringUser(entries);
+ }
onRebuildComplete(entries);
}
+
+ static private boolean packageNameEquals(PackageItemInfo info1, PackageItemInfo info2) {
+ if (info1 == null || info2 == null) {
+ return false;
+ }
+ if (info1.packageName == null || info2.packageName == null) {
+ return false;
+ }
+ return info1.packageName.equals(info2.packageName);
+ }
+
+ private ArrayList<ApplicationsState.AppEntry> removeDuplicateIgnoringUser(
+ ArrayList<ApplicationsState.AppEntry> entries)
+ {
+ int size = entries.size();
+ // returnList will not have more entries than entries
+ ArrayList<ApplicationsState.AppEntry> returnEntries = new
+ ArrayList<ApplicationsState.AppEntry>(size);
+
+ // assume appinfo of same package but different users are grouped together
+ PackageItemInfo lastInfo = null;
+ for (int i = 0; i < size; i++) {
+ AppEntry appEntry = entries.get(i);
+ PackageItemInfo info = appEntry.info;
+ if (!packageNameEquals(lastInfo, appEntry.info)) {
+ returnEntries.add(appEntry);
+ }
+ lastInfo = info;
+ }
+ returnEntries.trimToSize();
+ return returnEntries;
+ }
+
@Override
public void onRebuildComplete(ArrayList<AppEntry> entries) {
mBaseEntries = entries;
diff --git a/src/com/android/settings/dashboard/SummaryLoader.java b/src/com/android/settings/dashboard/SummaryLoader.java
index 780d68e..814d1e4 100644
--- a/src/com/android/settings/dashboard/SummaryLoader.java
+++ b/src/com/android/settings/dashboard/SummaryLoader.java
@@ -94,15 +94,17 @@
});
}
+ /**
+ * Only call from the main thread.
+ */
public void setListening(boolean listening) {
- synchronized (mReceivers) {
- // Unregister listeners immediately.
- mListening = false;
- for (int i = 0; i < mReceivers.size(); i++) {
- mActivity.unregisterReceiver(mReceivers.get(i));
- }
- mReceivers.clear();
+ if (mListening == listening) return;
+ mListening = listening;
+ // Unregister listeners immediately.
+ for (int i = 0; i < mReceivers.size(); i++) {
+ mActivity.unregisterReceiver(mReceivers.get(i));
}
+ mReceivers.clear();
mWorker.obtainMessage(Worker.MSG_SET_LISTENING, listening ? 1 : 0, 0).sendToTarget();
}
@@ -148,22 +150,27 @@
* This ensures that the receivers are unregistered immediately, since most summary loader
* operations are asynchronous.
*/
- public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
- synchronized (mReceivers) {
- if (!mListening) {
- return;
+ public void registerReceiver(final BroadcastReceiver receiver, final IntentFilter filter) {
+ mActivity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (!mListening) {
+ return;
+ }
+ mReceivers.add(receiver);
+ mActivity.registerReceiver(receiver, filter);
}
- mReceivers.add(receiver);
- mActivity.registerReceiver(receiver, filter);
- }
+ });
}
private synchronized void setListeningW(boolean listening) {
- if (mListening == listening) return;
if (DEBUG) Log.d(TAG, "Listening " + listening);
- mListening = listening;
for (SummaryProvider p : mSummaryMap.keySet()) {
- p.setListening(listening);
+ try {
+ p.setListening(listening);
+ } catch (Exception e) {
+ Log.d(TAG, "Problem in setListening", e);
+ }
}
}
@@ -172,10 +179,6 @@
if (provider != null) {
if (DEBUG) Log.d(TAG, "Creating " + tile);
mSummaryMap.put(provider, tile.intent.getComponent());
- if (mListening) {
- // If we are somehow already listening, put the provider in that state.
- provider.setListening(true);
- }
}
}
diff --git a/src/com/android/settings/datausage/DataUsageSummary.java b/src/com/android/settings/datausage/DataUsageSummary.java
index 3195641..bd6ae5c 100644
--- a/src/com/android/settings/datausage/DataUsageSummary.java
+++ b/src/com/android/settings/datausage/DataUsageSummary.java
@@ -25,6 +25,7 @@
import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemProperties;
+import android.os.UserManager;
import android.provider.SearchIndexableResource;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
@@ -116,7 +117,9 @@
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- inflater.inflate(R.menu.data_usage, menu);
+ if (UserManager.get(getContext()).isAdminUser()) {
+ inflater.inflate(R.menu.data_usage, menu);
+ }
super.onCreateOptionsMenu(menu, inflater);
}
diff --git a/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java b/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java
index aa1a068..f816682 100644
--- a/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java
@@ -19,8 +19,6 @@
import android.content.Intent;
import android.content.res.Resources;
import android.os.UserHandle;
-import android.support.v14.preference.PreferenceFragment;
-import android.view.View;
import android.widget.Button;
import com.android.internal.logging.MetricsProto.MetricsEvent;
@@ -29,6 +27,8 @@
import com.android.settings.SetupChooseLockGeneric;
import com.android.settings.SetupWizardUtils;
import com.android.setupwizardlib.SetupWizardRecyclerLayout;
+import com.android.setupwizardlib.items.Item;
+import com.android.setupwizardlib.items.RecyclerItemAdapter;
import com.android.setupwizardlib.view.NavigationBar;
public class SetupFingerprintEnrollIntroduction extends FingerprintEnrollIntroduction
@@ -56,12 +56,22 @@
@Override
protected void initViews() {
+ final SetupWizardRecyclerLayout layout =
+ (SetupWizardRecyclerLayout) findViewById(R.id.setup_wizard_layout);
+ final RecyclerItemAdapter adapter = (RecyclerItemAdapter) layout.getAdapter();
+ final Item nextItem = (Item) adapter.findItemById(R.id.next_button);
+ nextItem.setTitle(
+ getText(R.string.security_settings_fingerprint_enroll_introduction_continue_setup));
+
+ final Item cancelItem = (Item) adapter.findItemById(R.id.cancel_button);
+ cancelItem.setTitle(
+ getText(R.string.security_settings_fingerprint_enroll_introduction_cancel_setup));
+
SetupWizardUtils.setImmersiveMode(this);
getNavigationBar().setNavigationBarListener(this);
Button nextButton = getNavigationBar().getNextButton();
nextButton.setText(null);
nextButton.setEnabled(false);
- SetupWizardRecyclerLayout layout = (SetupWizardRecyclerLayout) getSetupWizardLayout();
layout.setDividerInset(getResources().getDimensionPixelSize(
R.dimen.suw_items_icon_divider_inset));
}
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
index 79ce204..78f9833 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
@@ -368,7 +368,8 @@
}
addedSome = true;
mAppListGroup.addPreference(pref);
- if (mAppListGroup.getPreferenceCount() > (MAX_ITEMS_TO_LIST + 1)) {
+ if (mAppListGroup.getPreferenceCount() - getCachedCount()
+ > (MAX_ITEMS_TO_LIST + 1)) {
break;
}
}
@@ -391,8 +392,10 @@
stats.add(new BatterySipper(type, null, use));
use += 5;
}
- stats.add(new BatterySipper(DrainType.APP,
- new FakeUid(Process.FIRST_APPLICATION_UID), use));
+ for (int i = 0; i < 100; i++) {
+ stats.add(new BatterySipper(DrainType.APP,
+ new FakeUid(Process.FIRST_APPLICATION_UID + i), use));
+ }
stats.add(new BatterySipper(DrainType.APP,
new FakeUid(0), use));
diff --git a/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java b/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java
index fb0a4d5..61e3b9d 100644
--- a/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java
+++ b/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java
@@ -262,7 +262,6 @@
final TextServicesManager tsm = (TextServicesManager) getSystemService(
Context.TEXT_SERVICES_MANAGER_SERVICE);
if (!tsm.isSpellCheckerEnabled()) {
- spellChecker.setEnabled(false);
spellChecker.setSummary(R.string.switch_off_text);
} else {
final SpellCheckerInfo sci = tsm.getCurrentSpellChecker();
diff --git a/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment2.java b/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment2.java
index 6916242..c0d67bb 100644
--- a/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment2.java
+++ b/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment2.java
@@ -16,6 +16,7 @@
package com.android.settings.inputmethod;
+import android.annotation.Nullable;
import android.app.Activity;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
@@ -45,6 +46,7 @@
private int mInputDeviceId = -1;
private InputManager mIm;
private InputMethodInfo mImi;
+ @Nullable
private InputMethodSubtype mSubtype;
private KeyboardLayout[] mKeyboardLayouts;
private Map<Preference, KeyboardLayout> mPreferenceMap = new HashMap<>();
@@ -82,7 +84,7 @@
mImi = activity.getIntent().getParcelableExtra(EXTRA_INPUT_METHOD_INFO);
mSubtype = activity.getIntent().getParcelableExtra(EXTRA_INPUT_METHOD_SUBTYPE);
- if (mInputDeviceIdentifier == null || mImi == null || mSubtype == null) {
+ if (mInputDeviceIdentifier == null || mImi == null) {
activity.finish();
}
diff --git a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
index cf8db7a..07bcf6a 100644
--- a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
+++ b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
@@ -147,7 +147,7 @@
Preference pref = new Preference(getPrefContext(), null);
final InputMethodInfo imi = info.mImi;
final InputMethodSubtype imSubtype = info.mImSubtype;
- if (imi != null && imSubtype != null) {
+ if (imi != null) {
pref.setTitle(getDisplayName(getContext(), imi, imSubtype));
KeyboardLayout layout = info.mLayout;
if (layout != null) {
@@ -214,9 +214,9 @@
}
private void showKeyboardLayoutScreen(
- InputDeviceIdentifier inputDeviceIdentifier,
- InputMethodInfo imi,
- InputMethodSubtype imSubtype) {
+ @NonNull InputDeviceIdentifier inputDeviceIdentifier,
+ @NonNull InputMethodInfo imi,
+ @Nullable InputMethodSubtype imSubtype) {
final Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClass(getActivity(), Settings.KeyboardLayoutPickerActivity.class);
intent.putExtra(KeyboardLayoutPickerFragment2.EXTRA_INPUT_DEVICE_IDENTIFIER,
@@ -272,12 +272,15 @@
};
@NonNull
- static String getDisplayName(
+ static CharSequence getDisplayName(
@NonNull Context context, @NonNull InputMethodInfo imi,
- @NonNull InputMethodSubtype imSubtype) {
+ @Nullable InputMethodSubtype imSubtype) {
+ final CharSequence imeName = imi.loadLabel(context.getPackageManager());
+ if (imSubtype == null) {
+ return imeName;
+ }
final CharSequence imSubtypeName = imSubtype.getDisplayName(
context, imi.getPackageName(), imi.getServiceInfo().applicationInfo);
- final CharSequence imeName = imi.loadLabel(context.getPackageManager());
return String.format(
context.getString(R.string.physical_device_title), imSubtypeName, imeName);
}
@@ -330,8 +333,21 @@
final InputManager im = getContext().getSystemService(InputManager.class);
if (imm != null && im != null) {
for (InputMethodInfo imi : imm.getEnabledInputMethodList()) {
- for (InputMethodSubtype subtype : imm.getEnabledInputMethodSubtypeList(
- imi, true /* allowsImplicitlySelectedSubtypes */)) {
+ final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(
+ imi, true /* allowsImplicitlySelectedSubtypes */);
+ if (subtypes.isEmpty()) {
+ // Here we use null to indicate that this IME has no subtype.
+ final InputMethodSubtype nullSubtype = null;
+ final KeyboardLayout layout = im.getKeyboardLayoutForInputDevice(
+ deviceInfo.mDeviceIdentifier, imi, nullSubtype);
+ keyboardInfoList.add(new Keyboards.KeyboardInfo(imi, nullSubtype, layout));
+ continue;
+ }
+
+ // If the IME supports subtypes, we pick up "keyboard" subtypes only.
+ final int N = subtypes.size();
+ for (int i = 0; i < N; ++i) {
+ final InputMethodSubtype subtype = subtypes.get(i);
if (!IM_SUBTYPE_MODE_KEYBOARD.equalsIgnoreCase(subtype.getMode())) {
continue;
}
@@ -421,14 +437,14 @@
public static final class KeyboardInfo {
@NonNull
public final InputMethodInfo mImi;
- @NonNull
+ @Nullable
public final InputMethodSubtype mImSubtype;
@NonNull
public final KeyboardLayout mLayout;
public KeyboardInfo(
@NonNull final InputMethodInfo imi,
- @NonNull final InputMethodSubtype imSubtype,
+ @Nullable final InputMethodSubtype imSubtype,
@NonNull final KeyboardLayout layout) {
mImi = imi;
mImSubtype = imSubtype;
diff --git a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
index 2ce4e72..bc17814 100644
--- a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
+++ b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
@@ -38,7 +38,6 @@
import java.text.NumberFormat;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -152,8 +151,9 @@
public void onBindViewHolder(final CustomViewHolder holder, int i) {
final LocaleStore.LocaleInfo feedItem = mFeedItemList.get(i);
final LocaleDragCell dragCell = holder.getLocaleDragCell();
- String label = feedItem.getFullNameNative();
- dragCell.setLabel(label);
+ final String label = feedItem.getFullNameNative();
+ final String description = feedItem.getFullNameInUiLanguage();
+ dragCell.setLabelAndDescription(label, description);
dragCell.setLocalized(feedItem.isTranslated());
dragCell.setMiniLabel(mNumberFormatter.format(i + 1));
dragCell.setShowCheckbox(mRemoveMode);
@@ -166,7 +166,7 @@
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
LocaleStore.LocaleInfo feedItem =
- (LocaleStore.LocaleInfo) holder.getLocaleDragCell().getTag();
+ (LocaleStore.LocaleInfo) dragCell.getTag();
feedItem.setChecked(isChecked);
}
});
@@ -183,12 +183,14 @@
return itemCount;
}
- private void onItemMove(int fromPosition, int toPosition) {
+ void onItemMove(int fromPosition, int toPosition) {
if (fromPosition >= 0 && toPosition >= 0) {
- Collections.swap(mFeedItemList, fromPosition, toPosition);
+ final LocaleStore.LocaleInfo saved = mFeedItemList.get(fromPosition);
+ mFeedItemList.remove(fromPosition);
+ mFeedItemList.add(toPosition, saved);
} else {
// TODO: It looks like sometimes the RecycleView tries to swap item -1
- // Investigate and file a bug.
+ // I did not see it in a while, but if it happens, investigate and file a bug.
Log.e(TAG, String.format(Locale.US,
"Negative position in onItemMove %d -> %d", fromPosition, toPosition));
}
@@ -206,6 +208,23 @@
}
}
+ boolean isRemoveMode() {
+ return mRemoveMode;
+ }
+
+ void removeItem(int position) {
+ int itemCount = mFeedItemList.size();
+ if (itemCount <= 1) {
+ return;
+ }
+ if (position < 0 || position >= itemCount) {
+ return;
+ }
+ mFeedItemList.remove(position);
+ notifyDataSetChanged();
+ doTheUpdate();
+ }
+
void removeChecked() {
int itemCount = mFeedItemList.size();
for (int i = itemCount - 1; i >= 0; i--) {
diff --git a/src/com/android/settings/localepicker/LocaleDragCell.java b/src/com/android/settings/localepicker/LocaleDragCell.java
index b3ecfad..ea86189 100644
--- a/src/com/android/settings/localepicker/LocaleDragCell.java
+++ b/src/com/android/settings/localepicker/LocaleDragCell.java
@@ -87,9 +87,11 @@
invalidate();
}
- public void setLabel(String labelText) {
+ public void setLabelAndDescription(String labelText, String description) {
mLabel.setText(labelText);
mCheckbox.setText(labelText);
+ mLabel.setContentDescription(description);
+ mCheckbox.setContentDescription(description);
invalidate();
}
diff --git a/src/com/android/settings/localepicker/LocaleLinearLayoutManager.java b/src/com/android/settings/localepicker/LocaleLinearLayoutManager.java
new file mode 100644
index 0000000..630f382
--- /dev/null
+++ b/src/com/android/settings/localepicker/LocaleLinearLayoutManager.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.localepicker;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+import com.android.settings.R;
+
+/**
+ * Add accessibility actions to the drag-and-drop locale list
+ *
+ * <p>Dragging is not supported neither by TalkBack or the accessibility
+ * framework at the moment. So we need custom actions to be able
+ * to change the order of the locales.</p>
+ *
+ * <p>Also, the remove functionality is difficult to discover and use
+ * with TalkBack only, so we are also adding a "remove" action.</p>
+ *
+ * <p>It only removes one locale at the time, but most users don't
+ * really add many locales "by mistake", so there is no real need
+ * to delete a lot of locales at once.</p>
+ */
+public class LocaleLinearLayoutManager extends LinearLayoutManager {
+ private final LocaleDragAndDropAdapter mAdapter;
+ private final Context mContext;
+
+ private final AccessibilityNodeInfoCompat.AccessibilityActionCompat mActionMoveUp;
+ private final AccessibilityNodeInfoCompat.AccessibilityActionCompat mActionMoveDown;
+ private final AccessibilityNodeInfoCompat.AccessibilityActionCompat mActionMoveTop;
+ private final AccessibilityNodeInfoCompat.AccessibilityActionCompat mActionMoveBottom;
+ private final AccessibilityNodeInfoCompat.AccessibilityActionCompat mActionRemove;
+
+ public LocaleLinearLayoutManager(Context context, LocaleDragAndDropAdapter adapter) {
+ super(context);
+ this.mContext = context;
+ this.mAdapter = adapter;
+
+ this.mActionMoveUp = new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+ R.id.action_drag_move_up,
+ mContext.getString(R.string.action_drag_label_move_up));
+ this.mActionMoveDown = new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+ R.id.action_drag_move_down,
+ mContext.getString(R.string.action_drag_label_move_down));
+ this.mActionMoveTop = new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+ R.id.action_drag_move_top,
+ mContext.getString(R.string.action_drag_label_move_top));
+ this.mActionMoveBottom = new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+ R.id.action_drag_move_bottom,
+ mContext.getString(R.string.action_drag_label_move_bottom));
+ this.mActionRemove = new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+ R.id.action_drag_remove,
+ mContext.getString(R.string.action_drag_label_remove));
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler,
+ RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) {
+
+ super.onInitializeAccessibilityNodeInfoForItem(recycler, state, host, info);
+
+ final int itemCount = this.getItemCount();
+ final int position = this.getPosition(host);
+ final LocaleDragCell dragCell = (LocaleDragCell) host;
+
+ // We want the description to be something not localizable, so that any TTS engine for
+ // any language can handle it. And we want the position to be part of it.
+ // So we use something like "2, French (France)"
+ final String description =
+ (position + 1) + ", " + dragCell.getCheckbox().getContentDescription();
+ info.setContentDescription(description);
+
+ if (mAdapter.isRemoveMode()) { // We don't move things around in remove mode
+ return;
+ }
+
+ // The order in which we add the actions is important for the circular selection menu.
+ // With the current order the "up" action will be (more or less) up, and "down" more
+ // or less down ("more or less" because we have 5 actions)
+ if (position > 0) { // it is not the first one
+ info.addAction(mActionMoveUp);
+ info.addAction(mActionMoveTop);
+ }
+ if (position + 1 < itemCount) { // it is not the last one
+ info.addAction(mActionMoveDown);
+ info.addAction(mActionMoveBottom);
+ }
+ if (itemCount > 1) {
+ info.addAction(mActionRemove);
+ }
+ }
+
+ @Override
+ public boolean performAccessibilityActionForItem(RecyclerView.Recycler recycler,
+ RecyclerView.State state, View host, int action, Bundle args) {
+
+ final int itemCount = this.getItemCount();
+ final int position = this.getPosition(host);
+
+ switch (action) {
+ case R.id.action_drag_move_up:
+ if (position > 0) {
+ mAdapter.onItemMove(position, position - 1);
+ return true;
+ }
+ return false;
+ case R.id.action_drag_move_down:
+ if (position + 1 < itemCount) {
+ mAdapter.onItemMove(position, position + 1);
+ return true;
+ }
+ return false;
+ case R.id.action_drag_move_top:
+ if (position != 0) {
+ mAdapter.onItemMove(position, 0);
+ return true;
+ }
+ return false;
+ case R.id.action_drag_move_bottom:
+ if (position != itemCount - 1) {
+ mAdapter.onItemMove(position, itemCount - 1);
+ return true;
+ }
+ return false;
+ case R.id.action_drag_remove:
+ if (itemCount > 1) {
+ mAdapter.removeItem(position);
+ return true;
+ }
+ return false;
+ default:
+ return super.performAccessibilityActionForItem(recycler, state, host, action, args);
+ }
+ }
+}
diff --git a/src/com/android/settings/localepicker/LocaleListEditor.java b/src/com/android/settings/localepicker/LocaleListEditor.java
index 3287d39..b95a590 100644
--- a/src/com/android/settings/localepicker/LocaleListEditor.java
+++ b/src/com/android/settings/localepicker/LocaleListEditor.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
-import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.LocaleList;
import android.view.LayoutInflater;
@@ -30,7 +29,6 @@
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.LinearLayout;
import com.android.internal.app.LocalePicker;
import com.android.internal.app.LocalePickerWithRegion;
@@ -76,9 +74,8 @@
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstState) {
- View result = super.onCreateView(inflater, container, savedInstState);
- LinearLayout ll = (LinearLayout) result;
- View myLayout = inflater.inflate(R.layout.locale_order_list, ll);
+ final View result = super.onCreateView(inflater, container, savedInstState);
+ final View myLayout = inflater.inflate(R.layout.locale_order_list, (ViewGroup) result);
getActivity().setTitle(R.string.pref_title_lang_selection);
@@ -236,7 +233,7 @@
private void configureDragAndDrop(View view) {
final RecyclerView list = (RecyclerView) view.findViewById(R.id.dragList);
- final LinearLayoutManager llm = new LinearLayoutManager(this.getContext());
+ final LocaleLinearLayoutManager llm = new LocaleLinearLayoutManager(getContext(), mAdapter);
llm.setAutoMeasureEnabled(true);
list.setLayoutManager(llm);
diff --git a/src/com/android/settings/notification/ZenRuleNameDialog.java b/src/com/android/settings/notification/ZenRuleNameDialog.java
index f69198b..c6ce23b 100644
--- a/src/com/android/settings/notification/ZenRuleNameDialog.java
+++ b/src/com/android/settings/notification/ZenRuleNameDialog.java
@@ -19,6 +19,7 @@
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
+import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
@@ -51,6 +52,9 @@
@Override
public void onClick(DialogInterface dialog, int which) {
final String newName = trimmedText();
+ if (TextUtils.isEmpty(newName)) {
+ return;
+ }
if (!mIsNew && mOriginalRuleName != null
&& mOriginalRuleName.equals(newName)) {
return; // no change to an existing rule, just dismiss
diff --git a/src/com/android/settings/print/PrintJobSettingsFragment.java b/src/com/android/settings/print/PrintJobSettingsFragment.java
index 3cd18e1..08bbd97 100644
--- a/src/com/android/settings/print/PrintJobSettingsFragment.java
+++ b/src/com/android/settings/print/PrintJobSettingsFragment.java
@@ -63,7 +63,6 @@
private Preference mMessagePreference;
private PrintJobId mPrintJobId;
- private PrintJob mPrintJob;
@Override
protected int getMetricsCategory() {
@@ -134,17 +133,21 @@
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case MENU_ITEM_ID_CANCEL: {
- getPrintJob().cancel();
- finish();
- return true;
- }
+ PrintJob printJob = getPrintJob();
- case MENU_ITEM_ID_RESTART: {
- getPrintJob().restart();
- finish();
- return true;
+ if (printJob != null) {
+ switch (item.getItemId()) {
+ case MENU_ITEM_ID_CANCEL: {
+ printJob.cancel();
+ finish();
+ return true;
+ }
+
+ case MENU_ITEM_ID_RESTART: {
+ printJob.restart();
+ finish();
+ return true;
+ }
}
}
@@ -161,10 +164,7 @@
}
private PrintJob getPrintJob() {
- if (mPrintJob == null) {
- mPrintJob = mPrintManager.getPrintJob(mPrintJobId);
- }
- return mPrintJob;
+ return mPrintManager.getPrintJob(mPrintJobId);
}
private void updateUi() {
diff --git a/src/com/android/settings/print/PrintServiceSettingsFragment.java b/src/com/android/settings/print/PrintServiceSettingsFragment.java
index e54241c..42d8a8d 100644
--- a/src/com/android/settings/print/PrintServiceSettingsFragment.java
+++ b/src/com/android/settings/print/PrintServiceSettingsFragment.java
@@ -606,7 +606,7 @@
@Override
public Loader<List<PrinterInfo>> onCreateLoader(int id, Bundle args) {
if (id == LOADER_ID_PRINTERS_LOADER) {
- return new PrintersLoader(getActivity());
+ return new PrintersLoader(getContext());
}
return null;
}
diff --git a/src/com/android/settings/print/PrintSettingsFragment.java b/src/com/android/settings/print/PrintSettingsFragment.java
index fb80f2932..c1fbfbb 100644
--- a/src/com/android/settings/print/PrintSettingsFragment.java
+++ b/src/com/android/settings/print/PrintSettingsFragment.java
@@ -115,12 +115,10 @@
getPreferenceScreen().removePreference(mActivePrintJobsCategory);
mPrintJobsController = new PrintJobsController();
- getActivity().getLoaderManager().initLoader(LOADER_ID_PRINT_JOBS_LOADER, null,
- mPrintJobsController);
+ getLoaderManager().initLoader(LOADER_ID_PRINT_JOBS_LOADER, null, mPrintJobsController);
mPrintServicesController = new PrintServicesController();
- getActivity().getLoaderManager().initLoader(LOADER_ID_PRINT_SERVICES, null,
- mPrintServicesController);
+ getLoaderManager().initLoader(LOADER_ID_PRINT_SERVICES, null, mPrintServicesController);
}
@Override
@@ -294,7 +292,7 @@
@Override
public Loader<List<PrintJobInfo>> onCreateLoader(int id, Bundle args) {
if (id == LOADER_ID_PRINT_JOBS_LOADER) {
- return new PrintJobsLoader(getActivity());
+ return new PrintJobsLoader(getContext());
}
return null;
}
diff --git a/src/com/android/settings/vpn2/AppManagementFragment.java b/src/com/android/settings/vpn2/AppManagementFragment.java
index 1a48954..f8e30f9 100644
--- a/src/com/android/settings/vpn2/AppManagementFragment.java
+++ b/src/com/android/settings/vpn2/AppManagementFragment.java
@@ -177,8 +177,10 @@
VpnUtils.clearLockdownVpn(getContext());
}
mConnectivityManager.setAlwaysOnVpnPackageForUser(mUserId, isEnabled ? mPackageName : null);
+ if (isEnabled && !isVpnAlwaysOn()) {
+ CannotConnectFragment.show(this, mVpnLabel);
+ }
updateUI();
- showCantConnectNotificationIfNeeded(isEnabled);
}
private void updateUI() {
@@ -246,52 +248,42 @@
return getAlwaysOnVpnPackage() != null && !isVpnAlwaysOn();
}
- private void showCantConnectNotificationIfNeeded(boolean isEnabledExpected) {
- // Display notification only when user tries to turn on but system fails to turn it on.
- if (isEnabledExpected && !isVpnAlwaysOn()) {
- String appDisplayName = mPackageName;
- try {
- appDisplayName = VpnConfig.getVpnLabel(getContext(), mPackageName).toString();
- } catch (NameNotFoundException e) {
- // Use default package name as app name. Quietly fail.
+ public static class CannotConnectFragment extends DialogFragment {
+ private static final String TAG = "CannotConnect";
+ private static final String ARG_VPN_LABEL = "label";
+
+ public static void show(AppManagementFragment parent, String vpnLabel) {
+ if (parent.getFragmentManager().findFragmentByTag(TAG) == null) {
+ final Bundle args = new Bundle();
+ args.putString(ARG_VPN_LABEL, vpnLabel);
+
+ final DialogFragment frag = new CannotConnectFragment();
+ frag.setArguments(args);
+ frag.show(parent.getFragmentManager(), TAG);
}
- postCantConnectNotification(getContext(), appDisplayName,
- mPackageUid /* notificationId */);
}
- }
- /**
- * @param notificationId should be unique to the vpn app, e.g. uid, to keep one notification per
- * vpn app per user
- */
- private static void postCantConnectNotification(Context context, @NonNull String vpnName,
- int notificationId) {
- final Resources res = context.getResources();
- // Only action is specified to match cross-profile intent filter set by ManagedProfileSetup
- final Intent intent = new Intent(Settings.ACTION_VPN_SETTINGS);
- final PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,
- Intent.FLAG_ACTIVITY_NEW_TASK);
-
- final Notification notification = new Notification.Builder(context)
- .setContentTitle(res.getString(R.string.vpn_cant_connect_notification_title,
- vpnName))
- .setContentText(res.getString(R.string.vpn_tap_for_vpn_settings))
- .setSmallIcon(R.drawable.ic_settings_wireless)
- .setContentIntent(pendingIntent)
- .setAutoCancel(true)
- .build();
-
- NotificationManager nm = context.getSystemService(NotificationManager.class);
- nm.notify(notificationId, notification);
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final String vpnLabel = getArguments().getString(ARG_VPN_LABEL);
+ return new AlertDialog.Builder(getActivity())
+ .setTitle(getActivity().getString(R.string.vpn_cant_connect_title, vpnLabel))
+ .setMessage(getActivity().getString(R.string.vpn_cant_connect_message))
+ .setPositiveButton(R.string.okay, null)
+ .create();
+ }
}
public static class ReplaceExistingVpnFragment extends DialogFragment
implements DialogInterface.OnClickListener {
+ private static final String TAG = "ReplaceExistingVpn";
public static void show(AppManagementFragment parent) {
- final ReplaceExistingVpnFragment frag = new ReplaceExistingVpnFragment();
- frag.setTargetFragment(parent, 0);
- frag.show(parent.getFragmentManager(), null);
+ if (parent.getFragmentManager().findFragmentByTag(TAG) == null) {
+ final ReplaceExistingVpnFragment frag = new ReplaceExistingVpnFragment();
+ frag.setTargetFragment(parent, 0);
+ frag.show(parent.getFragmentManager(), TAG);
+ }
}
@Override
@@ -300,7 +292,7 @@
.setTitle(R.string.vpn_replace_always_on_vpn_title)
.setMessage(getActivity().getString(R.string.vpn_replace_always_on_vpn_message))
.setNegativeButton(getActivity().getString(R.string.vpn_cancel), null)
- .setPositiveButton(getActivity().getString(R.string.vpn_continue), this)
+ .setPositiveButton(getActivity().getString(R.string.vpn_replace), this)
.create();
}
diff --git a/src/com/android/settings/vpn2/AppPreference.java b/src/com/android/settings/vpn2/AppPreference.java
index e3649a8..3369970 100644
--- a/src/com/android/settings/vpn2/AppPreference.java
+++ b/src/com/android/settings/vpn2/AppPreference.java
@@ -32,20 +32,45 @@
*/
public class AppPreference extends ManageablePreference {
public static final int STATE_CONNECTED = LegacyVpnInfo.STATE_CONNECTED;
- public static final int STATE_DISCONNECTED = LegacyVpnInfo.STATE_DISCONNECTED;
+ public static final int STATE_DISCONNECTED = STATE_NONE;
- private int mState = STATE_DISCONNECTED;
- private String mPackageName;
- private String mName;
+ private final String mPackageName;
+ private final String mName;
- public AppPreference(Context context) {
+ public AppPreference(Context context, int userId, String packageName) {
super(context, null /* attrs */);
- }
-
- @Override
- public void setUserId(int userId) {
super.setUserId(userId);
- update();
+
+ mPackageName = packageName;
+
+ // Fetch icon and VPN label
+ String label = packageName;
+ Drawable icon = null;
+ try {
+ // Make all calls to the package manager as the appropriate user.
+ Context userContext = getUserContext();
+ PackageManager pm = userContext.getPackageManager();
+ // The nested catch block is for the case that the app doesn't exist, so we can fall
+ // back to the default activity icon.
+ try {
+ PackageInfo pkgInfo = pm.getPackageInfo(mPackageName, 0 /* flags */);
+ if (pkgInfo != null) {
+ icon = pkgInfo.applicationInfo.loadIcon(pm);
+ label = VpnConfig.getVpnLabel(userContext, mPackageName).toString();
+ }
+ } catch (PackageManager.NameNotFoundException pkgNotFound) {
+ // Use default app label and icon as fallback
+ }
+ if (icon == null) {
+ icon = pm.getDefaultActivityIcon();
+ }
+ } catch (PackageManager.NameNotFoundException userNotFound) {
+ // No user, no useful information to obtain. Quietly fail.
+ }
+ mName = label;
+
+ setTitle(mName);
+ setIcon(icon);
}
public PackageInfo getPackageInfo() {
@@ -65,58 +90,6 @@
return mPackageName;
}
- public void setPackageName(String name) {
- mPackageName = name;
- update();
- }
-
- public int getState() {
- return mState;
- }
-
- public void setState(int state) {
- mState = state;
- update();
- }
-
- private void update() {
- if (mPackageName == null || mUserId == UserHandle.USER_NULL) {
- return;
- }
-
- setSummary(getSummaryString(mState == STATE_DISCONNECTED ? STATE_NONE : mState));
-
- mName = mPackageName;
- Drawable icon = null;
-
- try {
- // Make all calls to the package manager as the appropriate user.
- Context userContext = getUserContext();
- PackageManager pm = userContext.getPackageManager();
- // Fetch icon and VPN label- the nested catch block is for the case that the app doesn't
- // exist, in which case we can fall back to the default activity icon for an activity in
- // that user.
- try {
- PackageInfo pkgInfo = pm.getPackageInfo(mPackageName, 0 /* flags */);
- if (pkgInfo != null) {
- icon = pkgInfo.applicationInfo.loadIcon(pm);
- mName = VpnConfig.getVpnLabel(userContext, mPackageName).toString();
- }
- } catch (PackageManager.NameNotFoundException pkgNotFound) {
- // Use default app label and icon as fallback
- }
- if (icon == null) {
- icon = pm.getDefaultActivityIcon();
- }
- } catch (PackageManager.NameNotFoundException userNotFound) {
- // No user, no useful information to obtain. Quietly fail.
- }
- setTitle(mName);
- setIcon(icon);
-
- notifyHierarchyChanged();
- }
-
private Context getUserContext() throws PackageManager.NameNotFoundException {
UserHandle user = UserHandle.of(mUserId);
return getContext().createPackageContextAsUser(
diff --git a/src/com/android/settings/vpn2/LegacyVpnPreference.java b/src/com/android/settings/vpn2/LegacyVpnPreference.java
index a9b8aec..c1550e2 100644
--- a/src/com/android/settings/vpn2/LegacyVpnPreference.java
+++ b/src/com/android/settings/vpn2/LegacyVpnPreference.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.support.v7.preference.Preference;
+import android.text.TextUtils;
import android.view.View;
import com.android.internal.net.VpnProfile;
@@ -31,11 +32,9 @@
public class LegacyVpnPreference extends ManageablePreference {
private VpnProfile mProfile;
- /** One of the STATE_* fields from LegacyVpnInfo, or STATE_NONE */
- private int mState = STATE_NONE;
-
LegacyVpnPreference(Context context) {
super(context, null /* attrs */);
+ setIcon(R.mipmap.ic_launcher_settings);
}
public VpnProfile getProfile() {
@@ -43,22 +42,13 @@
}
public void setProfile(VpnProfile profile) {
- mProfile = profile;
- update();
- }
-
- public void setState(int state) {
- mState = state;
- update();
- }
-
- private void update() {
- setSummary(getSummaryString(mState));
- if (mProfile != null) {
- setIcon(R.mipmap.ic_launcher_settings);
- setTitle(mProfile.name);
+ final String oldLabel = (mProfile != null ? mProfile.name : null);
+ final String newLabel = (profile != null ? profile.name : null);
+ if (!TextUtils.equals(oldLabel, newLabel)) {
+ setTitle(newLabel);
+ notifyHierarchyChanged();
}
- notifyHierarchyChanged();
+ mProfile = profile;
}
@Override
@@ -93,5 +83,4 @@
}
super.onClick(v);
}
-
-}
\ No newline at end of file
+}
diff --git a/src/com/android/settings/vpn2/ManageablePreference.java b/src/com/android/settings/vpn2/ManageablePreference.java
index ad8a8a3..e31a396 100644
--- a/src/com/android/settings/vpn2/ManageablePreference.java
+++ b/src/com/android/settings/vpn2/ManageablePreference.java
@@ -34,6 +34,7 @@
public static int STATE_NONE = -1;
boolean mIsAlwaysOn = false;
+ int mState = STATE_NONE;
int mUserId;
public ManageablePreference(Context context, AttributeSet attrs) {
@@ -56,24 +57,40 @@
return mIsAlwaysOn;
}
+ public int getState() {
+ return mState;
+ }
+
+ public void setState(int state) {
+ if (mState != state) {
+ mState = state;
+ updateSummary();
+ notifyHierarchyChanged();
+ }
+ }
+
public void setAlwaysOn(boolean isEnabled) {
- mIsAlwaysOn = isEnabled;
+ if (mIsAlwaysOn != isEnabled) {
+ mIsAlwaysOn = isEnabled;
+ updateSummary();
+ }
}
/**
- * State is not shown for {@code STATE_NONE}
+ * Update the preference summary string (see {@see Preference#setSummary}) with a string
+ * reflecting connection status and always-on setting.
*
- * @return summary string showing current connection state and always-on-vpn state
+ * State is not shown for {@code STATE_NONE}.
*/
- protected String getSummaryString(int state) {
+ protected void updateSummary() {
final Resources res = getContext().getResources();
final String[] states = res.getStringArray(R.array.vpn_states);
- String summary = state == STATE_NONE ? "" : states[state];
+ String summary = (mState == STATE_NONE ? "" : states[mState]);
if (mIsAlwaysOn) {
final String alwaysOnString = res.getString(R.string.vpn_always_on_active);
summary = TextUtils.isEmpty(summary) ? alwaysOnString : res.getString(
R.string.join_two_unrelated_items, summary, alwaysOnString);
}
- return summary;
+ setSummary(summary);
}
}
diff --git a/src/com/android/settings/vpn2/VpnSettings.java b/src/com/android/settings/vpn2/VpnSettings.java
index 6c47b43..a675779 100644
--- a/src/com/android/settings/vpn2/VpnSettings.java
+++ b/src/com/android/settings/vpn2/VpnSettings.java
@@ -34,7 +34,6 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.security.Credentials;
@@ -211,8 +210,8 @@
final List<VpnProfile> vpnProfiles = loadVpnProfiles(mKeyStore);
final List<AppVpnInfo> vpnApps = getVpnApps(getActivity(), /* includeProfiles */ true);
- final List<LegacyVpnInfo> connectedLegacyVpns = getConnectedLegacyVpns();
- final List<AppVpnInfo> connectedAppVpns = getConnectedAppVpns();
+ final Map<String, LegacyVpnInfo> connectedLegacyVpns = getConnectedLegacyVpns();
+ final Set<AppVpnInfo> connectedAppVpns = getConnectedAppVpns();
final Set<AppVpnInfo> alwaysOnAppVpnInfos = getAlwaysOnAppVpnInfos();
final String lockdownVpnKey = VpnUtils.getLockdownVpn();
@@ -231,18 +230,26 @@
for (VpnProfile profile : vpnProfiles) {
LegacyVpnPreference p = findOrCreatePreference(profile);
- p.setState(LegacyVpnPreference.STATE_NONE);
+ if (connectedLegacyVpns.containsKey(profile.key)) {
+ p.setState(connectedLegacyVpns.get(profile.key).state);
+ } else {
+ p.setState(LegacyVpnPreference.STATE_NONE);
+ }
p.setAlwaysOn(lockdownVpnKey != null && lockdownVpnKey.equals(profile.key));
updates.add(p);
}
for (AppVpnInfo app : vpnApps) {
AppPreference p = findOrCreatePreference(app);
- p.setState(AppPreference.STATE_DISCONNECTED);
+ if (connectedAppVpns.contains(app)) {
+ p.setState(AppPreference.STATE_CONNECTED);
+ } else {
+ p.setState(AppPreference.STATE_DISCONNECTED);
+ }
p.setAlwaysOn(alwaysOnAppVpnInfos.contains(app));
updates.add(p);
}
- // Trim preferences for deleted VPNs
+ // Trim out deleted VPN preferences
mLegacyVpnPreferences.values().retainAll(updates);
mAppPreferences.values().retainAll(updates);
@@ -260,20 +267,6 @@
for (Preference pref : updates) {
vpnGroup.addPreference(pref);
}
-
- // Mark connected VPNs
- for (LegacyVpnInfo info : connectedLegacyVpns) {
- final LegacyVpnPreference preference = mLegacyVpnPreferences.get(info.key);
- if (preference != null) {
- preference.setState(info.state);
- }
- }
- for (AppVpnInfo app : connectedAppVpns) {
- final AppPreference preference = mAppPreferences.get(app);
- if (preference != null) {
- preference.setState(AppPreference.STATE_CONNECTED);
- }
- }
}
});
@@ -369,6 +362,7 @@
pref.setOnPreferenceClickListener(this);
mLegacyVpnPreferences.put(profile.key, pref);
}
+ // This may change as the profile can update and keep the same key.
pref.setProfile(profile);
return pref;
}
@@ -377,33 +371,31 @@
private AppPreference findOrCreatePreference(AppVpnInfo app) {
AppPreference pref = mAppPreferences.get(app);
if (pref == null) {
- pref = new AppPreference(getPrefContext());
+ pref = new AppPreference(getPrefContext(), app.userId, app.packageName);
pref.setOnGearClickListener(mGearListener);
pref.setOnPreferenceClickListener(this);
mAppPreferences.put(app, pref);
}
- pref.setUserId(app.userId);
- pref.setPackageName(app.packageName);
return pref;
}
@WorkerThread
- private List<LegacyVpnInfo> getConnectedLegacyVpns() {
+ private Map<String, LegacyVpnInfo> getConnectedLegacyVpns() {
try {
mConnectedLegacyVpn = mConnectivityService.getLegacyVpnInfo(UserHandle.myUserId());
if (mConnectedLegacyVpn != null) {
- return Collections.singletonList(mConnectedLegacyVpn);
+ return Collections.singletonMap(mConnectedLegacyVpn.key, mConnectedLegacyVpn);
}
} catch (RemoteException e) {
Log.e(LOG_TAG, "Failure updating VPN list with connected legacy VPNs", e);
}
- return Collections.emptyList();
+ return Collections.emptyMap();
}
@WorkerThread
- private List<AppVpnInfo> getConnectedAppVpns() {
+ private Set<AppVpnInfo> getConnectedAppVpns() {
// Mark connected third-party services
- List<AppVpnInfo> connections = new ArrayList<>();
+ Set<AppVpnInfo> connections = new ArraySet<>();
try {
for (UserHandle profile : mUserManager.getUserProfiles()) {
VpnConfig config = mConnectivityService.getVpnConfig(profile.getIdentifier());
diff --git a/src/com/android/settings/wifi/WifiApEnabler.java b/src/com/android/settings/wifi/WifiApEnabler.java
index cb94732..8cfd049 100644
--- a/src/com/android/settings/wifi/WifiApEnabler.java
+++ b/src/com/android/settings/wifi/WifiApEnabler.java
@@ -27,6 +27,7 @@
import android.support.v14.preference.SwitchPreference;
import com.android.settings.R;
+import com.android.settings.datausage.DataSaverBackend;
import com.android.settingslib.TetherUtil;
import java.util.ArrayList;
@@ -35,6 +36,7 @@
private final Context mContext;
private final SwitchPreference mSwitch;
private final CharSequence mOriginalSummary;
+ private final DataSaverBackend mDataSaverBackend;
private WifiManager mWifiManager;
private final IntentFilter mIntentFilter;
@@ -70,14 +72,16 @@
}
};
- public WifiApEnabler(Context context, SwitchPreference switchPreference) {
+ public WifiApEnabler(Context context, DataSaverBackend dataSaverBackend,
+ SwitchPreference switchPreference) {
mContext = context;
+ mDataSaverBackend = dataSaverBackend;
mSwitch = switchPreference;
mOriginalSummary = switchPreference.getSummary();
switchPreference.setPersistent(false);
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- mCm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
mWifiRegexs = mCm.getTetherableWifiRegexs();
@@ -99,7 +103,7 @@
boolean isAirplaneMode = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
if(!isAirplaneMode) {
- mSwitch.setEnabled(true);
+ mSwitch.setEnabled(!mDataSaverBackend.isDataSaverEnabled());
} else {
mSwitch.setSummary(mOriginalSummary);
mSwitch.setEnabled(false);
@@ -162,7 +166,7 @@
*/
mSwitch.setChecked(true);
/* Doesnt need the airplane check */
- mSwitch.setEnabled(true);
+ mSwitch.setEnabled(!mDataSaverBackend.isDataSaverEnabled());
break;
case WifiManager.WIFI_AP_STATE_DISABLING:
mSwitch.setSummary(R.string.wifi_tether_stopping);