diff --git a/res/xml/enterprise_privacy_settings.xml b/res/xml/enterprise_privacy_settings.xml index 45c784ea1b1..2d07fa2c50f 100644 --- a/res/xml/enterprise_privacy_settings.xml +++ b/res/xml/enterprise_privacy_settings.xml @@ -45,7 +45,8 @@ android:selectable="false"/> - + diff --git a/src/com/android/settings/core/DynamicAvailabilityPreferenceController.java b/src/com/android/settings/core/DynamicAvailabilityPreferenceController.java index 9323aa34821..8d98b50b941 100644 --- a/src/com/android/settings/core/DynamicAvailabilityPreferenceController.java +++ b/src/com/android/settings/core/DynamicAvailabilityPreferenceController.java @@ -29,6 +29,7 @@ public abstract class DynamicAvailabilityPreferenceController extends Preference private Preference mPreference; private PreferenceScreen mScreen; + private PreferenceAvailabilityObserver mAvailabilityObserver = null; public DynamicAvailabilityPreferenceController(Context context, Lifecycle lifecycle) { super(context); @@ -37,6 +38,14 @@ public abstract class DynamicAvailabilityPreferenceController extends Preference } } + public void setAvailabilityObserver(PreferenceAvailabilityObserver observer) { + mAvailabilityObserver = observer; + } + + public PreferenceAvailabilityObserver getAvailabilityObserver() { + return mAvailabilityObserver; + } + @Override public void displayPreference(PreferenceScreen screen) { mScreen = screen; @@ -56,4 +65,10 @@ public abstract class DynamicAvailabilityPreferenceController extends Preference 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 00000000000..46ff3bacb54 --- /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 f0aca01a1a5..556baec56db 100644 --- a/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java +++ b/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java @@ -50,15 +50,15 @@ public abstract class AdminGrantedPermissionsPreferenceControllerBase 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 @@ public abstract class AdminGrantedPermissionsPreferenceControllerBase 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 e98ece8068b..32f2bbee698 100644 --- a/src/com/android/settings/enterprise/AlwaysOnVpnCurrentUserPreferenceController.java +++ b/src/com/android/settings/enterprise/AlwaysOnVpnCurrentUserPreferenceController.java @@ -42,7 +42,9 @@ public class AlwaysOnVpnCurrentUserPreferenceController @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 4796b75190f..84863684a54 100644 --- a/src/com/android/settings/enterprise/AlwaysOnVpnManagedProfilePreferenceController.java +++ b/src/com/android/settings/enterprise/AlwaysOnVpnManagedProfilePreferenceController.java @@ -33,7 +33,9 @@ public class AlwaysOnVpnManagedProfilePreferenceController @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 d020676efb6..fc89dc803d0 100644 --- a/src/com/android/settings/enterprise/CaCertsPreferenceController.java +++ b/src/com/android/settings/enterprise/CaCertsPreferenceController.java @@ -44,8 +44,11 @@ public class CaCertsPreferenceController extends DynamicAvailabilityPreferenceCo @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 acbcc2e23b1..9876f71ef19 100644 --- a/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceController.java +++ b/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceController.java @@ -42,14 +42,18 @@ public class EnterpriseInstalledPackagesPreferenceController 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 @@ public class EnterpriseInstalledPackagesPreferenceController 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 69e04164352..372982f8296 100644 --- a/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java +++ b/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java @@ -45,7 +45,9 @@ public class EnterprisePrivacyPreferenceController extends DynamicAvailabilityPr @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 a80dec015ad..f7327c87406 100644 --- a/src/com/android/settings/enterprise/EnterprisePrivacySettings.java +++ b/src/com/android/settings/enterprise/EnterprisePrivacySettings.java @@ -61,23 +61,32 @@ public class EnterprisePrivacySettings extends DashboardFragment { 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(); + 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 2f43a618c77..35f6e4120b6 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 @@ public class EnterpriseSetDefaultAppsPreferenceController @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 00000000000..4c89659c82b --- /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 mAvailablePrefs = new HashSet(); + 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 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 2f35da6bd2b..7485fe20edb 100644 --- a/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerBase.java +++ b/src/com/android/settings/enterprise/FailedPasswordWipePreferenceControllerBase.java @@ -46,6 +46,8 @@ public abstract class FailedPasswordWipePreferenceControllerBase @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 6ee7fc4cdbc..76c49dade06 100644 --- a/src/com/android/settings/enterprise/GlobalHttpProxyPreferenceController.java +++ b/src/com/android/settings/enterprise/GlobalHttpProxyPreferenceController.java @@ -32,7 +32,9 @@ public class GlobalHttpProxyPreferenceController extends DynamicAvailabilityPref @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 b090bed9f56..b13aec74d92 100644 --- a/src/com/android/settings/enterprise/ImePreferenceController.java +++ b/src/com/android/settings/enterprise/ImePreferenceController.java @@ -43,7 +43,9 @@ public class ImePreferenceController extends DynamicAvailabilityPreferenceContro @Override public boolean isAvailable() { - return mFeatureProvider.getImeLabelIfOwnerSet() != null; + final boolean available = mFeatureProvider.getImeLabelIfOwnerSet() != null; + notifyOnAvailabilityUpdate(available); + return available; } @Override diff --git a/tests/robotests/src/com/android/settings/core/DynamicAvailabilityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/core/DynamicAvailabilityPreferenceControllerTest.java index 38a8356aef2..399c753b8ce 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 android.support.v7.preference.PreferenceScreen; 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 @@ public final class DynamicAvailabilityPreferenceControllerTest { 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 @@ public final class DynamicAvailabilityPreferenceControllerTest { 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 @@ public final class DynamicAvailabilityPreferenceControllerTest { @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 96ce081700d..c1a3143065b 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 android.support.v7.preference.Preference; 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.invocation.InvocationOnMock; 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 @@ public abstract class AdminGrantedPermissionsPreferenceControllerTestBase { @Mock(answer = Answers.RETURNS_DEEP_STUBS) protected Context mContext; private FakeFeatureFactory mFeatureFactory; + @Mock private PreferenceAvailabilityObserver mObserver; protected AdminGrantedPermissionsPreferenceControllerBase mController; @@ -64,6 +69,12 @@ public abstract class AdminGrantedPermissionsPreferenceControllerTestBase { 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 @@ public abstract class AdminGrantedPermissionsPreferenceControllerTestBase { 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 @@ public abstract class AdminGrantedPermissionsPreferenceControllerTestBase { 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 9d1bd582446..51c8a7b381d 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 android.support.v7.preference.Preference; 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.mockito.MockitoAnnotations; 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 @@ import static org.mockito.Mockito.when; @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 @@ public final class AlwaysOnVpnCurrentUserPreferenceControllerTest { .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 @@ public final class AlwaysOnVpnCurrentUserPreferenceControllerTest { 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 @@ public final class AlwaysOnVpnCurrentUserPreferenceControllerTest { @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 8ac10d16630..456271136d8 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 android.support.v7.preference.Preference; 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.mockito.MockitoAnnotations; 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 @@ import static org.mockito.Mockito.when; @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 @@ public final class AlwaysOnVpnManagedProfilePreferenceControllerTest { 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 @@ public final class AlwaysOnVpnManagedProfilePreferenceControllerTest { 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 @@ public final class AlwaysOnVpnManagedProfilePreferenceControllerTest { @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 fef2e0f017f..2c0e320ada3 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 android.support.v7.preference.Preference; 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.mockito.MockitoAnnotations; 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 @@ import static org.mockito.Mockito.when; @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 @@ public final class CaCertsPreferenceControllerTest { 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 @@ public final class CaCertsPreferenceControllerTest { 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 @@ public final class CaCertsPreferenceControllerTest { @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 4255d963e93..cf54bb0c5d2 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.R; 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.mockito.stubbing.Answer; 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 @@ import static org.mockito.Mockito.when; @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 @@ public final class EnterpriseInstalledPackagesPreferenceControllerTest { 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 @@ public final class EnterpriseInstalledPackagesPreferenceControllerTest { 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 @@ public final class EnterpriseInstalledPackagesPreferenceControllerTest { 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 @@ public final class EnterpriseInstalledPackagesPreferenceControllerTest { 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 @@ public final class EnterpriseInstalledPackagesPreferenceControllerTest { @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 fe483471bde..101a45ae60c 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 android.support.v7.preference.Preference; 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.mockito.MockitoAnnotations; 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 @@ import static org.mockito.Mockito.when; @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 @@ public final class EnterprisePrivacyPreferenceControllerTest { 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 final class EnterprisePrivacyPreferenceControllerTest { 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 @@ public final class EnterprisePrivacyPreferenceControllerTest { @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 d41be75767b..16fa5ba72c2 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.junit.runner.RunWith; 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 @@ import static org.mockito.Mockito.when; @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 @@ public final class EnterprisePrivacySettingsTest { } @Test - public void getPreferenceControllers() { + public void getPreferenceControllers() throws Exception { final List controllers = mSettings.getPreferenceControllers( ShadowApplication.getInstance().getApplicationContext()); verifyPreferenceControllers(controllers); } @Test - public void getSearchIndexProviderPreferenceControllers() { + public void getSearchIndexProviderPreferenceControllers() throws Exception { final List controllers = EnterprisePrivacySettings.SEARCH_INDEX_DATA_PROVIDER.getPreferenceControllers( ShadowApplication.getInstance().getApplicationContext()); verifyPreferenceControllers(controllers); } - private void verifyPreferenceControllers(List controllers) { + private void verifyPreferenceControllers(List 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 @@ public final class EnterprisePrivacySettingsTest { 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 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 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 34d9b244f85..5eb59e76943 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.SettingsRobolectricTestRunner; 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.anyInt; 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 @@ import static org.mockito.Mockito.when; @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 @@ public final class EnterpriseSetDefaultAppsPreferenceControllerTest { 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 @@ public final class EnterpriseSetDefaultAppsPreferenceControllerTest { when(mFeatureFactory.applicationFeatureProvider.findPersistentPreferredActivities(anyInt(), anyObject())).thenReturn(new ArrayList()); 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 @@ public final class EnterpriseSetDefaultAppsPreferenceControllerTest { @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 { 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 00000000000..1c92ea538c4 --- /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 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 c14b71e24f9..cbc220fbd7e 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.content.res.Resources; 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.Mock; 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 @@ public abstract class FailedPasswordWipePreferenceControllerTestBase { @Mock(answer = Answers.RETURNS_DEEP_STUBS) protected Context mContext; protected FakeFeatureFactory mFeatureFactory; + @Mock private PreferenceAvailabilityObserver mObserver; protected FailedPasswordWipePreferenceControllerBase mController; @@ -56,6 +59,12 @@ public abstract class FailedPasswordWipePreferenceControllerTestBase { 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 @@ public abstract class FailedPasswordWipePreferenceControllerTestBase { @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 a0bc9ee2b3f..016d9707cb0 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 android.support.v7.preference.Preference; 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.mockito.MockitoAnnotations; 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 @@ import static org.mockito.Mockito.when; @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 @@ public final class GlobalHttpProxyPreferenceControllerTest { 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 @@ public final class GlobalHttpProxyPreferenceControllerTest { 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 @@ public final class GlobalHttpProxyPreferenceControllerTest { @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 05d053553ed..3304b442d68 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 android.support.v7.preference.Preference; 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.mockito.MockitoAnnotations; 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 @@ import static org.mockito.Mockito.when; @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 @@ public final class ImePreferenceControllerTest { 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 @@ public final class ImePreferenceControllerTest { 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 @@ public final class ImePreferenceControllerTest { @Test public void testGetPreferenceKey() { - assertThat(mController.getPreferenceKey()).isEqualTo("input_method"); + assertThat(mController.getPreferenceKey()).isEqualTo(KEY_INPUT_METHOD); } }