Merge "Make footer preferences non-clickable by default" into oc-dev
diff --git a/res/layout/preference_progress_category.xml b/res/layout/preference_progress_category.xml
index d858697..2ac3dc1 100644
--- a/res/layout/preference_progress_category.xml
+++ b/res/layout/preference_progress_category.xml
@@ -20,9 +20,9 @@
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
- android:layout_marginBottom="16dp"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingTop="16dp"
+ android:paddingBottom="16dp"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
<LinearLayout
@@ -56,6 +56,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="16dip"
+ android:minWidth="32dp"
android:text="@string/progress_scanning"/>
</LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2f7aa64..5a9fe4b 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3609,8 +3609,6 @@
<!-- Text for filter option in ManageApps screen to display list of
packages installed on sdcard. -->
<string name="filter_apps_onsdcard" product="default">On SD card</string>
- <!-- [CHAR LIMIT=25] Manage applications, text telling using an application is disabled. -->
- <string name="disabled">Disabled</string>
<!-- [CHAR LIMIT=30] Manage applications, text telling using an application is not installed for the current user. The key part is that it's not installed. -->
<string name="not_installed">Not installed for this user</string>
<!-- [CHAR LIMIT=30] App details, text telling an application is installed. -->
@@ -7671,8 +7669,6 @@
<!-- Summary of app not allowed to display over other apps [CHAR LIMIT=60] -->
<string name="system_alert_window_off">No</string>
- <!-- Title for settings screen for controlling apps that can install other apps on device [CHAR LIMIT=50] -->
- <string name="install_other_apps">Install unknown apps</string>
<!-- Keywords for setting screen for controlling apps that can install other apps on device -->
<string name="keywords_install_other_apps">install apps unknown sources</string>
@@ -7702,10 +7698,6 @@
<string name="write_settings_on">Yes</string>
<!-- Summary of app not allowed to write system settings [CHAR LIMIT=45] -->
<string name="write_settings_off">No</string>
- <!-- Summary of app trusted to install apps [CHAR LIMIT=45] -->
- <string name="external_source_trusted">Allowed</string>
- <!-- Summary of app not trusted to install apps [CHAR LIMIT=45] -->
- <string name="external_source_untrusted">Not allowed</string>
<!-- Title of switch preference that controls whether an external app source is trusted or not [CHAR LIMIT=50] -->
<string name="external_source_switch_title">Allow from this source</string>
@@ -8685,4 +8677,8 @@
<!-- Title for the installed app info storage page. The total storage space taken up by this app. [CHAR LIMIT=40]-->
<string name="app_info_storage_title">Space used</string>
+ <!-- The divider symbol between different parts of the notification header including spaces. not translatable [CHAR LIMIT=3] -->
+ <string name="notification_header_divider_symbol_with_spaces" translatable="false">" • "</string>
+
+
</resources>
diff --git a/res/xml/enterprise_privacy_settings.xml b/res/xml/enterprise_privacy_settings.xml
index 45c784e..2d07fa2 100644
--- a/res/xml/enterprise_privacy_settings.xml
+++ b/res/xml/enterprise_privacy_settings.xml
@@ -45,7 +45,8 @@
android:selectable="false"/>
</PreferenceCategory>
- <PreferenceCategory android:title="@string/enterprise_privacy_exposure_changes_category">
+ <PreferenceCategory android:title="@string/enterprise_privacy_exposure_changes_category"
+ android:key="exposure_changes_category">
<Preference android:fragment="com.android.settings.enterprise.ApplicationListFragment$EnterpriseInstalledPackages"
android:key="number_enterprise_installed_packages"
android:title="@string/enterprise_privacy_enterprise_installed_packages"/>
diff --git a/res/xml/wifi_network_details_fragment.xml b/res/xml/wifi_network_details_fragment.xml
index fafbbb0..f9926ca 100644
--- a/res/xml/wifi_network_details_fragment.xml
+++ b/res/xml/wifi_network_details_fragment.xml
@@ -82,8 +82,12 @@
<!-- IPv6 Details -->
<PreferenceCategory
- android:key="ipv6_details_category"
+ android:key="ipv6_category"
android:title="@string/wifi_details_ipv6_address_header"
- android:selectable="false"/>
+ android:selectable="false">
+ <Preference
+ android:key="ipv6_addresses"
+ android:selectable="false"/>
+ </PreferenceCategory>
</PreferenceScreen>
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 9655fd2..8a77fea 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -603,7 +603,8 @@
}
/**
- * Returns the managed profile of the current user or null if none found.
+ * Returns the managed profile of the current user or {@code null} if none is found or a profile
+ * exists but it is disabled.
*/
public static UserHandle getManagedProfile(UserManager userManager) {
List<UserHandle> userProfiles = userManager.getUserProfiles();
@@ -622,6 +623,29 @@
}
/**
+ * Returns the managed profile of the current user or {@code null} if none is found. Unlike
+ * {@link #getManagedProfile} this method returns enabled and disabled managed profiles.
+ */
+ public static UserHandle getManagedProfileWithDisabled(UserManager userManager) {
+ // TODO: Call getManagedProfileId from here once Robolectric supports
+ // API level 24 and UserManager.getProfileIdsWithDisabled can be Mocked (to avoid having
+ // yet another implementation that loops over user profiles in this method). In the meantime
+ // we need to use UserManager.getProfiles that is available on API 23 (the one currently
+ // used for Settings Robolectric tests).
+ final int myUserId = UserHandle.myUserId();
+ List<UserInfo> profiles = userManager.getProfiles(myUserId);
+ final int count = profiles.size();
+ for (int i = 0; i < count; i++) {
+ final UserInfo profile = profiles.get(i);
+ if (profile.isManagedProfile()
+ && profile.getUserHandle().getIdentifier() != myUserId) {
+ return profile.getUserHandle();
+ }
+ }
+ return null;
+ }
+
+ /**
* Retrieves the id for the given user's managed profile.
*
* @return the managed profile id or UserHandle.USER_NULL if there is none.
diff --git a/src/com/android/settings/accounts/AutoSyncWorkDataPreferenceController.java b/src/com/android/settings/accounts/AutoSyncWorkDataPreferenceController.java
index 1d08968..acf43aa 100644
--- a/src/com/android/settings/accounts/AutoSyncWorkDataPreferenceController.java
+++ b/src/com/android/settings/accounts/AutoSyncWorkDataPreferenceController.java
@@ -27,7 +27,7 @@
public AutoSyncWorkDataPreferenceController(Context context, Fragment parent) {
super(context, parent);
- mUserHandle = Utils.getManagedProfile(mUserManager);
+ mUserHandle = Utils.getManagedProfileWithDisabled(mUserManager);
}
@Override
diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
index 083681a..3ee3c50 100755
--- a/src/com/android/settings/applications/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -144,7 +144,8 @@
private static final int LOADER_CHART_DATA = 2;
private static final int LOADER_STORAGE = 3;
- private static final int LOADER_BATTERY = 4;
+ @VisibleForTesting
+ static final int LOADER_BATTERY = 4;
private static final int DLG_FORCE_STOP = DLG_BASE + 1;
private static final int DLG_DISABLE = DLG_BASE + 2;
@@ -207,7 +208,8 @@
private AppStorageStats mLastResult;
private String mBatteryPercent;
- private final LoaderCallbacks<BatteryStatsHelper> mBatteryCallbacks =
+ @VisibleForTesting
+ final LoaderCallbacks<BatteryStatsHelper> mBatteryCallbacks =
new LoaderCallbacks<BatteryStatsHelper>() {
@Override
@@ -415,11 +417,16 @@
mDataCallbacks);
loaderManager.restartLoader(LOADER_STORAGE, Bundle.EMPTY, this);
}
- getLoaderManager().initLoader(LOADER_BATTERY, Bundle.EMPTY, mBatteryCallbacks);
+ restartBatteryStatsLoader();
new MemoryUpdater().execute();
updateDynamicPrefs();
}
+ @VisibleForTesting
+ public void restartBatteryStatsLoader() {
+ getLoaderManager().restartLoader(LOADER_BATTERY, Bundle.EMPTY, mBatteryCallbacks);
+ }
+
@Override
public void onPause() {
getLoaderManager().destroyLoader(LOADER_CHART_DATA);
diff --git a/src/com/android/settings/core/DynamicAvailabilityPreferenceController.java b/src/com/android/settings/core/DynamicAvailabilityPreferenceController.java
index 9323aa3..8d98b50 100644
--- a/src/com/android/settings/core/DynamicAvailabilityPreferenceController.java
+++ b/src/com/android/settings/core/DynamicAvailabilityPreferenceController.java
@@ -29,6 +29,7 @@
private Preference mPreference;
private PreferenceScreen mScreen;
+ private PreferenceAvailabilityObserver mAvailabilityObserver = null;
public DynamicAvailabilityPreferenceController(Context context, Lifecycle lifecycle) {
super(context);
@@ -37,6 +38,14 @@
}
}
+ public void setAvailabilityObserver(PreferenceAvailabilityObserver observer) {
+ mAvailabilityObserver = observer;
+ }
+
+ public PreferenceAvailabilityObserver getAvailabilityObserver() {
+ return mAvailabilityObserver;
+ }
+
@Override
public void displayPreference(PreferenceScreen screen) {
mScreen = screen;
@@ -56,4 +65,10 @@
mScreen.addPreference(mPreference);
}
}
+
+ protected void notifyOnAvailabilityUpdate(boolean available) {
+ if (mAvailabilityObserver != null) {
+ mAvailabilityObserver.onPreferenceAvailabilityUpdated(getPreferenceKey(), available);
+ }
+ }
}
diff --git a/src/com/android/settings/core/PreferenceAvailabilityObserver.java b/src/com/android/settings/core/PreferenceAvailabilityObserver.java
new file mode 100644
index 0000000..46ff3ba
--- /dev/null
+++ b/src/com/android/settings/core/PreferenceAvailabilityObserver.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 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.core;
+
+/**
+ * @deprecated This interface allows a {@link android.support.v7.preference.PreferenceGroup}'s
+ * controller to observe the availability of the {@link android.support.v7.preference.Preference}s
+ * inside it, hiding the group when all preferences become unavailable. In the future,
+ * {@link android.support.v7.preference.PreferenceGroup} will have native support for that
+ * functionality, removing the need for this interface.
+ */
+public interface PreferenceAvailabilityObserver {
+
+ /**
+ * Notifies the observer that the availability of the preference identified by {@code key} has
+ * been updated.
+ */
+ void onPreferenceAvailabilityUpdated(String key, boolean available);
+}
diff --git a/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java b/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java
index f0aca01..556baec 100644
--- a/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java
+++ b/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java
@@ -50,15 +50,15 @@
true /* async */,
(num) -> {
if (num == 0) {
- preference.setVisible(false);
mHasApps = false;
} else {
- preference.setVisible(true);
preference.setSummary(mContext.getResources().getQuantityString(
R.plurals.enterprise_privacy_number_packages_lower_bound,
num, num));
mHasApps = true;
}
+ preference.setVisible(mHasApps);
+ notifyOnAvailabilityUpdate(mHasApps);
});
}
@@ -80,6 +80,7 @@
mFeatureProvider.calculateNumberOfAppsWithAdminGrantedPermissions(mPermissions,
false /* async */, (num) -> haveAppsWithAdminGrantedPermissions[0] = num > 0);
mHasApps = haveAppsWithAdminGrantedPermissions[0];
+ notifyOnAvailabilityUpdate(mHasApps);
return mHasApps;
}
diff --git a/src/com/android/settings/enterprise/AlwaysOnVpnCurrentUserPreferenceController.java b/src/com/android/settings/enterprise/AlwaysOnVpnCurrentUserPreferenceController.java
index e98ece8..32f2bbe 100644
--- a/src/com/android/settings/enterprise/AlwaysOnVpnCurrentUserPreferenceController.java
+++ b/src/com/android/settings/enterprise/AlwaysOnVpnCurrentUserPreferenceController.java
@@ -42,7 +42,9 @@
@Override
public boolean isAvailable() {
- return mFeatureProvider.isAlwaysOnVpnSetInCurrentUser();
+ final boolean available = mFeatureProvider.isAlwaysOnVpnSetInCurrentUser();
+ notifyOnAvailabilityUpdate(available);
+ return available;
}
@Override
diff --git a/src/com/android/settings/enterprise/AlwaysOnVpnManagedProfilePreferenceController.java b/src/com/android/settings/enterprise/AlwaysOnVpnManagedProfilePreferenceController.java
index 4796b75..8486368 100644
--- a/src/com/android/settings/enterprise/AlwaysOnVpnManagedProfilePreferenceController.java
+++ b/src/com/android/settings/enterprise/AlwaysOnVpnManagedProfilePreferenceController.java
@@ -33,7 +33,9 @@
@Override
public boolean isAvailable() {
- return mFeatureProvider.isAlwaysOnVpnSetInManagedProfile();
+ final boolean available = mFeatureProvider.isAlwaysOnVpnSetInManagedProfile();
+ notifyOnAvailabilityUpdate(available);
+ return available;
}
@Override
diff --git a/src/com/android/settings/enterprise/CaCertsPreferenceController.java b/src/com/android/settings/enterprise/CaCertsPreferenceController.java
index d020676..fc89dc8 100644
--- a/src/com/android/settings/enterprise/CaCertsPreferenceController.java
+++ b/src/com/android/settings/enterprise/CaCertsPreferenceController.java
@@ -44,8 +44,11 @@
@Override
public boolean isAvailable() {
- return mFeatureProvider.getNumberOfOwnerInstalledCaCertsForCurrentUserAndManagedProfile()
- > 0;
+ final boolean available =
+ mFeatureProvider.getNumberOfOwnerInstalledCaCertsForCurrentUserAndManagedProfile()
+ > 0;
+ notifyOnAvailabilityUpdate(available);
+ return available;
}
@Override
diff --git a/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceController.java b/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceController.java
index acbcc2e..9876f71 100644
--- a/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceController.java
+++ b/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceController.java
@@ -42,14 +42,18 @@
public void updateState(Preference preference) {
mFeatureProvider.calculateNumberOfPolicyInstalledApps(true /* async */,
(num) -> {
+ final boolean available;
if (num == 0) {
- preference.setVisible(false);
+ available = false;
} else {
- preference.setVisible(true);
+ available = true;
preference.setSummary(mContext.getResources().getQuantityString(
R.plurals.enterprise_privacy_number_packages_lower_bound, num,
num));
+
}
+ preference.setVisible(available);
+ notifyOnAvailabilityUpdate(available);
});
}
@@ -68,7 +72,9 @@
final Boolean[] haveEnterpriseInstalledPackages = { null };
mFeatureProvider.calculateNumberOfPolicyInstalledApps(false /* async */,
(num) -> haveEnterpriseInstalledPackages[0] = num > 0);
- return haveEnterpriseInstalledPackages[0];
+ final boolean available = haveEnterpriseInstalledPackages[0];
+ notifyOnAvailabilityUpdate(available);
+ return available;
}
@Override
diff --git a/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java b/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java
index 69e0416..372982f 100644
--- a/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java
+++ b/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java
@@ -45,7 +45,9 @@
@Override
public boolean isAvailable() {
- return mFeatureProvider.hasDeviceOwner();
+ final boolean available = mFeatureProvider.hasDeviceOwner();
+ notifyOnAvailabilityUpdate(available);
+ return available;
}
@Override
diff --git a/src/com/android/settings/enterprise/EnterprisePrivacySettings.java b/src/com/android/settings/enterprise/EnterprisePrivacySettings.java
index a80dec0..f7327c8 100644
--- a/src/com/android/settings/enterprise/EnterprisePrivacySettings.java
+++ b/src/com/android/settings/enterprise/EnterprisePrivacySettings.java
@@ -61,23 +61,32 @@
controllers.add(new NetworkLogsPreferenceController(context));
controllers.add(new BugReportsPreferenceController(context));
controllers.add(new SecurityLogsPreferenceController(context));
- controllers.add(new EnterpriseInstalledPackagesPreferenceController(context, lifecycle,
- async));
- controllers.add(new AdminGrantedLocationPermissionsPreferenceController(context, lifecycle,
- async));
- controllers.add(new AdminGrantedMicrophonePermissionPreferenceController(context, lifecycle,
- async));
- controllers.add(new AdminGrantedCameraPermissionPreferenceController(context, lifecycle,
- async));
- controllers.add(new EnterpriseSetDefaultAppsPreferenceController(context, lifecycle));
- controllers.add(new AlwaysOnVpnCurrentUserPreferenceController(context, lifecycle));
- controllers.add(new AlwaysOnVpnManagedProfilePreferenceController(context, lifecycle));
- controllers.add(new GlobalHttpProxyPreferenceController(context, lifecycle));
- controllers.add(new CaCertsPreferenceController(context, lifecycle));
+ final List exposureChangesCategoryControllers = new ArrayList<PreferenceController>();
+ exposureChangesCategoryControllers.add(new EnterpriseInstalledPackagesPreferenceController(
+ context, lifecycle, async));
+ exposureChangesCategoryControllers.add(
+ new AdminGrantedLocationPermissionsPreferenceController(context, lifecycle, async));
+ exposureChangesCategoryControllers.add(
+ new AdminGrantedMicrophonePermissionPreferenceController(context, lifecycle,
+ async));
+ exposureChangesCategoryControllers.add(new AdminGrantedCameraPermissionPreferenceController(
+ context, lifecycle, async));
+ exposureChangesCategoryControllers.add(new EnterpriseSetDefaultAppsPreferenceController(
+ context, lifecycle));
+ exposureChangesCategoryControllers.add(new AlwaysOnVpnCurrentUserPreferenceController(
+ context, lifecycle));
+ exposureChangesCategoryControllers.add(new AlwaysOnVpnManagedProfilePreferenceController(
+ context, lifecycle));
+ exposureChangesCategoryControllers.add(new ImePreferenceController(context, lifecycle));
+ exposureChangesCategoryControllers.add(new GlobalHttpProxyPreferenceController(context,
+ lifecycle));
+ exposureChangesCategoryControllers.add(new CaCertsPreferenceController(context, lifecycle));
+ controllers.addAll(exposureChangesCategoryControllers);
+ controllers.add(new ExposureChangesCategoryPreferenceController(context, lifecycle,
+ exposureChangesCategoryControllers, async));
controllers.add(new FailedPasswordWipeCurrentUserPreferenceController(context, lifecycle));
controllers.add(new FailedPasswordWipeManagedProfilePreferenceController(context,
lifecycle));
- controllers.add(new ImePreferenceController(context, lifecycle));
return controllers;
}
diff --git a/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java
index 2f43a61..35f6e41 100644
--- a/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java
+++ b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java
@@ -1,3 +1,4 @@
+
/*
* Copyright (C) 2017 The Android Open Source Project
*
@@ -48,7 +49,9 @@
@Override
public boolean isAvailable() {
- return getNumberOfEnterpriseSetDefaultApps() > 0;
+ final boolean available = getNumberOfEnterpriseSetDefaultApps() > 0;
+ notifyOnAvailabilityUpdate(available);
+ return available;
}
@Override
diff --git a/src/com/android/settings/enterprise/ExposureChangesCategoryPreferenceController.java b/src/com/android/settings/enterprise/ExposureChangesCategoryPreferenceController.java
new file mode 100644
index 0000000..4c89659
--- /dev/null
+++ b/src/com/android/settings/enterprise/ExposureChangesCategoryPreferenceController.java
@@ -0,0 +1,111 @@
+
+/*
+ * Copyright (C) 2017 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.enterprise;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.core.DynamicAvailabilityPreferenceController;
+import com.android.settings.core.PreferenceAvailabilityObserver;
+import com.android.settings.core.lifecycle.Lifecycle;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A controller that hides a {@link android.support.v7.preference.PreferenceGroup} when none of the
+ * {@link Preference}s inside it are visible.
+ *
+ * TODO(b/62051162): Use {@link android.support.v7.preference.PreferenceGroup}'s native ability to
+ * hide itself when all {@link Preference}s inside it are invisible when that functionality becomes
+ * available. This custom controller will still be needed to remove the
+ * {@link android.support.v7.preference.PreferenceGroup} from the search index as required (by
+ * having {@link #isAvailable()} return {@code false} if the method returns {@code false} for all
+ * {@link Preference}s in the {@link android.support.v7.preference.PreferenceGroup}).
+ */
+public class ExposureChangesCategoryPreferenceController
+ extends DynamicAvailabilityPreferenceController implements PreferenceAvailabilityObserver {
+
+ private static final String KEY_EXPOSURE_CHANGES_CATEGORY = "exposure_changes_category";
+ private final Set<String> mAvailablePrefs = new HashSet<String>();
+ private Preference mPreference = null;
+ private boolean mControllingUi;
+
+ /**
+ * When {@code controllingUi} is {@code true}, some of the preferences may have their visibility
+ * determined asynchronously. In this case, {@link #isAvailable()} must always return {@code
+ * true} and the group should be hidden using {@link Preference#setVisible()} if all preferences
+ * report that they are invisible.
+ * When {@code controllingUi} is {@code false}, we are running on the search indexer thread and
+ * visibility must be determined synchronously. {@link #isAvailable()} can rely on all
+ * preferences having their visibility determined already and should return whether the group is
+ * visible or not.
+ */
+ public ExposureChangesCategoryPreferenceController(Context context, Lifecycle lifecycle,
+ List<DynamicAvailabilityPreferenceController> controllers, boolean controllingUi) {
+ super(context, lifecycle);
+ mControllingUi = controllingUi;
+ for (final DynamicAvailabilityPreferenceController controller : controllers) {
+ controller.setAvailabilityObserver(this);
+ }
+ }
+
+ @Override
+ public void onPreferenceAvailabilityUpdated(String key, boolean available) {
+ if (available) {
+ mAvailablePrefs.add(key);
+ } else {
+ mAvailablePrefs.remove(key);
+ }
+ available = haveAnyVisiblePreferences();
+ if (mControllingUi) {
+ notifyOnAvailabilityUpdate(available);
+ }
+ if (mPreference != null) {
+ mPreference.setVisible(available);
+ }
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ mPreference = preference;
+ mPreference.setVisible(haveAnyVisiblePreferences());
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (mControllingUi) {
+ // When running on the main UI thread, some preferences determine their visibility
+ // asynchronously. Always return true here and determine the pref group's actual
+ // visibility as the other preferences report their visibility asynchronously via
+ // onPreferenceAvailabilityUpdated().
+ return true;
+ }
+ final boolean available = haveAnyVisiblePreferences();
+ notifyOnAvailabilityUpdate(available);
+ return available;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_EXPOSURE_CHANGES_CATEGORY;
+ }
+
+ private boolean haveAnyVisiblePreferences() {
+ return mAvailablePrefs.size() > 0;
+ }
+}
diff --git a/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerBase.java b/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerBase.java
index 2f35da6..7485fe2 100644
--- a/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerBase.java
+++ b/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerBase.java
@@ -46,6 +46,8 @@
@Override
public boolean isAvailable() {
- return getMaximumFailedPasswordsBeforeWipe() > 0;
+ final boolean available = getMaximumFailedPasswordsBeforeWipe() > 0;
+ notifyOnAvailabilityUpdate(available);
+ return available;
}
}
diff --git a/src/com/android/settings/enterprise/GlobalHttpProxyPreferenceController.java b/src/com/android/settings/enterprise/GlobalHttpProxyPreferenceController.java
index 6ee7fc4..76c49da 100644
--- a/src/com/android/settings/enterprise/GlobalHttpProxyPreferenceController.java
+++ b/src/com/android/settings/enterprise/GlobalHttpProxyPreferenceController.java
@@ -32,7 +32,9 @@
@Override
public boolean isAvailable() {
- return mFeatureProvider.isGlobalHttpProxySet();
+ final boolean available = mFeatureProvider.isGlobalHttpProxySet();
+ notifyOnAvailabilityUpdate(available);
+ return available;
}
@Override
diff --git a/src/com/android/settings/enterprise/ImePreferenceController.java b/src/com/android/settings/enterprise/ImePreferenceController.java
index b090bed..b13aec7 100644
--- a/src/com/android/settings/enterprise/ImePreferenceController.java
+++ b/src/com/android/settings/enterprise/ImePreferenceController.java
@@ -43,7 +43,9 @@
@Override
public boolean isAvailable() {
- return mFeatureProvider.getImeLabelIfOwnerSet() != null;
+ final boolean available = mFeatureProvider.getImeLabelIfOwnerSet() != null;
+ notifyOnAvailabilityUpdate(available);
+ return available;
}
@Override
diff --git a/src/com/android/settings/gestures/DoubleTwistPreferenceController.java b/src/com/android/settings/gestures/DoubleTwistPreferenceController.java
index f1c7455..12f28d2 100644
--- a/src/com/android/settings/gestures/DoubleTwistPreferenceController.java
+++ b/src/com/android/settings/gestures/DoubleTwistPreferenceController.java
@@ -20,21 +20,27 @@
import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorManager;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.text.TextUtils;
import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settings.core.lifecycle.Lifecycle;
public class DoubleTwistPreferenceController extends GesturePreferenceController {
private static final String PREF_KEY_VIDEO = "gesture_double_twist_video";
private final String mDoubleTwistPrefKey;
+ private final UserManager mUserManager;
public DoubleTwistPreferenceController(Context context, Lifecycle lifecycle, String key) {
super(context, lifecycle);
mDoubleTwistPrefKey = key;
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
}
@Override
@@ -55,9 +61,14 @@
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
- final boolean enabled = (boolean) newValue;
+ final int enabled = (boolean) newValue ? 1 : 0;
Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED, enabled ? 1 : 0);
+ Settings.Secure.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED, enabled);
+ final int managedProfileUserId = getManagedProfileUserId();
+ if (managedProfileUserId != UserHandle.USER_NULL) {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED, enabled, managedProfileUserId);
+ }
return true;
}
@@ -68,6 +79,11 @@
return doubleTwistEnabled != 0;
}
+ @VisibleForTesting
+ int getManagedProfileUserId() {
+ return Utils.getManagedProfileId(mUserManager, UserHandle.myUserId());
+ }
+
private boolean hasSensor(int nameResId, int vendorResId) {
final Resources resources = mContext.getResources();
final String name = resources.getString(nameResId);
diff --git a/src/com/android/settings/notification/AppNotificationSettings.java b/src/com/android/settings/notification/AppNotificationSettings.java
index efcb5a1..6c4f27c 100644
--- a/src/com/android/settings/notification/AppNotificationSettings.java
+++ b/src/com/android/settings/notification/AppNotificationSettings.java
@@ -95,7 +95,6 @@
getPreferenceScreen().setOrderingAsAdded(true);
setupBlock();
addHeaderPref();
- addAppLinkPref();
mShowLegacyChannelConfig = mBackend.onlyHasDefaultChannel(mAppRow.pkg, mAppRow.uid);
if (mShowLegacyChannelConfig) {
@@ -120,6 +119,7 @@
return;
}
populateChannelList();
+ addAppLinkPref();
}
}.execute();
}
diff --git a/src/com/android/settings/notification/ChannelNotificationSettings.java b/src/com/android/settings/notification/ChannelNotificationSettings.java
index 8c43a3f..3ae0bfa 100644
--- a/src/com/android/settings/notification/ChannelNotificationSettings.java
+++ b/src/com/android/settings/notification/ChannelNotificationSettings.java
@@ -22,15 +22,17 @@
import android.app.Activity;
import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.content.Intent;
-import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Bundle;
-import android.os.UserHandle;
+import android.os.AsyncTask;
import android.provider.Settings;
import android.support.v7.preference.Preference;
import android.text.TextUtils;
+import android.text.BidiFormatter;
+import android.text.SpannableStringBuilder;
import android.util.ArrayMap;
import android.util.Log;
import android.view.LayoutInflater;
@@ -38,7 +40,6 @@
import android.widget.Switch;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.widget.LockPatternUtils;
import com.android.settings.AppHeader;
import com.android.settings.R;
import com.android.settings.RingtonePreference;
@@ -64,6 +65,8 @@
private RestrictedSwitchPreference mVibrate;
private NotificationSoundPreference mRingtone;
private FooterPreference mFooter;
+ private NotificationChannelGroup mChannelGroup;
+ private AppHeaderController mHeaderPref;
@Override
public int getMetricsCategory() {
@@ -93,6 +96,27 @@
mShowLegacyChannelConfig = true;
} else {
populateUpgradedChannelPrefs();
+
+ if (mChannel.getGroup() != null) {
+ // Go look up group name
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... unused) {
+ if (mChannel.getGroup() != null) {
+ mChannelGroup = mBackend.getGroup(mChannel.getGroup(), mPkg, mUid);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void unused) {
+ if (getHost() == null || mChannelGroup == null) {
+ return;
+ }
+ setChannelGroupLabel(mChannelGroup.getName());
+ }
+ }.execute();
+ }
}
updateDependents(mChannel.getImportance() == IMPORTANCE_NONE);
@@ -114,9 +138,10 @@
rows.put(mAppRow.pkg, mAppRow);
collectConfigActivities(rows);
final Activity activity = getActivity();
- final Preference pref = FeatureFactory.getFactory(activity)
+ mHeaderPref = FeatureFactory.getFactory(activity)
.getApplicationFeatureProvider(activity)
- .newAppHeaderController(this /* fragment */, null /* appHeader */)
+ .newAppHeaderController(this /* fragment */, null /* appHeader */);
+ final Preference pref = mHeaderPref
.setIcon(mAppRow.icon)
.setLabel(mChannel.getName())
.setSummary(mAppRow.label)
@@ -128,6 +153,20 @@
getPreferenceScreen().addPreference(pref);
}
+ private void setChannelGroupLabel(CharSequence groupName) {
+ final SpannableStringBuilder summary = new SpannableStringBuilder();
+ BidiFormatter bidi = BidiFormatter.getInstance();
+ summary.append(bidi.unicodeWrap(mAppRow.label.toString()));
+ if (groupName != null) {
+ summary.append(bidi.unicodeWrap(mContext.getText(
+ R.string.notification_header_divider_symbol_with_spaces)));
+ summary.append(bidi.unicodeWrap(groupName.toString()));
+ }
+ final Activity activity = getActivity();
+ mHeaderPref.setSummary(summary.toString());
+ mHeaderPref.done(activity, getPrefContext());
+ }
+
private void addFooterPref() {
if (!TextUtils.isEmpty(mChannel.getDescription())) {
FooterPreference descPref = new FooterPreference(getPrefContext());
@@ -175,6 +214,7 @@
private void setupVibrate() {
mVibrate = (RestrictedSwitchPreference) findPreference(KEY_VIBRATE);
mVibrate.setDisabledByAdmin(mSuspendedAppsAdmin);
+ mVibrate.setEnabled(!(mAppRow.lockedImportance || mVibrate.isDisabledByAdmin()));
mVibrate.setChecked(mChannel.shouldVibrate());
mVibrate.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
@@ -191,6 +231,7 @@
private void setupRingtone() {
mRingtone = (NotificationSoundPreference) findPreference(KEY_RINGTONE);
mRingtone.setRingtone(mChannel.getSound());
+ mRingtone.setEnabled(!(mAppRow.lockedImportance));
mRingtone.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
@@ -246,12 +287,15 @@
channelArgs.putBoolean(AppHeader.EXTRA_HIDE_INFO_BUTTON, true);
channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg);
channelArgs.putString(Settings.EXTRA_CHANNEL_ID, mChannel.getId());
- Intent channelIntent = Utils.onBuildStartFragmentIntent(getActivity(),
- ChannelImportanceSettings.class.getName(),
- channelArgs, null, R.string.notification_importance_title, null,
- false, getMetricsCategory());
- mImportance.setIntent(channelIntent);
- mImportance.setEnabled(mSuspendedAppsAdmin == null);
+ mImportance.setEnabled(mSuspendedAppsAdmin == null && !mAppRow.lockedImportance);
+ // Set up intent to show importance selection only if this setting is enabled.
+ if (mImportance.isEnabled()) {
+ Intent channelIntent = Utils.onBuildStartFragmentIntent(getActivity(),
+ ChannelImportanceSettings.class.getName(),
+ channelArgs, null, R.string.notification_importance_title, null,
+ false, getMetricsCategory());
+ mImportance.setIntent(channelIntent);
+ }
mImportance.setSummary(getImportanceSummary(mChannel.getImportance()));
}
@@ -345,7 +389,7 @@
if (mAppLink != null) {
setVisible(mAppLink, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
}
- if (mFooter !=null) {
+ if (mFooter != null) {
setVisible(mFooter, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
}
}
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index 84d7e31..96737db 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -122,6 +122,19 @@
}
}
+
+ public NotificationChannelGroup getGroup(String groupId, String pkg, int uid) {
+ if (groupId == null) {
+ return null;
+ }
+ try {
+ return sINM.getNotificationChannelGroupForPackage(groupId, pkg, uid);
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ return null;
+ }
+ }
+
public ParceledListSlice<NotificationChannelGroup> getChannelGroups(String pkg, int uid) {
try {
return sINM.getNotificationChannelGroupsForPackage(pkg, uid, false);
diff --git a/src/com/android/settings/notification/NotificationSettingsBase.java b/src/com/android/settings/notification/NotificationSettingsBase.java
index bfa9355..717cf08 100644
--- a/src/com/android/settings/notification/NotificationSettingsBase.java
+++ b/src/com/android/settings/notification/NotificationSettingsBase.java
@@ -263,7 +263,7 @@
}
protected void addAppLinkPref() {
- if (mAppRow.settingsIntent != null) {
+ if (mAppRow.settingsIntent != null && mAppLink == null) {
mAppLink = new Preference(getPrefContext());
mAppLink.setKey(KEY_APP_LINK);
mAppLink.setOrder(500);
@@ -293,6 +293,8 @@
private void setupImportanceToggle() {
mImportanceToggle = (RestrictedSwitchPreference) findPreference(KEY_ALLOW_SOUND);
mImportanceToggle.setDisabledByAdmin(mSuspendedAppsAdmin);
+ mImportanceToggle.setEnabled(!(mAppRow.lockedImportance
+ || mImportanceToggle.isDisabledByAdmin()));
mImportanceToggle.setChecked(mChannel.getImportance() >= IMPORTANCE_DEFAULT
|| mChannel.getImportance() == IMPORTANCE_UNSPECIFIED);
mImportanceToggle.setOnPreferenceChangeListener(
@@ -313,6 +315,7 @@
protected void setupPriorityPref(boolean priority) {
mPriority = (RestrictedSwitchPreference) findPreference(KEY_BYPASS_DND);
mPriority.setDisabledByAdmin(mSuspendedAppsAdmin);
+ mPriority.setEnabled(!(mAppRow.lockedImportance || mPriority.isDisabledByAdmin()));
mPriority.setChecked(priority);
mPriority.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
diff --git a/src/com/android/settings/notification/VolumeSeekBarPreference.java b/src/com/android/settings/notification/VolumeSeekBarPreference.java
index 8a51961..b4fe6d8 100644
--- a/src/com/android/settings/notification/VolumeSeekBarPreference.java
+++ b/src/com/android/settings/notification/VolumeSeekBarPreference.java
@@ -91,9 +91,10 @@
mStopped = true;
if (mVolumizer != null) {
mVolumizer.stop();
+ mVolumizer = null;
}
}
-
+
@Override
public void onBindViewHolder(PreferenceViewHolder view) {
super.onBindViewHolder(view);
diff --git a/src/com/android/settings/wifi/ConnectivityManagerWrapper.java b/src/com/android/settings/wifi/ConnectivityManagerWrapper.java
new file mode 100644
index 0000000..7317666
--- /dev/null
+++ b/src/com/android/settings/wifi/ConnectivityManagerWrapper.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 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.wifi;
+
+import android.net.ConnectivityManager;
+
+/**
+ * Wrapper around {@link ConnectivityManager} to facilitate unit testing.
+ */
+public class ConnectivityManagerWrapper {
+ private final ConnectivityManager mConnectivityManager;
+
+ public ConnectivityManagerWrapper(ConnectivityManager connectivityManager) {
+ mConnectivityManager = connectivityManager;
+ }
+
+ /**
+ * {@link ConnectivityManager#stopTethering}
+ */
+ public void stopTethering(int type) {
+ mConnectivityManager.stopTethering(type);
+ }
+}
diff --git a/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java b/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java
index 1a60256..bf67565 100644
--- a/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java
+++ b/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java
@@ -38,7 +38,7 @@
import com.android.settings.search.SearchIndexableRaw;
import com.android.settingslib.wifi.AccessPoint;
import com.android.settingslib.wifi.AccessPointPreference;
-import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiSavedConfigUtils;
import java.util.ArrayList;
import java.util.Arrays;
@@ -124,7 +124,8 @@
PreferenceScreen preferenceScreen = getPreferenceScreen();
final Context context = getPrefContext();
- final List<AccessPoint> accessPoints = getSavedConfigs(context, mWifiManager);
+ final List<AccessPoint> accessPoints =
+ WifiSavedConfigUtils.getAllConfigs(context, mWifiManager);
Collections.sort(accessPoints, SAVED_NETWORK_COMPARATOR);
preferenceScreen.removeAll();
@@ -142,39 +143,6 @@
}
}
- /**
- * Retrieved the list of saved network configurations from {@link WifiManager}.
- * Each configuration is represented by {@link AccessPoint}.
- *
- * @param context The application context
- * @param wifiManager An instance of {@link WifiManager}
- * @return List of {@link AccessPoint}
- */
- private static List<AccessPoint> getSavedConfigs(Context context, WifiManager wifiManager) {
- List<AccessPoint> savedConfigs = new ArrayList<>();
- List<WifiConfiguration> savedNetworks = wifiManager.getConfiguredNetworks();
- for (WifiConfiguration network : savedNetworks) {
- // Configuration for Passpoint network is configured temporary by WifiService for
- // connection attempt only. The underlying configuration is saved as Passpoint
- // configuration, which will be retrieved with WifiManager#getPasspointConfiguration
- // call below.
- if (network.isPasspoint()) {
- continue;
- }
- savedConfigs.add(new AccessPoint(context, network));
- }
- try {
- List<PasspointConfiguration> savedPasspointConfigs =
- wifiManager.getPasspointConfigurations();
- for (PasspointConfiguration config : savedPasspointConfigs) {
- savedConfigs.add(new AccessPoint(context, config));
- }
- } catch (UnsupportedOperationException e) {
- // Passpoint not supported.
- }
- return savedConfigs;
- }
-
private void showDialog(LongPressAccessPointPreference accessPoint, boolean edit) {
if (mDialog != null) {
removeDialog(WifiSettings.WIFI_DIALOG_ID);
@@ -292,8 +260,8 @@
result.add(data);
// Add available Wi-Fi access points
- final List<AccessPoint> accessPoints =
- getSavedConfigs(context, context.getSystemService(WifiManager.class));
+ final List<AccessPoint> accessPoints = WifiSavedConfigUtils.getAllConfigs(
+ context, context.getSystemService(WifiManager.class));
final int accessPointsSize = accessPoints.size();
for (int i = 0; i < accessPointsSize; ++i){
diff --git a/src/com/android/settings/wifi/WifiDetailPreference.java b/src/com/android/settings/wifi/WifiDetailPreference.java
index 6d34ad1..b62df56 100644
--- a/src/com/android/settings/wifi/WifiDetailPreference.java
+++ b/src/com/android/settings/wifi/WifiDetailPreference.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.TextView;
@@ -37,6 +38,7 @@
}
public void setDetailText(String text) {
+ if (TextUtils.equals(mDetailText, text)) return;
mDetailText = text;
notifyChanged();
}
diff --git a/src/com/android/settings/wifi/WifiEnabler.java b/src/com/android/settings/wifi/WifiEnabler.java
index abab154..7718898 100644
--- a/src/com/android/settings/wifi/WifiEnabler.java
+++ b/src/com/android/settings/wifi/WifiEnabler.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiInfo;
@@ -27,6 +28,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
import android.widget.Toast;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -43,6 +45,7 @@
private final SwitchWidgetController mSwitchWidget;
private final WifiManager mWifiManager;
+ private final ConnectivityManagerWrapper mConnectivityManager;
private final MetricsFeatureProvider mMetricsFeatureProvider;
private Context mContext;
@@ -76,12 +79,21 @@
private static final int EVENT_UPDATE_INDEX = 0;
public WifiEnabler(Context context, SwitchWidgetController switchWidget,
- MetricsFeatureProvider metricsFeatureProvider) {
+ MetricsFeatureProvider metricsFeatureProvider) {
+ this(context, switchWidget, metricsFeatureProvider, new ConnectivityManagerWrapper(
+ (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)));
+ }
+
+ @VisibleForTesting
+ WifiEnabler(Context context, SwitchWidgetController switchWidget,
+ MetricsFeatureProvider metricsFeatureProvider,
+ ConnectivityManagerWrapper connectivityManagerWrapper) {
mContext = context;
mSwitchWidget = switchWidget;
mSwitchWidget.setListener(this);
mMetricsFeatureProvider = metricsFeatureProvider;
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ mConnectivityManager = connectivityManagerWrapper;
mIntentFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
// The order matters! We really should not depend on this. :(
@@ -198,7 +210,7 @@
// Disable tethering if enabling Wifi
if (mayDisableTethering(isChecked)) {
- mWifiManager.setWifiApEnabled(null, false);
+ mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
}
if (isChecked) {
mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_WIFI_ON);
diff --git a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
index b3f83b3..0d585c7 100644
--- a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
+++ b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
@@ -16,6 +16,7 @@
package com.android.settings.wifi.details;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import android.app.Fragment;
@@ -27,6 +28,7 @@
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.IpPrefix;
+import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkBadging;
@@ -67,6 +69,7 @@
import java.net.UnknownHostException;
import java.util.List;
import java.util.StringJoiner;
+import java.util.stream.Collectors;
/**
* Controller for logic pertaining to displaying Wifi information for the
@@ -100,7 +103,9 @@
@VisibleForTesting
static final String KEY_DNS_PREF = "dns";
@VisibleForTesting
- static final String KEY_IPV6_ADDRESS_CATEGORY = "ipv6_details_category";
+ static final String KEY_IPV6_CATEGORY = "ipv6_category";
+ @VisibleForTesting
+ static final String KEY_IPV6_ADDRESSES_PREF = "ipv6_addresses";
private AccessPoint mAccessPoint;
private final ConnectivityManagerWrapper mConnectivityManagerWrapper;
@@ -133,8 +138,9 @@
private WifiDetailPreference mGatewayPref;
private WifiDetailPreference mSubnetPref;
private WifiDetailPreference mDnsPref;
+ private PreferenceCategory mIpv6Category;
+ private Preference mIpv6AddressPref;
- private PreferenceCategory mIpv6AddressCategory;
private final IntentFilter mFilter;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@@ -160,9 +166,24 @@
}
}
+ private boolean hasCapabilityChanged(NetworkCapabilities nc, int cap) {
+ // If this is the first time we get NetworkCapabilities, report that something changed.
+ if (mNetworkCapabilities == null) return true;
+
+ // nc can never be null, see ConnectivityService#callCallbackForRequest.
+ return mNetworkCapabilities.hasCapability(cap) != nc.hasCapability(cap);
+ }
+
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
+ // If the network just validated or lost Internet access, refresh network state.
+ // Don't do this on every NetworkCapabilities change because refreshNetworkState
+ // sends IPCs to the system server from the UI thread, which can cause jank.
if (network.equals(mNetwork) && !nc.equals(mNetworkCapabilities)) {
+ if (hasCapabilityChanged(nc, NET_CAPABILITY_VALIDATED) ||
+ hasCapabilityChanged(nc, NET_CAPABILITY_CAPTIVE_PORTAL)) {
+ refreshNetworkState();
+ }
mNetworkCapabilities = nc;
updateIpLayerInfo();
}
@@ -241,8 +262,8 @@
mSubnetPref = (WifiDetailPreference) screen.findPreference(KEY_SUBNET_MASK_PREF);
mDnsPref = (WifiDetailPreference) screen.findPreference(KEY_DNS_PREF);
- mIpv6AddressCategory =
- (PreferenceCategory) screen.findPreference(KEY_IPV6_ADDRESS_CATEGORY);
+ mIpv6Category = (PreferenceCategory) screen.findPreference(KEY_IPV6_CATEGORY);
+ mIpv6AddressPref = (Preference) screen.findPreference(KEY_IPV6_ADDRESSES_PREF);
mSecurityPref.setDetailText(mAccessPoint.getSecurityString(false /* concise */));
mForgetButton = (Button) mButtonsPref.findViewById(R.id.forget_button);
@@ -315,8 +336,6 @@
mFrequencyPref.setDetailText(band);
updateIpLayerInfo();
- mButtonsPref.setVisible(mForgetButton.getVisibility() == View.VISIBLE
- || mSignInButton.getVisibility() == View.VISIBLE);
}
private void exitActivity() {
@@ -348,74 +367,69 @@
mSignalStrengthPref.setDetailText(mSignalStr[summarySignalLevel]);
}
+ private void updatePreference(WifiDetailPreference pref, String detailText) {
+ if (!TextUtils.isEmpty(detailText)) {
+ pref.setDetailText(detailText);
+ pref.setVisible(true);
+ } else {
+ pref.setVisible(false);
+ }
+ }
+
private void updateIpLayerInfo() {
mSignInButton.setVisibility(canSignIntoNetwork() ? View.VISIBLE : View.INVISIBLE);
-
- // Reset all fields
- mIpv6AddressCategory.removeAll();
- mIpv6AddressCategory.setVisible(false);
- mIpAddressPref.setVisible(false);
- mSubnetPref.setVisible(false);
- mGatewayPref.setVisible(false);
- mDnsPref.setVisible(false);
+ mButtonsPref.setVisible(mForgetButton.getVisibility() == View.VISIBLE
+ || mSignInButton.getVisibility() == View.VISIBLE);
if (mNetwork == null || mLinkProperties == null) {
+ mIpAddressPref.setVisible(false);
+ mSubnetPref.setVisible(false);
+ mGatewayPref.setVisible(false);
+ mDnsPref.setVisible(false);
+ mIpv6Category.setVisible(false);
return;
}
- List<InetAddress> addresses = mLinkProperties.getAddresses();
- // Set IPv4 and IPv6 addresses
- for (int i = 0; i < addresses.size(); i++) {
- InetAddress addr = addresses.get(i);
- if (addr instanceof Inet4Address) {
- mIpAddressPref.setDetailText(addr.getHostAddress());
- mIpAddressPref.setVisible(true);
- } else if (addr instanceof Inet6Address) {
- String ip = addr.getHostAddress();
- Preference pref = new Preference(mPrefContext);
- pref.setKey(ip);
- pref.setTitle(ip);
- pref.setSelectable(false);
- mIpv6AddressCategory.addPreference(pref);
- mIpv6AddressCategory.setVisible(true);
- }
- }
-
- // Set up IPv4 gateway and subnet mask
- String gateway = null;
+ // Find IPv4 and IPv6 addresses.
+ String ipv4Address = null;
String subnet = null;
+ StringJoiner ipv6Addresses = new StringJoiner("\n");
+
+ for (LinkAddress addr : mLinkProperties.getLinkAddresses()) {
+ if (addr.getAddress() instanceof Inet4Address) {
+ ipv4Address = addr.getAddress().getHostAddress();
+ subnet = ipv4PrefixLengthToSubnetMask(addr.getPrefixLength());
+ } else if (addr.getAddress() instanceof Inet6Address) {
+ ipv6Addresses.add(addr.getAddress().getHostAddress());
+ }
+ }
+
+ // Find IPv4 default gateway.
+ String gateway = null;
for (RouteInfo routeInfo : mLinkProperties.getRoutes()) {
- if (routeInfo.hasGateway() && routeInfo.getGateway() instanceof Inet4Address) {
+ if (routeInfo.isIPv4Default() && routeInfo.hasGateway()) {
gateway = routeInfo.getGateway().getHostAddress();
- }
- IpPrefix ipPrefix = routeInfo.getDestination();
- if (ipPrefix != null && ipPrefix.getAddress() instanceof Inet4Address
- && ipPrefix.getPrefixLength() > 0) {
- subnet = ipv4PrefixLengthToSubnetMask(ipPrefix.getPrefixLength());
+ break;
}
}
- if (!TextUtils.isEmpty(subnet)) {
- mSubnetPref.setDetailText(subnet);
- mSubnetPref.setVisible(true);
- }
+ // Find IPv4 DNS addresses.
+ String dnsServers = mLinkProperties.getDnsServers().stream()
+ .filter(Inet4Address.class::isInstance)
+ .map(InetAddress::getHostAddress)
+ .collect(Collectors.joining(","));
- if (!TextUtils.isEmpty(gateway)) {
- mGatewayPref.setDetailText(gateway);
- mGatewayPref.setVisible(true);
- }
+ // Update UI.
+ updatePreference(mIpAddressPref, ipv4Address);
+ updatePreference(mSubnetPref, subnet);
+ updatePreference(mGatewayPref, gateway);
+ updatePreference(mDnsPref, dnsServers);
- // Set IPv4 DNS addresses
- StringJoiner stringJoiner = new StringJoiner(",");
- for (InetAddress dnsServer : mLinkProperties.getDnsServers()) {
- if (dnsServer instanceof Inet4Address) {
- stringJoiner.add(dnsServer.getHostAddress());
- }
- }
- String dnsText = stringJoiner.toString();
- if (!dnsText.isEmpty()) {
- mDnsPref.setDetailText(dnsText);
- mDnsPref.setVisible(true);
+ if (ipv6Addresses.length() > 0) {
+ mIpv6AddressPref.setSummary(ipv6Addresses.toString());
+ mIpv6Category.setVisible(true);
+ } else {
+ mIpv6Category.setVisible(false);
}
}
diff --git a/tests/robotests/src/com/android/settings/accounts/AutoSyncWorkDataPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/AutoSyncWorkDataPreferenceControllerTest.java
index d93d3a7..cd66d5e 100644
--- a/tests/robotests/src/com/android/settings/accounts/AutoSyncWorkDataPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/AutoSyncWorkDataPreferenceControllerTest.java
@@ -21,6 +21,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;
import android.app.Fragment;
@@ -46,6 +47,8 @@
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class AutoSyncWorkDataPreferenceControllerTest {
+ private static int MANAGED_PROFILE_ID = 10;
+
@Mock(answer = RETURNS_DEEP_STUBS)
private UserManager mUserManager;
@Mock(answer = RETURNS_DEEP_STUBS)
@@ -80,29 +83,30 @@
@Test
public void checkIsAvailable_singleUserProfile_shouldNotDisplay() {
- final List<UserInfo> infos = new ArrayList<>();
- infos.add(new UserInfo(1, "user 1", 0));
when(mUserManager.isManagedProfile()).thenReturn(false);
when(mUserManager.isLinkedUser()).thenReturn(false);
- when(mUserManager.getProfiles(anyInt())).thenReturn(infos);
+
+ final List<UserInfo> infos = new ArrayList<>();
+ infos.add(new UserInfo(UserHandle.USER_SYSTEM, "user 1", 0 /* flags */));
+ when(mUserManager.getProfiles(eq(UserHandle.USER_SYSTEM))).thenReturn(infos);
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void multipleProfile_shouldInitWithWorkProfileUserHandle() {
- final int id1 = 1;
- final int id2 = 2;
- final UserInfo managedUser = new UserInfo(id2, "user 2", FLAG_MANAGED_PROFILE);
- final List<UserHandle> infos = new ArrayList<>();
- infos.add(new UserHandle(id1));
- infos.add(new UserHandle(id2));
- when(mUserManager.getUserProfiles()).thenReturn(infos);
- when(mUserManager.getUserHandle()).thenReturn(id1);
- when(mUserManager.getUserInfo(id2)).thenReturn(managedUser);
+ when(mUserManager.isManagedProfile()).thenReturn(false);
+ when(mUserManager.isLinkedUser()).thenReturn(false);
+
+ final List<UserInfo> infos = new ArrayList<>();
+ infos.add(new UserInfo(UserHandle.USER_SYSTEM, "user 1", 0 /* flags */));
+ infos.add(new UserInfo(
+ MANAGED_PROFILE_ID, "work profile", UserInfo.FLAG_MANAGED_PROFILE));
+ when(mUserManager.getProfiles(eq(UserHandle.USER_SYSTEM))).thenReturn(infos);
mController = new AutoSyncWorkDataPreferenceController(mContext, mFragment);
- assertThat(mController.mUserHandle.getIdentifier()).isEqualTo(id2);
+ assertThat(mController.mUserHandle.getIdentifier()).isEqualTo(MANAGED_PROFILE_ID);
+ assertThat(mController.isAvailable()).isTrue();
}
}
diff --git a/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java b/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java
index 98c28bf..2fbad13 100644
--- a/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java
+++ b/tests/robotests/src/com/android/settings/applications/InstalledAppDetailsTest.java
@@ -17,6 +17,7 @@
package com.android.settings.applications;
import android.app.AlertDialog;
+import android.app.LoaderManager;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
@@ -25,6 +26,7 @@
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.BatteryStats;
+import android.os.Bundle;
import android.os.UserManager;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceManager;
@@ -107,8 +109,10 @@
private PackageManager mPackageManager;
@Mock
private BatteryUtils mBatteryUtils;
- private FakeFeatureFactory mFeatureFactory;
+ @Mock
+ private LoaderManager mLoaderManager;
+ private FakeFeatureFactory mFeatureFactory;
private InstalledAppDetails mAppDetail;
private Context mShadowContext;
private Preference mBatteryPreference;
@@ -528,6 +532,16 @@
verify(button).setText(R.string.disable_text);
}
+ @Test
+ public void testRestartBatteryStatsLoader() {
+ doReturn(mLoaderManager).when(mAppDetail).getLoaderManager();
+
+ mAppDetail.restartBatteryStatsLoader();
+
+ verify(mLoaderManager).restartLoader(InstalledAppDetails.LOADER_BATTERY, Bundle.EMPTY,
+ mAppDetail.mBatteryCallbacks);
+ }
+
@Implements(Utils.class)
public static class ShadowUtils {
@Implementation
diff --git a/tests/robotests/src/com/android/settings/core/DynamicAvailabilityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/core/DynamicAvailabilityPreferenceControllerTest.java
index 38a8356..399c753 100644
--- a/tests/robotests/src/com/android/settings/core/DynamicAvailabilityPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/core/DynamicAvailabilityPreferenceControllerTest.java
@@ -22,6 +22,7 @@
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
+import com.android.settings.core.PreferenceAvailabilityObserver;
import com.android.settings.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -50,6 +51,7 @@
private @Mock Preference mPreference;
private @Mock PreferenceScreen mScreen;
private @Mock Lifecycle mLifecycle;
+ private @Mock PreferenceAvailabilityObserver mObserver;
private boolean mIsAvailable;
private Preference mUpdatedPreference = null;
@@ -115,6 +117,21 @@
assertThat(mUpdatedPreference).isEqualTo(mPreference);
}
+ @Test
+ public void testNotifyOnAvailabilityUpdate() {
+ final DynamicAvailabilityPreferenceController controller
+ = new DynamicAvailabilityPreferenceControllerTestable(mLifecycle);
+ controller.setAvailabilityObserver(mObserver);
+ assertThat(controller.getAvailabilityObserver()).isEqualTo(mObserver);
+
+ mIsAvailable = false;
+ controller.isAvailable();
+ verify(mObserver).onPreferenceAvailabilityUpdated(PREFERENCE_KEY, false);
+
+ mIsAvailable = true;
+ controller.isAvailable();
+ verify(mObserver).onPreferenceAvailabilityUpdated(PREFERENCE_KEY, true);
+ }
private class DynamicAvailabilityPreferenceControllerTestable
extends DynamicAvailabilityPreferenceController {
@@ -124,6 +141,7 @@
@Override
public boolean isAvailable() {
+ notifyOnAvailabilityUpdate(mIsAvailable);
return mIsAvailable;
}
diff --git a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerTestBase.java b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerTestBase.java
index 96ce081..c1a3143 100644
--- a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerTestBase.java
+++ b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerTestBase.java
@@ -21,6 +21,7 @@
import com.android.settings.R;
import com.android.settings.applications.ApplicationFeatureProvider;
+import com.android.settings.core.PreferenceAvailabilityObserver;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
@@ -32,9 +33,12 @@
import org.mockito.stubbing.Answer;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
@@ -48,6 +52,7 @@
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
protected Context mContext;
private FakeFeatureFactory mFeatureFactory;
+ @Mock private PreferenceAvailabilityObserver mObserver;
protected AdminGrantedPermissionsPreferenceControllerBase mController;
@@ -64,6 +69,12 @@
FakeFeatureFactory.setupForTest(mContext);
mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
mController = createController(true /* async */);
+ mController.setAvailabilityObserver(mObserver);
+ }
+
+ @Test
+ public void testGetAvailabilityObserver() {
+ assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
}
private void setNumberOfPackagesWithAdminGrantedPermissions(int number, boolean async) {
@@ -85,6 +96,7 @@
setNumberOfPackagesWithAdminGrantedPermissions(0, true /* async */);
mController.updateState(preference);
assertThat(preference.isVisible()).isFalse();
+ verify(mObserver).onPreferenceAvailabilityUpdated(mKey, false);
setNumberOfPackagesWithAdminGrantedPermissions(20, true /* async */);
when(mContext.getResources().getQuantityString(
@@ -93,27 +105,33 @@
mController.updateState(preference);
assertThat(preference.getSummary()).isEqualTo("minimum 20 apps");
assertThat(preference.isVisible()).isTrue();
+ verify(mObserver).onPreferenceAvailabilityUpdated(mKey, true);
}
@Test
public void testIsAvailableSync() {
final AdminGrantedPermissionsPreferenceControllerBase controller
= createController(false /* async */);
+ controller.setAvailabilityObserver(mObserver);
setNumberOfPackagesWithAdminGrantedPermissions(0, false /* async */);
assertThat(controller.isAvailable()).isFalse();
+ verify(mObserver).onPreferenceAvailabilityUpdated(mKey, false);
setNumberOfPackagesWithAdminGrantedPermissions(20, false /* async */);
assertThat(controller.isAvailable()).isTrue();
+ verify(mObserver).onPreferenceAvailabilityUpdated(mKey, true);
}
@Test
public void testIsAvailableAsync() {
setNumberOfPackagesWithAdminGrantedPermissions(0, true /* async */);
assertThat(mController.isAvailable()).isTrue();
+ verify(mObserver, never()).onPreferenceAvailabilityUpdated(eq(mKey), anyBoolean());
setNumberOfPackagesWithAdminGrantedPermissions(20, true /* async */);
assertThat(mController.isAvailable()).isTrue();
+ verify(mObserver, never()).onPreferenceAvailabilityUpdated(eq(mKey), anyBoolean());
}
@Test
diff --git a/tests/robotests/src/com/android/settings/enterprise/AlwaysOnVpnCurrentUserPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/AlwaysOnVpnCurrentUserPreferenceControllerTest.java
index 9d1bd58..51c8a7b 100644
--- a/tests/robotests/src/com/android/settings/enterprise/AlwaysOnVpnCurrentUserPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/AlwaysOnVpnCurrentUserPreferenceControllerTest.java
@@ -22,6 +22,7 @@
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
+import com.android.settings.core.PreferenceAvailabilityObserver;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
@@ -33,6 +34,7 @@
import org.robolectric.annotation.Config;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
@@ -42,12 +44,14 @@
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public final class AlwaysOnVpnCurrentUserPreferenceControllerTest {
- private final String VPN_SET_DEVICE = "VPN set";
- private final String VPN_SET_PERSONAL = "VPN set in personal profile";
+ private static final String VPN_SET_DEVICE = "VPN set";
+ private static final String VPN_SET_PERSONAL = "VPN set in personal profile";
+ private static final String KEY_ALWAYS_ON_VPN_PRIMARY_USER = "always_on_vpn_primary_user";
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
private FakeFeatureFactory mFeatureFactory;
+ @Mock private PreferenceAvailabilityObserver mObserver;
private AlwaysOnVpnCurrentUserPreferenceController mController;
@@ -62,6 +66,12 @@
.thenReturn(VPN_SET_DEVICE);
when(mContext.getString(R.string.enterprise_privacy_always_on_vpn_personal))
.thenReturn(VPN_SET_PERSONAL);
+ mController.setAvailabilityObserver(mObserver);
+ }
+
+ @Test
+ public void testGetAvailabilityObserver() {
+ assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
}
@Test
@@ -85,10 +95,12 @@
when(mFeatureFactory.enterprisePrivacyFeatureProvider.isAlwaysOnVpnSetInCurrentUser())
.thenReturn(false);
assertThat(mController.isAvailable()).isFalse();
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_ALWAYS_ON_VPN_PRIMARY_USER, false);
when(mFeatureFactory.enterprisePrivacyFeatureProvider.isAlwaysOnVpnSetInCurrentUser())
.thenReturn(true);
assertThat(mController.isAvailable()).isTrue();
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_ALWAYS_ON_VPN_PRIMARY_USER, true);
}
@Test
@@ -99,6 +111,6 @@
@Test
public void testGetPreferenceKey() {
- assertThat(mController.getPreferenceKey()).isEqualTo("always_on_vpn_primary_user");
+ assertThat(mController.getPreferenceKey()).isEqualTo(KEY_ALWAYS_ON_VPN_PRIMARY_USER);
}
}
diff --git a/tests/robotests/src/com/android/settings/enterprise/AlwaysOnVpnManagedProfilePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/AlwaysOnVpnManagedProfilePreferenceControllerTest.java
index 8ac10d1..4562711 100644
--- a/tests/robotests/src/com/android/settings/enterprise/AlwaysOnVpnManagedProfilePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/AlwaysOnVpnManagedProfilePreferenceControllerTest.java
@@ -21,6 +21,7 @@
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
+import com.android.settings.core.PreferenceAvailabilityObserver;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
@@ -32,6 +33,7 @@
import org.robolectric.annotation.Config;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
@@ -41,9 +43,12 @@
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public final class AlwaysOnVpnManagedProfilePreferenceControllerTest {
+ private static final String KEY_ALWAYS_ON_VPN_MANAGED_PROFILE = "always_on_vpn_managed_profile";
+
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
private FakeFeatureFactory mFeatureFactory;
+ @Mock private PreferenceAvailabilityObserver mObserver;
private AlwaysOnVpnManagedProfilePreferenceController mController;
@@ -54,6 +59,12 @@
mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
mController = new AlwaysOnVpnManagedProfilePreferenceController(mContext,
null /* lifecycle */);
+ mController.setAvailabilityObserver(mObserver);
+ }
+
+ @Test
+ public void testGetAvailabilityObserver() {
+ assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
}
@Test
@@ -61,10 +72,12 @@
when(mFeatureFactory.enterprisePrivacyFeatureProvider.isAlwaysOnVpnSetInManagedProfile())
.thenReturn(false);
assertThat(mController.isAvailable()).isFalse();
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_ALWAYS_ON_VPN_MANAGED_PROFILE, false);
when(mFeatureFactory.enterprisePrivacyFeatureProvider.isAlwaysOnVpnSetInManagedProfile())
.thenReturn(true);
assertThat(mController.isAvailable()).isTrue();
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_ALWAYS_ON_VPN_MANAGED_PROFILE, true);
}
@Test
@@ -75,6 +88,6 @@
@Test
public void testGetPreferenceKey() {
- assertThat(mController.getPreferenceKey()).isEqualTo("always_on_vpn_managed_profile");
+ assertThat(mController.getPreferenceKey()).isEqualTo(KEY_ALWAYS_ON_VPN_MANAGED_PROFILE);
}
}
diff --git a/tests/robotests/src/com/android/settings/enterprise/CaCertsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/CaCertsPreferenceControllerTest.java
index fef2e0f..2c0e320 100644
--- a/tests/robotests/src/com/android/settings/enterprise/CaCertsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/CaCertsPreferenceControllerTest.java
@@ -23,6 +23,7 @@
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
+import com.android.settings.core.PreferenceAvailabilityObserver;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
@@ -34,6 +35,7 @@
import org.robolectric.annotation.Config;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
@@ -43,9 +45,12 @@
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public final class CaCertsPreferenceControllerTest {
+ private static final String KEY_CA_CERTS = "ca_certs";
+
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
private FakeFeatureFactory mFeatureFactory;
+ @Mock private PreferenceAvailabilityObserver mObserver;
private CaCertsPreferenceController mController;
@@ -55,6 +60,12 @@
FakeFeatureFactory.setupForTest(mContext);
mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
mController = new CaCertsPreferenceController(mContext, null /* lifecycle */);
+ mController.setAvailabilityObserver(mObserver);
+ }
+
+ @Test
+ public void testGetAvailabilityObserver() {
+ assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
}
@Test
@@ -74,10 +85,12 @@
when(mFeatureFactory.enterprisePrivacyFeatureProvider
.getNumberOfOwnerInstalledCaCertsForCurrentUserAndManagedProfile()).thenReturn(0);
assertThat(mController.isAvailable()).isFalse();
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_CA_CERTS, false);
when(mFeatureFactory.enterprisePrivacyFeatureProvider
.getNumberOfOwnerInstalledCaCertsForCurrentUserAndManagedProfile()).thenReturn(10);
assertThat(mController.isAvailable()).isTrue();
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_CA_CERTS, true);
}
@Test
@@ -88,6 +101,6 @@
@Test
public void testGetPreferenceKey() {
- assertThat(mController.getPreferenceKey()).isEqualTo("ca_certs");
+ assertThat(mController.getPreferenceKey()).isEqualTo(KEY_CA_CERTS);
}
}
diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceControllerTest.java
index 4255d96..cf54bb0 100644
--- a/tests/robotests/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceControllerTest.java
@@ -24,6 +24,7 @@
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.applications.ApplicationFeatureProvider;
+import com.android.settings.core.PreferenceAvailabilityObserver;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
@@ -37,9 +38,12 @@
import org.robolectric.annotation.Config;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyObject;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
@@ -49,9 +53,13 @@
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public final class EnterpriseInstalledPackagesPreferenceControllerTest {
+ private static final String KEY_NUMBER_ENTERPRISE_INSTALLED_PACKAGES
+ = "number_enterprise_installed_packages";
+
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
private FakeFeatureFactory mFeatureFactory;
+ @Mock private PreferenceAvailabilityObserver mObserver;
private EnterpriseInstalledPackagesPreferenceController mController;
@@ -62,6 +70,12 @@
mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
mController = new EnterpriseInstalledPackagesPreferenceController(mContext,
null /* lifecycle */, true /* async */);
+ mController.setAvailabilityObserver(mObserver);
+ }
+
+ @Test
+ public void testGetAvailabilityObserver() {
+ assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
}
private void setNumberOfEnterpriseInstalledPackages(int number, boolean async) {
@@ -82,6 +96,8 @@
setNumberOfEnterpriseInstalledPackages(0, true /* async */);
mController.updateState(preference);
assertThat(preference.isVisible()).isFalse();
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_NUMBER_ENTERPRISE_INSTALLED_PACKAGES,
+ false);
setNumberOfEnterpriseInstalledPackages(20, true /* async */);
when(mContext.getResources().getQuantityString(
@@ -90,6 +106,8 @@
mController.updateState(preference);
assertThat(preference.getSummary()).isEqualTo("minimum 20 apps");
assertThat(preference.isVisible()).isTrue();
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_NUMBER_ENTERPRISE_INSTALLED_PACKAGES,
+ true);
}
@Test
@@ -97,21 +115,30 @@
final EnterpriseInstalledPackagesPreferenceController controller
= new EnterpriseInstalledPackagesPreferenceController(mContext,
null /* lifecycle */, false /* async */);
+ controller.setAvailabilityObserver(mObserver);
setNumberOfEnterpriseInstalledPackages(0, false /* async */);
assertThat(controller.isAvailable()).isFalse();
+ verify(mObserver).onPreferenceAvailabilityUpdated(
+ KEY_NUMBER_ENTERPRISE_INSTALLED_PACKAGES, false);
setNumberOfEnterpriseInstalledPackages(20, false /* async */);
assertThat(controller.isAvailable()).isTrue();
+ verify(mObserver).onPreferenceAvailabilityUpdated(
+ KEY_NUMBER_ENTERPRISE_INSTALLED_PACKAGES, true);
}
@Test
public void testIsAvailableAsync() {
setNumberOfEnterpriseInstalledPackages(0, true /* async */);
assertThat(mController.isAvailable()).isTrue();
+ verify(mObserver, never()).onPreferenceAvailabilityUpdated(
+ eq(KEY_NUMBER_ENTERPRISE_INSTALLED_PACKAGES), anyBoolean());
setNumberOfEnterpriseInstalledPackages(20, true /* async */);
assertThat(mController.isAvailable()).isTrue();
+ verify(mObserver, never()).onPreferenceAvailabilityUpdated(
+ eq(KEY_NUMBER_ENTERPRISE_INSTALLED_PACKAGES), anyBoolean());
}
@Test
@@ -123,6 +150,6 @@
@Test
public void testGetPreferenceKey() {
assertThat(mController.getPreferenceKey())
- .isEqualTo("number_enterprise_installed_packages");
+ .isEqualTo(KEY_NUMBER_ENTERPRISE_INSTALLED_PACKAGES);
}
}
diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceControllerTest.java
index fe48347..101a45a 100644
--- a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceControllerTest.java
@@ -23,6 +23,7 @@
import com.android.settings.R;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
+import com.android.settings.core.PreferenceAvailabilityObserver;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
@@ -34,6 +35,7 @@
import org.robolectric.annotation.Config;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
@@ -43,13 +45,15 @@
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public final class EnterprisePrivacyPreferenceControllerTest {
- private final String MANAGED_GENERIC = "managed by organization";
- private final String MANAGED_WITH_NAME = "managed by Foo, Inc.";
- private final String MANAGING_ORGANIZATION = "Foo, Inc.";
+ private static final String MANAGED_GENERIC = "managed by organization";
+ private static final String MANAGED_WITH_NAME = "managed by Foo, Inc.";
+ private static final String MANAGING_ORGANIZATION = "Foo, Inc.";
+ private static final String KEY_ENTERPRISE_PRIVACY = "enterprise_privacy";
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
private FakeFeatureFactory mFeatureFactory;
+ @Mock private PreferenceAvailabilityObserver mObserver;
private EnterprisePrivacyPreferenceController mController;
@@ -59,6 +63,12 @@
FakeFeatureFactory.setupForTest(mContext);
mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
mController = new EnterprisePrivacyPreferenceController(mContext, null /* lifecycle */);
+ mController.setAvailabilityObserver(mObserver);
+ }
+
+ @Test
+ public void testGetAvailabilityObserver() {
+ assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
}
@Test
@@ -85,10 +95,11 @@
public void testIsAvailable() {
when(mFeatureFactory.enterprisePrivacyFeatureProvider.hasDeviceOwner()).thenReturn(false);
assertThat(mController.isAvailable()).isFalse();
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_ENTERPRISE_PRIVACY, false);
when(mFeatureFactory.enterprisePrivacyFeatureProvider.hasDeviceOwner()).thenReturn(true);
assertThat(mController.isAvailable()).isTrue();
-
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_ENTERPRISE_PRIVACY, true);
}
@Test
@@ -99,6 +110,6 @@
@Test
public void testGetPreferenceKey() {
- assertThat(mController.getPreferenceKey()).isEqualTo("enterprise_privacy");
+ assertThat(mController.getPreferenceKey()).isEqualTo(KEY_ENTERPRISE_PRIVACY);
}
}
diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java
index d41be75..16fa5ba 100644
--- a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java
@@ -16,12 +16,16 @@
package com.android.settings.enterprise;
+import android.app.Application;
import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
+import com.android.settings.core.DynamicAvailabilityPreferenceController;
import com.android.settings.core.PreferenceController;
import com.android.settings.testutils.FakeFeatureFactory;
@@ -31,10 +35,14 @@
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
+import org.xmlpull.v1.XmlPullParser;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
@@ -46,6 +54,9 @@
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public final class EnterprisePrivacySettingsTest {
+ private final static String RESOURCES_NAMESPACE = "http://schemas.android.com/apk/res/android";
+ private final static String ATTR_KEY = "key";
+
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
private FakeFeatureFactory mFeatureFactory;
@@ -101,23 +112,24 @@
}
@Test
- public void getPreferenceControllers() {
+ public void getPreferenceControllers() throws Exception {
final List<PreferenceController> controllers = mSettings.getPreferenceControllers(
ShadowApplication.getInstance().getApplicationContext());
verifyPreferenceControllers(controllers);
}
@Test
- public void getSearchIndexProviderPreferenceControllers() {
+ public void getSearchIndexProviderPreferenceControllers() throws Exception {
final List<PreferenceController> controllers
= EnterprisePrivacySettings.SEARCH_INDEX_DATA_PROVIDER.getPreferenceControllers(
ShadowApplication.getInstance().getApplicationContext());
verifyPreferenceControllers(controllers);
}
- private void verifyPreferenceControllers(List<PreferenceController> controllers) {
+ private void verifyPreferenceControllers(List<PreferenceController> controllers)
+ throws Exception {
assertThat(controllers).isNotNull();
- assertThat(controllers.size()).isEqualTo(15);
+ assertThat(controllers.size()).isEqualTo(16);
int position = 0;
assertThat(controllers.get(position++)).isInstanceOf(NetworkLogsPreferenceController.class);
assertThat(controllers.get(position++)).isInstanceOf(BugReportsPreferenceController.class);
@@ -137,14 +149,71 @@
AlwaysOnVpnCurrentUserPreferenceController.class);
assertThat(controllers.get(position++)).isInstanceOf(
AlwaysOnVpnManagedProfilePreferenceController.class);
+ assertThat(controllers.get(position++)).isInstanceOf(ImePreferenceController.class);
assertThat(controllers.get(position++)).isInstanceOf(
GlobalHttpProxyPreferenceController.class);
assertThat(controllers.get(position++)).isInstanceOf(
CaCertsPreferenceController.class);
+ final PreferenceController exposureChangesCategoryController = controllers.get(position);
+ final int exposureChangesCategoryControllerIndex = position;
+ assertThat(controllers.get(position++)).isInstanceOf(
+ ExposureChangesCategoryPreferenceController.class);
assertThat(controllers.get(position++)).isInstanceOf(
FailedPasswordWipeCurrentUserPreferenceController.class);
assertThat(controllers.get(position++)).isInstanceOf(
FailedPasswordWipeManagedProfilePreferenceController.class);
- assertThat(controllers.get(position++)).isInstanceOf(ImePreferenceController.class);
+
+ // The "Changes made by your organization's admin" category is hidden when all Preferences
+ // inside it become unavailable. To do this correctly, the category's controller must:
+ // a) Observe the availability of all Preferences in the category and
+ // b) Be listed after those Preferences' controllers, so that availability is updated in
+ // the correct order
+
+ // Find all Preferences in the category.
+ final XmlResourceParser parser = RuntimeEnvironment.application.getResources().getXml(
+ R.xml.enterprise_privacy_settings);
+ boolean done = false;
+ int type;
+ final Set<String> expectedObserved = new HashSet<>();
+ while (!done && (type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type != XmlPullParser.START_TAG || !"exposure_changes_category".equals(
+ parser.getAttributeValue(RESOURCES_NAMESPACE, ATTR_KEY))) {
+ continue;
+ }
+ int depth = 1;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type == XmlPullParser.START_TAG) {
+ final String key = parser.getAttributeValue(RESOURCES_NAMESPACE, ATTR_KEY);
+ if (key != null) {
+ expectedObserved.add(key);
+ }
+ depth++;
+ } else if (type == XmlPullParser.END_TAG) {
+ depth--;
+ if (depth == 0) {
+ done = true;
+ break;
+ }
+ }
+ }
+ }
+
+ // Find all Preferences the category's controller is observing.
+ final Set<String> actualObserved = new HashSet<>();
+ int maxObservedIndex = -1;
+ for (int i = 0; i < controllers.size(); i++) {
+ final PreferenceController controller = controllers.get(i);
+ if (controller instanceof DynamicAvailabilityPreferenceController &&
+ ((DynamicAvailabilityPreferenceController) controller).getAvailabilityObserver()
+ == exposureChangesCategoryController) {
+ actualObserved.add(controller.getPreferenceKey());
+ maxObservedIndex = i;
+ }
+ }
+
+ // Verify that the category's controller is observing the Preferences inside it.
+ assertThat(actualObserved).isEqualTo(expectedObserved);
+ // Verify that the category's controller is listed after the Preferences' controllers.
+ assertThat(maxObservedIndex).isLessThan(exposureChangesCategoryControllerIndex);
}
}
diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java
index 34d9b24..5eb59e7 100644
--- a/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java
@@ -29,6 +29,7 @@
import com.android.settings.TestConfig;
import com.android.settings.applications.EnterpriseDefaultApps;
import com.android.settings.applications.UserAppInfo;
+import com.android.settings.core.PreferenceAvailabilityObserver;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
@@ -48,6 +49,7 @@
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.anyObject;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
@@ -57,11 +59,14 @@
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public final class EnterpriseSetDefaultAppsPreferenceControllerTest {
+ private static final String KEY_DEFAULT_APPS = "number_enterprise_set_default_apps";
+
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private UserManager mUm;
private FakeFeatureFactory mFeatureFactory;
+ @Mock private PreferenceAvailabilityObserver mObserver;
private EnterpriseSetDefaultAppsPreferenceController mController;
@@ -72,6 +77,12 @@
mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
mController = new EnterpriseSetDefaultAppsPreferenceController(mContext,
null /* lifecycle */);
+ mController.setAvailabilityObserver(mObserver);
+ }
+
+ @Test
+ public void testGetAvailabilityObserver() {
+ assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
}
private void setEnterpriseSetDefaultApps(Intent[] intents, int number) {
@@ -118,10 +129,12 @@
when(mFeatureFactory.applicationFeatureProvider.findPersistentPreferredActivities(anyInt(),
anyObject())).thenReturn(new ArrayList<UserAppInfo>());
assertThat(mController.isAvailable()).isFalse();
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_DEFAULT_APPS, false);
setEnterpriseSetDefaultApps(EnterpriseDefaultApps.BROWSER.getIntents(), 1);
configureUsers(1);
assertThat(mController.isAvailable()).isTrue();
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_DEFAULT_APPS, true);
}
@Test
@@ -132,8 +145,7 @@
@Test
public void testGetPreferenceKey() {
- assertThat(mController.getPreferenceKey())
- .isEqualTo("number_enterprise_set_default_apps");
+ assertThat(mController.getPreferenceKey()).isEqualTo(KEY_DEFAULT_APPS);
}
private static class MatchesIntents extends ArgumentMatcher<Intent[]> {
diff --git a/tests/robotests/src/com/android/settings/enterprise/ExposureChangesCategoryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/ExposureChangesCategoryPreferenceControllerTest.java
new file mode 100644
index 0000000..1c92ea5
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/enterprise/ExposureChangesCategoryPreferenceControllerTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2017 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.enterprise;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.core.DynamicAvailabilityPreferenceController;
+import com.android.settings.core.PreferenceAvailabilityObserver;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Tests for {@link ExposureChangesCategoryPreferenceController}.
+ */
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public final class ExposureChangesCategoryPreferenceControllerTest {
+
+ private static final String KEY_1 = "key_1";
+ private static final String KEY_2 = "key_2";
+ private static final String KEY_EXPOSURE_CHANGES_CATEGORY = "exposure_changes_category";
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+ private List<DynamicAvailabilityPreferenceController> mControllers;
+ private ExposureChangesCategoryPreferenceController mController;
+ @Mock private PreferenceAvailabilityObserver mObserver;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mControllers = Arrays.asList(mock(DynamicAvailabilityPreferenceController.class),
+ mock(DynamicAvailabilityPreferenceController.class));
+ mController = new ExposureChangesCategoryPreferenceController(mContext,
+ null /* lifecycle */, mControllers, true /* controllingUi */);
+ mController.setAvailabilityObserver(mObserver);
+ }
+
+ @Test
+ public void testInitialization() {
+ verify(mControllers.get(0)).setAvailabilityObserver(mController);
+ verify(mControllers.get(1)).setAvailabilityObserver(mController);
+ }
+
+ @Test
+ public void testGetAvailabilityObserver() {
+ assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
+ }
+
+ @Test
+ public void testOnPreferenceAvailabilityUpdated() {
+ final Preference preference = new Preference(mContext, null, 0, 0);
+ preference.setVisible(true);
+
+ mController.updateState(preference);
+ assertThat(preference.isVisible()).isFalse();
+
+ mController.onPreferenceAvailabilityUpdated(KEY_1, true);
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_EXPOSURE_CHANGES_CATEGORY, true);
+ assertThat(preference.isVisible()).isTrue();
+ reset(mObserver);
+
+ mController.onPreferenceAvailabilityUpdated(KEY_2, true);
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_EXPOSURE_CHANGES_CATEGORY, true);
+ assertThat(preference.isVisible()).isTrue();
+ reset(mObserver);
+
+ mController.onPreferenceAvailabilityUpdated(KEY_1, false);
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_EXPOSURE_CHANGES_CATEGORY, true);
+ assertThat(preference.isVisible()).isTrue();
+ reset(mObserver);
+
+ mController.onPreferenceAvailabilityUpdated(KEY_2, false);
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_EXPOSURE_CHANGES_CATEGORY, false);
+ assertThat(preference.isVisible()).isFalse();
+ }
+
+ @Test
+ public void testUpdateState() {
+ final Preference preference = new Preference(mContext, null, 0, 0);
+ preference.setVisible(false);
+
+ mController.onPreferenceAvailabilityUpdated(KEY_1, true);
+ mController.updateState(preference);
+ assertThat(preference.isVisible()).isTrue();
+ }
+
+ @Test
+ public void testIsAvailableForUi() {
+ assertThat(mController.isAvailable()).isTrue();
+ verify(mObserver, never()).onPreferenceAvailabilityUpdated(
+ eq(KEY_EXPOSURE_CHANGES_CATEGORY), anyBoolean());
+
+ mController.onPreferenceAvailabilityUpdated(KEY_1, true);
+ reset(mObserver);
+ assertThat(mController.isAvailable()).isTrue();
+ verify(mObserver, never()).onPreferenceAvailabilityUpdated(
+ eq(KEY_EXPOSURE_CHANGES_CATEGORY), anyBoolean());
+
+ mController.onPreferenceAvailabilityUpdated(KEY_1, false);
+ reset(mObserver);
+ assertThat(mController.isAvailable()).isTrue();
+ verify(mObserver, never()).onPreferenceAvailabilityUpdated(
+ eq(KEY_EXPOSURE_CHANGES_CATEGORY), anyBoolean());
+ }
+
+ @Test
+ public void testIsAvailableForSearch() {
+ final ExposureChangesCategoryPreferenceController controller
+ = new ExposureChangesCategoryPreferenceController(mContext, null /* lifecycle */,
+ mControllers, false /* controllingUi */);
+ controller.setAvailabilityObserver(mObserver);
+ verify(mControllers.get(0)).setAvailabilityObserver(controller);
+ verify(mControllers.get(1)).setAvailabilityObserver(controller);
+
+ assertThat(controller.isAvailable()).isFalse();
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_EXPOSURE_CHANGES_CATEGORY, false);
+ reset(mObserver);
+
+ controller.onPreferenceAvailabilityUpdated(KEY_1, true);
+ verify(mObserver, never()).onPreferenceAvailabilityUpdated(
+ eq(KEY_EXPOSURE_CHANGES_CATEGORY), anyBoolean());
+ assertThat(controller.isAvailable()).isTrue();
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_EXPOSURE_CHANGES_CATEGORY, true);
+ reset(mObserver);
+
+ controller.onPreferenceAvailabilityUpdated(KEY_2, true);
+ verify(mObserver, never()).onPreferenceAvailabilityUpdated(
+ eq(KEY_EXPOSURE_CHANGES_CATEGORY), anyBoolean());
+ assertThat(controller.isAvailable()).isTrue();
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_EXPOSURE_CHANGES_CATEGORY, true);
+ reset(mObserver);
+
+ controller.onPreferenceAvailabilityUpdated(KEY_1, false);
+ verify(mObserver, never()).onPreferenceAvailabilityUpdated(
+ eq(KEY_EXPOSURE_CHANGES_CATEGORY), anyBoolean());
+ assertThat(controller.isAvailable()).isTrue();
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_EXPOSURE_CHANGES_CATEGORY, true);
+ reset(mObserver);
+
+ controller.onPreferenceAvailabilityUpdated(KEY_2, false);
+ verify(mObserver, never()).onPreferenceAvailabilityUpdated(
+ eq(KEY_EXPOSURE_CHANGES_CATEGORY), anyBoolean());
+ assertThat(controller.isAvailable()).isFalse();
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_EXPOSURE_CHANGES_CATEGORY, false);
+ }
+
+ @Test
+ public void testHandlePreferenceTreeClick() {
+ assertThat(mController.handlePreferenceTreeClick(new Preference(mContext, null, 0, 0)))
+ .isFalse();
+ }
+
+ @Test
+ public void testGetPreferenceKey() {
+ assertThat(mController.getPreferenceKey()).isEqualTo(KEY_EXPOSURE_CHANGES_CATEGORY);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerTestBase.java b/tests/robotests/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerTestBase.java
index c14b71e..cbc220f 100644
--- a/tests/robotests/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerTestBase.java
+++ b/tests/robotests/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerTestBase.java
@@ -21,6 +21,7 @@
import android.support.v7.preference.Preference;
import com.android.settings.R;
+import com.android.settings.core.PreferenceAvailabilityObserver;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
@@ -30,6 +31,7 @@
import org.mockito.MockitoAnnotations;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
@@ -42,6 +44,7 @@
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
protected Context mContext;
protected FakeFeatureFactory mFeatureFactory;
+ @Mock private PreferenceAvailabilityObserver mObserver;
protected FailedPasswordWipePreferenceControllerBase mController;
@@ -56,6 +59,12 @@
mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
}
+ @Test
+ public void testGetAvailabilityObserver() {
+ mController.setAvailabilityObserver(mObserver);
+ assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
+ }
+
public abstract void setMaximumFailedPasswordsBeforeWipe(int maximum);
@Test
@@ -72,11 +81,15 @@
@Test
public void testIsAvailable() {
+ mController.setAvailabilityObserver(mObserver);
+
setMaximumFailedPasswordsBeforeWipe(0);
assertThat(mController.isAvailable()).isFalse();
+ verify(mObserver).onPreferenceAvailabilityUpdated(mKey, false);
setMaximumFailedPasswordsBeforeWipe(10);
assertThat(mController.isAvailable()).isTrue();
+ verify(mObserver).onPreferenceAvailabilityUpdated(mKey, true);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/enterprise/GlobalHttpProxyPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/GlobalHttpProxyPreferenceControllerTest.java
index a0bc9ee..016d970 100644
--- a/tests/robotests/src/com/android/settings/enterprise/GlobalHttpProxyPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/GlobalHttpProxyPreferenceControllerTest.java
@@ -21,6 +21,7 @@
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
+import com.android.settings.core.PreferenceAvailabilityObserver;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
@@ -32,6 +33,7 @@
import org.robolectric.annotation.Config;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
@@ -40,9 +42,13 @@
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public final class GlobalHttpProxyPreferenceControllerTest {
+
+ private static final String KEY_GLOBAL_HTTP_PROXY = "global_http_proxy";
+
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
private FakeFeatureFactory mFeatureFactory;
+ @Mock private PreferenceAvailabilityObserver mObserver;
private GlobalHttpProxyPreferenceController mController;
@@ -52,6 +58,12 @@
FakeFeatureFactory.setupForTest(mContext);
mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
mController = new GlobalHttpProxyPreferenceController(mContext, null /* lifecycle */);
+ mController.setAvailabilityObserver(mObserver);
+ }
+
+ @Test
+ public void testGetAvailabilityObserver() {
+ assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
}
@Test
@@ -59,10 +71,12 @@
when(mFeatureFactory.enterprisePrivacyFeatureProvider.isGlobalHttpProxySet())
.thenReturn(false);
assertThat(mController.isAvailable()).isFalse();
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_GLOBAL_HTTP_PROXY, false);
when(mFeatureFactory.enterprisePrivacyFeatureProvider.isGlobalHttpProxySet())
.thenReturn(true);
assertThat(mController.isAvailable()).isTrue();
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_GLOBAL_HTTP_PROXY, true);
}
@Test
@@ -73,6 +87,6 @@
@Test
public void testGetPreferenceKey() {
- assertThat(mController.getPreferenceKey()).isEqualTo("global_http_proxy");
+ assertThat(mController.getPreferenceKey()).isEqualTo(KEY_GLOBAL_HTTP_PROXY);
}
}
diff --git a/tests/robotests/src/com/android/settings/enterprise/ImePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/ImePreferenceControllerTest.java
index 05d0535..3304b44 100644
--- a/tests/robotests/src/com/android/settings/enterprise/ImePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/ImePreferenceControllerTest.java
@@ -23,6 +23,7 @@
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
+import com.android.settings.core.PreferenceAvailabilityObserver;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
@@ -34,6 +35,7 @@
import org.robolectric.annotation.Config;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
@@ -43,12 +45,14 @@
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public final class ImePreferenceControllerTest {
- private final String DEFAULT_IME_LABEL = "Test IME";
- private final String DEFAULT_IME_TEXT = "Set to Test IME";
+ private static final String DEFAULT_IME_LABEL = "Test IME";
+ private static final String DEFAULT_IME_TEXT = "Set to Test IME";
+ private static final String KEY_INPUT_METHOD = "input_method";
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
private FakeFeatureFactory mFeatureFactory;
+ @Mock private PreferenceAvailabilityObserver mObserver;
private ImePreferenceController mController;
@@ -60,6 +64,12 @@
mController = new ImePreferenceController(mContext, null /* lifecycle */);
when(mContext.getResources().getString(R.string.enterprise_privacy_input_method_name,
DEFAULT_IME_LABEL)).thenReturn(DEFAULT_IME_TEXT);
+ mController.setAvailabilityObserver(mObserver);
+ }
+
+ @Test
+ public void testGetAvailabilityObserver() {
+ assertThat(mController.getAvailabilityObserver()).isEqualTo(mObserver);
}
@Test
@@ -77,10 +87,12 @@
when(mFeatureFactory.enterprisePrivacyFeatureProvider.getImeLabelIfOwnerSet())
.thenReturn(null);
assertThat(mController.isAvailable()).isFalse();
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_INPUT_METHOD, false);
when(mFeatureFactory.enterprisePrivacyFeatureProvider.getImeLabelIfOwnerSet())
.thenReturn(DEFAULT_IME_LABEL);
assertThat(mController.isAvailable()).isTrue();
+ verify(mObserver).onPreferenceAvailabilityUpdated(KEY_INPUT_METHOD, true);
}
@Test
@@ -91,6 +103,6 @@
@Test
public void testGetPreferenceKey() {
- assertThat(mController.getPreferenceKey()).isEqualTo("input_method");
+ assertThat(mController.getPreferenceKey()).isEqualTo(KEY_INPUT_METHOD);
}
}
diff --git a/tests/robotests/src/com/android/settings/gestures/DoubleTwistPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/DoubleTwistPreferenceControllerTest.java
index 8393fc3..7caf3fc 100644
--- a/tests/robotests/src/com/android/settings/gestures/DoubleTwistPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/gestures/DoubleTwistPreferenceControllerTest.java
@@ -19,13 +19,14 @@
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorManager;
+import android.os.UserManager;
import android.provider.Settings;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
+import com.android.settings.testutils.shadow.ShadowSecureSettings;
-import com.android.settings.search2.InlineSwitchPayload;
-import com.android.settings.search2.ResultPayload;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -41,7 +42,9 @@
import static android.provider.Settings.Secure.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@RunWith(SettingsRobolectricTestRunner.class)
@@ -58,9 +61,15 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mock(UserManager.class));
mController = new DoubleTwistPreferenceController(mContext, null, KEY_DOUBLE_TWIST);
}
+ @After
+ public void tearDown() {
+ ShadowSecureSettings.clear();
+ }
+
@Test
public void isAvailable_hasSensor_shouldReturnTrue() {
// Mock sensors
@@ -94,6 +103,27 @@
}
@Test
+ @Config(shadows = {ShadowSecureSettings.class})
+ public void onPreferenceChange_hasWorkProfile_shouldUpdateSettingForWorkProfileUser() {
+ final int managedId = 2;
+ ShadowSecureSettings.putIntForUser(
+ null, CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED, 0, managedId);
+ DoubleTwistPreferenceController controller =
+ spy(new DoubleTwistPreferenceController(mContext, null, KEY_DOUBLE_TWIST));
+ doReturn(managedId).when(controller).getManagedProfileUserId();
+
+ // enable the gesture
+ controller.onPreferenceChange(null, true);
+ assertThat(Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED, 0, managedId)).isEqualTo(1);
+
+ // disable the gesture
+ controller.onPreferenceChange(null, false);
+ assertThat(Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED, 1, managedId)).isEqualTo(0);
+ }
+
+ @Test
public void testSwitchEnabled_configIsSet_shouldReturnTrue() {
// Set the setting to be enabled.
final Context context = ShadowApplication.getInstance().getApplicationContext();
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java
index fd4d0d2..64e188e 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java
@@ -68,4 +68,8 @@
Integer value = (Integer) mValueMap.get(name);
return value == null ? defaultValue : value;
}
+
+ public static void clear() {
+ mValueMap.clear();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiEnablerTest.java b/tests/robotests/src/com/android/settings/wifi/WifiEnablerTest.java
new file mode 100644
index 0000000..5646470
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/WifiEnablerTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 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.wifi;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
+import com.android.settings.widget.SwitchWidgetController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class WifiEnablerTest {
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private WifiManager mWifiManager;
+ @Mock
+ private ConnectivityManagerWrapper mConnectivityManager;
+
+ private WifiEnabler mEnabler;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
+ mEnabler = new WifiEnabler(mContext, mock(SwitchWidgetController.class),
+ mock(MetricsFeatureProvider.class), mConnectivityManager);
+ }
+
+ @Test
+ public void onSwitchToggled_avoidBadWifiConfigIsFalse_shouldReturnTrue() {
+ when(mWifiManager.setWifiEnabled(true)).thenReturn(true);
+ when(mWifiManager.getWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_ENABLED);
+
+ mEnabler.onSwitchToggled(true);
+
+ verify(mConnectivityManager).stopTethering(ConnectivityManager.TETHERING_WIFI);
+ }
+
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java
index 0298195..18cfa4e 100644
--- a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java
@@ -75,9 +75,13 @@
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -89,9 +93,6 @@
private static final String MAC_ADDRESS = WifiInfo.DEFAULT_MAC_ADDRESS;
private static final String SECURITY = "None";
- private InetAddress mIpv4Address;
- private Inet6Address mIpv6Address;
-
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private PreferenceScreen mockScreen;
@@ -120,35 +121,76 @@
@Mock private WifiDetailPreference mockSubnetPref;
@Mock private WifiDetailPreference mockDnsPref;
@Mock private Button mockForgetButton;
- @Mock private PreferenceCategory mockIpv6AddressCategory;
+ @Mock private PreferenceCategory mockIpv6Category;
+ @Mock private WifiDetailPreference mockIpv6AddressesPref;
@Captor private ArgumentCaptor<NetworkCallback> mCallbackCaptor;
@Captor private ArgumentCaptor<View.OnClickListener> mForgetClickListener;
+ @Captor private ArgumentCaptor<Preference> mIpv6AddressCaptor;
private Context mContext = RuntimeEnvironment.application;
private Lifecycle mLifecycle;
private LinkProperties mLinkProperties;
private WifiDetailPreferenceController mController;
+ // This class exists so that these values can be made static final. They can't be static final
+ // members of the test class, because any attempt to call IpPrefix or RouteInfo constructors
+ // during static initialization of the test class results in NoSuchMethorError being thrown
+ // when the test is run.
+ private static class Constants {
+ static final int IPV4_PREFIXLEN = 25;
+ static final LinkAddress IPV4_ADDR;
+ static final Inet4Address IPV4_GATEWAY;
+ static final RouteInfo IPV4_DEFAULT;
+ static final RouteInfo IPV4_SUBNET;
+ static final LinkAddress IPV6_LINKLOCAL;
+ static final LinkAddress IPV6_GLOBAL1;
+ static final LinkAddress IPV6_GLOBAL2;
+ static final InetAddress IPV4_DNS1;
+ static final InetAddress IPV4_DNS2;
+ static final InetAddress IPV6_DNS;
+
+ private static LinkAddress ipv6LinkAddress(String addr) throws UnknownHostException {
+ return new LinkAddress(InetAddress.getByName(addr), 64);
+ }
+
+ private static LinkAddress ipv4LinkAddress(String addr, int prefixlen)
+ throws UnknownHostException {
+ return new LinkAddress(InetAddress.getByName(addr), prefixlen);
+ }
+
+ static {
+ try {
+ // We create our test constants in these roundabout ways because the robolectric
+ // shadows don't contain NetworkUtils.parseNumericAddress and other utility methods,
+ // so the easy ways to do things fail with NoSuchMethodError.
+ IPV4_ADDR = ipv4LinkAddress("192.0.2.2", IPV4_PREFIXLEN);
+ IPV4_GATEWAY = (Inet4Address) InetAddress.getByName("192.0.2.127");
+
+ final Inet4Address any4 = (Inet4Address) InetAddress.getByName("0.0.0.0");
+ IpPrefix subnet = new IpPrefix(IPV4_ADDR.getAddress(), IPV4_PREFIXLEN);
+ IPV4_SUBNET = new RouteInfo(subnet, any4);
+ IPV4_DEFAULT = new RouteInfo(new IpPrefix(any4, 0), IPV4_GATEWAY);
+
+ IPV6_LINKLOCAL = ipv6LinkAddress("fe80::211:25ff:fef8:7cb2%1");
+ IPV6_GLOBAL1 = ipv6LinkAddress("2001:db8:1::211:25ff:fef8:7cb2");
+ IPV6_GLOBAL2 = ipv6LinkAddress("2001:db8:1::3dfe:8902:f98f:739d");
+
+ IPV4_DNS1 = InetAddress.getByName("8.8.8.8");
+ IPV4_DNS2 = InetAddress.getByName("8.8.4.4");
+ IPV6_DNS = InetAddress.getByName("2001:4860:4860::64");
+ } catch (UnknownHostException e) {
+ throw new RuntimeException("Invalid hardcoded IP addresss: " + e);
+ }
+ }
+ }
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mLifecycle = new Lifecycle();
- try {
- mIpv4Address = InetAddress.getByAddress(
- new byte[] { (byte) 255, (byte) 255, (byte) 255, (byte) 255 });
- mIpv6Address = Inet6Address.getByAddress(
- "123", /* host */
- new byte[] {
- (byte) 0xFE, (byte) 0x80, 0, 0, 0, 0, 0, 0, 0x02, 0x11, 0x25,
- (byte) 0xFF, (byte) 0xFE, (byte) 0xF8, (byte) 0x7C, (byte) 0xB2},
- 1 /*scope id */);
- } catch (UnknownHostException e) {
- throw new RuntimeException(e);
- }
-
when(mockAccessPoint.getConfig()).thenReturn(mockWifiConfig);
when(mockAccessPoint.getLevel()).thenReturn(LEVEL);
when(mockAccessPoint.getSecurityString(false)).thenReturn(SECURITY);
@@ -217,8 +259,10 @@
.thenReturn(mockSubnetPref);
when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_DNS_PREF))
.thenReturn(mockDnsPref);
- when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_IPV6_ADDRESS_CATEGORY))
- .thenReturn(mockIpv6AddressCategory);
+ when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_IPV6_CATEGORY))
+ .thenReturn(mockIpv6Category);
+ when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_IPV6_ADDRESSES_PREF))
+ .thenReturn(mockIpv6AddressesPref);
}
@Test
@@ -330,26 +374,23 @@
@Test
public void ipAddressPref_shouldHaveDetailTextSet() {
- LinkAddress ipv4Address = new LinkAddress(mIpv4Address, 32);
-
- mLinkProperties.addLinkAddress(ipv4Address);
+ mLinkProperties.addLinkAddress(Constants.IPV4_ADDR);
mController.displayPreference(mockScreen);
- verify(mockIpAddressPref).setDetailText(mIpv4Address.getHostAddress());
+ verify(mockIpAddressPref).setDetailText(Constants.IPV4_ADDR.getAddress().getHostAddress());
}
@Test
public void gatewayAndSubnet_shouldHaveDetailTextSet() {
- int prefixLength = 24;
- IpPrefix subnet = new IpPrefix(mIpv4Address, prefixLength);
- InetAddress gateway = mIpv4Address;
- mLinkProperties.addRoute(new RouteInfo(subnet, gateway));
+ mLinkProperties.addLinkAddress(Constants.IPV4_ADDR);
+ mLinkProperties.addRoute(Constants.IPV4_DEFAULT);
+ mLinkProperties.addRoute(Constants.IPV4_SUBNET);
mController.displayPreference(mockScreen);
- verify(mockSubnetPref).setDetailText("255.255.255.0");
- verify(mockGatewayPref).setDetailText(mIpv4Address.getHostAddress());
+ verify(mockSubnetPref).setDetailText("255.255.255.128");
+ verify(mockGatewayPref).setDetailText("192.0.2.127");
}
@Test
@@ -376,23 +417,146 @@
@Test
public void noLinkProperties_allIpDetailsHidden() {
when(mockConnectivityManager.getLinkProperties(mockNetwork)).thenReturn(null);
- reset(mockIpv6AddressCategory, mockIpAddressPref, mockSubnetPref, mockGatewayPref,
+ reset(mockIpv6Category, mockIpAddressPref, mockSubnetPref, mockGatewayPref,
mockDnsPref);
mController.displayPreference(mockScreen);
- verify(mockIpv6AddressCategory).setVisible(false);
+ verify(mockIpv6Category).setVisible(false);
verify(mockIpAddressPref).setVisible(false);
verify(mockSubnetPref).setVisible(false);
verify(mockGatewayPref).setVisible(false);
verify(mockDnsPref).setVisible(false);
- verify(mockIpv6AddressCategory, never()).setVisible(true);
+ verify(mockIpv6Category, never()).setVisible(true);
verify(mockIpAddressPref, never()).setVisible(true);
verify(mockSubnetPref, never()).setVisible(true);
verify(mockGatewayPref, never()).setVisible(true);
verify(mockDnsPref, never()).setVisible(true);
}
+ // Convenience method to convert a LinkAddress to a string without a prefix length.
+ private String asString(LinkAddress l) {
+ return l.getAddress().getHostAddress();
+ }
+
+ // Pretend that the NetworkCallback was triggered with a new copy of lp. We need to create a
+ // new copy because the code only updates if !mLinkProperties.equals(lp).
+ private void updateLinkProperties(LinkProperties lp) {
+ mCallbackCaptor.getValue().onLinkPropertiesChanged(mockNetwork, new LinkProperties(lp));
+ }
+
+ private void updateNetworkCapabilities(NetworkCapabilities nc) {
+ mCallbackCaptor.getValue().onCapabilitiesChanged(mockNetwork, new NetworkCapabilities(nc));
+ }
+
+ private NetworkCapabilities makeNetworkCapabilities() {
+ NetworkCapabilities nc = new NetworkCapabilities();
+ nc.clearAll();
+ nc.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ return nc;
+ }
+
+ private void verifyDisplayedIpv6Addresses(InOrder inOrder, LinkAddress... addresses) {
+ String text = Arrays.stream(addresses)
+ .map(address -> asString(address))
+ .collect(Collectors.joining("\n"));
+ inOrder.verify(mockIpv6AddressesPref).setSummary(text);
+ }
+
+ @Test
+ public void onLinkPropertiesChanged_updatesFields() {
+ mController.displayPreference(mockScreen);
+ mController.onResume();
+
+ InOrder inOrder = inOrder(mockIpAddressPref, mockGatewayPref, mockSubnetPref,
+ mockDnsPref, mockIpv6Category, mockIpv6AddressesPref);
+
+ LinkProperties lp = new LinkProperties();
+
+ lp.addLinkAddress(Constants.IPV6_LINKLOCAL);
+ updateLinkProperties(lp);
+ verifyDisplayedIpv6Addresses(inOrder, Constants.IPV6_LINKLOCAL);
+ inOrder.verify(mockIpv6Category).setVisible(true);
+
+ lp.addRoute(Constants.IPV4_DEFAULT);
+ updateLinkProperties(lp);
+ inOrder.verify(mockGatewayPref).setDetailText(Constants.IPV4_GATEWAY.getHostAddress());
+ inOrder.verify(mockGatewayPref).setVisible(true);
+
+ lp.addLinkAddress(Constants.IPV4_ADDR);
+ lp.addRoute(Constants.IPV4_SUBNET);
+ updateLinkProperties(lp);
+ inOrder.verify(mockIpAddressPref).setDetailText(asString(Constants.IPV4_ADDR));
+ inOrder.verify(mockIpAddressPref).setVisible(true);
+ inOrder.verify(mockSubnetPref).setDetailText("255.255.255.128");
+ inOrder.verify(mockSubnetPref).setVisible(true);
+
+ lp.addLinkAddress(Constants.IPV6_GLOBAL1);
+ lp.addLinkAddress(Constants.IPV6_GLOBAL2);
+ updateLinkProperties(lp);
+ verifyDisplayedIpv6Addresses(inOrder,
+ Constants.IPV6_LINKLOCAL,
+ Constants.IPV6_GLOBAL1,
+ Constants.IPV6_GLOBAL2);
+
+ lp.removeLinkAddress(Constants.IPV6_GLOBAL1);
+ updateLinkProperties(lp);
+ verifyDisplayedIpv6Addresses(inOrder,
+ Constants.IPV6_LINKLOCAL,
+ Constants.IPV6_GLOBAL2);
+
+ lp.addDnsServer(Constants.IPV6_DNS);
+ updateLinkProperties(lp);
+ inOrder.verify(mockDnsPref, never()).setVisible(true);
+
+ lp.addDnsServer(Constants.IPV4_DNS1);
+ lp.addDnsServer(Constants.IPV4_DNS2);
+ updateLinkProperties(lp);
+ inOrder.verify(mockDnsPref).setDetailText(
+ Constants.IPV4_DNS1.getHostAddress() + "," +
+ Constants.IPV4_DNS2.getHostAddress());
+ inOrder.verify(mockDnsPref).setVisible(true);
+ }
+
+ @Test
+ public void onCapabilitiesChanged_callsRefreshIfNecessary() {
+ NetworkCapabilities nc = makeNetworkCapabilities();
+ when(mockConnectivityManager.getNetworkCapabilities(mockNetwork))
+ .thenReturn(new NetworkCapabilities(nc));
+
+ String summary = "Connected, no Internet";
+ when(mockAccessPoint.getSettingsSummary()).thenReturn(summary);
+
+ InOrder inOrder = inOrder(mockConnectionDetailPref);
+ mController.displayPreference(mockScreen);
+ mController.onResume();
+ inOrder.verify(mockConnectionDetailPref).setTitle(summary);
+
+ // Check that an irrelevant capability update does not update the access point summary, as
+ // doing so could cause unnecessary jank...
+ summary = "Connected";
+ when(mockAccessPoint.getSettingsSummary()).thenReturn(summary);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mockConnectionDetailPref, never()).setTitle(any());
+
+ // ... but that if the network validates, then we do refresh.
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mockConnectionDetailPref).setTitle(summary);
+
+ summary = "Connected, no Internet";
+ when(mockAccessPoint.getSettingsSummary()).thenReturn(summary);
+
+ // Another irrelevant update won't cause the UI to refresh...
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mockConnectionDetailPref, never()).setTitle(any());
+
+ // ... but if the network is no longer validated, then we display "connected, no Internet".
+ nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mockConnectionDetailPref).setTitle(summary);
+ }
+
@Test
public void canForgetNetwork_noNetwork() {
when(mockAccessPoint.getConfig()).thenReturn(null);
@@ -475,7 +639,7 @@
}
@Test
- public void networkDisconnectdState_shouldFinishActivity() {
+ public void networkDisconnectedState_shouldFinishActivity() {
mController.onResume();
when(mockConnectivityManager.getNetworkInfo(any(Network.class))).thenReturn(null);
@@ -496,28 +660,29 @@
@Test
public void ipv6AddressPref_shouldHaveHostAddressTextSet() {
- LinkAddress ipv6Address = new LinkAddress(mIpv6Address, 128);
-
- mLinkProperties.addLinkAddress(ipv6Address);
+ mLinkProperties.addLinkAddress(Constants.IPV6_LINKLOCAL);
+ mLinkProperties.addLinkAddress(Constants.IPV6_GLOBAL1);
+ mLinkProperties.addLinkAddress(Constants.IPV6_GLOBAL2);
mController.displayPreference(mockScreen);
- ArgumentCaptor<Preference> preferenceCaptor = ArgumentCaptor.forClass(Preference.class);
- verify(mockIpv6AddressCategory).addPreference(preferenceCaptor.capture());
- assertThat(preferenceCaptor.getValue().getTitle()).isEqualTo(mIpv6Address.getHostAddress());
+ List <Preference> addrs = mIpv6AddressCaptor.getAllValues();
+
+ String expectedAddresses = String.join("\n",
+ asString(Constants.IPV6_LINKLOCAL),
+ asString(Constants.IPV6_GLOBAL1),
+ asString(Constants.IPV6_GLOBAL2));
+
+ verify(mockIpv6AddressesPref).setSummary(expectedAddresses);
}
@Test
public void ipv6AddressPref_shouldNotBeSelectable() {
- LinkAddress ipv6Address = new LinkAddress(mIpv6Address, 128);
-
- mLinkProperties.addLinkAddress(ipv6Address);
+ mLinkProperties.addLinkAddress(Constants.IPV6_GLOBAL2);
mController.displayPreference(mockScreen);
- ArgumentCaptor<Preference> preferenceCaptor = ArgumentCaptor.forClass(Preference.class);
- verify(mockIpv6AddressCategory).addPreference(preferenceCaptor.capture());
- assertThat(preferenceCaptor.getValue().isSelectable()).isFalse();
+ assertThat(mockIpv6AddressesPref.isSelectable()).isFalse();
}
@Test
@@ -529,22 +694,16 @@
inOrder.verify(mockSignInButton).setVisibility(View.INVISIBLE);
- NetworkCapabilities nc = new NetworkCapabilities();
- nc.clearAll();
- nc.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
-
- NetworkCallback callback = mCallbackCaptor.getValue();
- callback.onCapabilitiesChanged(mockNetwork, nc);
+ NetworkCapabilities nc = makeNetworkCapabilities();
+ updateNetworkCapabilities(nc);
inOrder.verify(mockSignInButton).setVisibility(View.INVISIBLE);
- nc = new NetworkCapabilities(nc);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
- callback.onCapabilitiesChanged(mockNetwork, nc);
+ updateNetworkCapabilities(nc);
inOrder.verify(mockSignInButton).setVisibility(View.VISIBLE);
- nc = new NetworkCapabilities(nc);
nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
- callback.onCapabilitiesChanged(mockNetwork, nc);
+ updateNetworkCapabilities(nc);
inOrder.verify(mockSignInButton).setVisibility(View.INVISIBLE);
}