diff --git a/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java index 265754cad8d..daf3992c3c2 100644 --- a/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java @@ -109,7 +109,7 @@ public class AppMemoryPreferenceController extends BasePreferenceController } return DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext) - ? AVAILABLE : DISABLED_DEPENDENT_SETTING; + ? AVAILABLE : DISABLED_UNSUPPORTED; } @Override diff --git a/src/com/android/settings/core/BasePreferenceController.java b/src/com/android/settings/core/BasePreferenceController.java index fd17012d574..44305dd897e 100644 --- a/src/com/android/settings/core/BasePreferenceController.java +++ b/src/com/android/settings/core/BasePreferenceController.java @@ -22,7 +22,6 @@ import com.android.settings.search.ResultPayload; import com.android.settings.search.SearchIndexableRaw; import com.android.settings.slices.SliceData; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.lifecycle.Lifecycle; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -30,6 +29,10 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.List; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceGroup; +import android.support.v7.preference.PreferenceScreen; + /** * Abstract class to consolidate utility between preference controllers and act as an interface * for Slices. The abstract classes that inherit from this class will act as the direct interfaces @@ -39,6 +42,12 @@ public abstract class BasePreferenceController extends AbstractPreferenceControl private static final String TAG = "SettingsPrefController"; + /** + * Denotes the availability of the Setting. + *

+ * Used both explicitly and by the convenience methods {@link #isAvailable()} and + * {@link #isSupported()}. + */ @Retention(RetentionPolicy.SOURCE) @IntDef({AVAILABLE, DISABLED_UNSUPPORTED, DISABLED_FOR_USER, DISABLED_DEPENDENT_SETTING, UNAVAILABLE_UNKNOWN}) @@ -52,21 +61,42 @@ public abstract class BasePreferenceController extends AbstractPreferenceControl /** * The setting is not supported by the device. + *

+ * There is no guarantee that the setting page exists, and any links to the Setting should take + * you to the home page of Settings. */ public static final int DISABLED_UNSUPPORTED = 1; /** * The setting cannot be changed by the current user. + *

+ * Links to the Setting should take you to the page of the Setting, even if it cannot be + * changed. */ public static final int DISABLED_FOR_USER = 2; /** * The setting has a dependency in the Settings App which is currently blocking access. + *

+ * It must be possible for the Setting to be enabled by changing the configuration of the device + * settings. That is, a setting that cannot be changed because of the state of another setting. + * This should not be used for a setting that would be hidden from the UI entirely. + *

+ * Correct use: Intensity of night display should be {@link #DISABLED_DEPENDENT_SETTING} when + * night display is off. + * Incorrect use: Mobile Data is {@link #DISABLED_DEPENDENT_SETTING} when there is no + * data-enabled sim. + *

+ * Links to the Setting should take you to the page of the Setting, even if it cannot be + * changed. */ public static final int DISABLED_DEPENDENT_SETTING = 3; /** * A catch-all case for internal errors and inexplicable unavailability. + *

+ * There is no guarantee that the setting page exists, and any links to the Setting should take + * you to the home page of Settings. */ public static final int UNAVAILABLE_UNKNOWN = 4; @@ -134,9 +164,25 @@ public abstract class BasePreferenceController extends AbstractPreferenceControl return mPreferenceKey; } + /** + * @return {@code true} when the controller can be changed on the device. + * + *

+ * Will return true for {@link #AVAILABLE} and {@link #DISABLED_DEPENDENT_SETTING}. + *

+ * When the availability status returned by {@link #getAvailabilityStatus()} is + * {@link #DISABLED_DEPENDENT_SETTING}, then the setting will be disabled by default in the + * DashboardFragment, and it is up to the {@link BasePreferenceController} to enable the + * preference at the right time. + * + * TODO (mfritze) Build a dependency mechanism to allow a controller to easily define the + * dependent setting. + */ @Override public final boolean isAvailable() { - return getAvailabilityStatus() == AVAILABLE; + final int availabilityStatus = getAvailabilityStatus(); + return (availabilityStatus == AVAILABLE) || + (availabilityStatus == DISABLED_DEPENDENT_SETTING); } /** @@ -150,6 +196,21 @@ public abstract class BasePreferenceController extends AbstractPreferenceControl return getAvailabilityStatus() != DISABLED_UNSUPPORTED; } + /** + * Displays preference in this controller. + */ + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + if (getAvailabilityStatus() == DISABLED_DEPENDENT_SETTING) { + // Disable preference if it depends on another setting. + final Preference preference = screen.findPreference(getPreferenceKey()); + if (preference != null) { + preference.setEnabled(false); + } + } + } + /** * @return the UI type supported by the controller. */ diff --git a/src/com/android/settings/widget/PreferenceCategoryController.java b/src/com/android/settings/widget/PreferenceCategoryController.java index cdd814c1d8f..c6477f3b321 100644 --- a/src/com/android/settings/widget/PreferenceCategoryController.java +++ b/src/com/android/settings/widget/PreferenceCategoryController.java @@ -43,7 +43,7 @@ public class PreferenceCategoryController extends BasePreferenceController { @Override public int getAvailabilityStatus() { if (mChildren == null || mChildren.isEmpty()) { - return DISABLED_DEPENDENT_SETTING; + return DISABLED_UNSUPPORTED; } // Category is available if any child is available for (AbstractPreferenceController controller : mChildren) { @@ -51,7 +51,7 @@ public class PreferenceCategoryController extends BasePreferenceController { return AVAILABLE; } } - return DISABLED_DEPENDENT_SETTING; + return DISABLED_UNSUPPORTED; } @Override diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppMemoryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppMemoryPreferenceControllerTest.java index 6a586d198e8..64ad32b8975 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppMemoryPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppMemoryPreferenceControllerTest.java @@ -110,7 +110,7 @@ public class AppMemoryPreferenceControllerTest { Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0); assertThat(mController.getAvailabilityStatus()) - .isEqualTo(BasePreferenceController.DISABLED_DEPENDENT_SETTING); + .isEqualTo(BasePreferenceController.DISABLED_UNSUPPORTED); } @Test diff --git a/tests/robotests/src/com/android/settings/core/BasePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/core/BasePreferenceControllerTest.java index 34fbb013595..3074d9e0d5b 100644 --- a/tests/robotests/src/com/android/settings/core/BasePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/core/BasePreferenceControllerTest.java @@ -21,27 +21,37 @@ import static com.android.settings.core.BasePreferenceController.DISABLED_FOR_US import static com.android.settings.core.BasePreferenceController.DISABLED_UNSUPPORTED; import static com.android.settings.core.BasePreferenceController.UNAVAILABLE_UNKNOWN; import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import android.content.Context; + import com.android.settings.slices.SliceData; import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceGroup; +import android.support.v7.preference.PreferenceScreen; + @RunWith(SettingsRobolectricTestRunner.class) public class BasePreferenceControllerTest { - @Mock - private BasePreferenceController mPreferenceController; + private final String KEY = "fake_key"; + + private Context mContext; + private FakeBasePreferenceController mPreferenceController; @Before public void setUp() { - MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + mPreferenceController = new FakeBasePreferenceController(mContext, KEY); } @@ -57,70 +67,70 @@ public class BasePreferenceControllerTest { @Test public void isAvailable_availableStatusAvailable_returnsTrue() { - when(mPreferenceController.getAvailabilityStatus()).thenReturn(AVAILABLE); + mPreferenceController.setAvailability(AVAILABLE); assertThat(mPreferenceController.isAvailable()).isTrue(); } @Test public void isAvailable_availableStatusUnsupported_returnsFalse() { - when(mPreferenceController.getAvailabilityStatus()).thenReturn(DISABLED_UNSUPPORTED); + mPreferenceController.setAvailability(DISABLED_UNSUPPORTED); assertThat(mPreferenceController.isAvailable()).isFalse(); } @Test - public void isAvailable_availableStatusDisabled_returnsFalse() { - when(mPreferenceController.getAvailabilityStatus()).thenReturn(DISABLED_FOR_USER); + public void isAvailable_availableStatusDisabledForUser_returnsFalse() { + mPreferenceController.setAvailability(DISABLED_FOR_USER); assertThat(mPreferenceController.isAvailable()).isFalse(); } @Test public void isAvailable_availableStatusBlockedDependent_returnsFalse() { - when(mPreferenceController.getAvailabilityStatus()).thenReturn(DISABLED_DEPENDENT_SETTING); + mPreferenceController.setAvailability(DISABLED_DEPENDENT_SETTING); - assertThat(mPreferenceController.isAvailable()).isFalse(); + assertThat(mPreferenceController.isAvailable()).isTrue(); } @Test public void isAvailable_availableStatusUnavailable_returnsFalse() { - when(mPreferenceController.getAvailabilityStatus()).thenReturn(UNAVAILABLE_UNKNOWN); + mPreferenceController.setAvailability(UNAVAILABLE_UNKNOWN); assertThat(mPreferenceController.isAvailable()).isFalse(); } @Test public void isSupported_availableStatusAvailable_returnsTrue() { - when(mPreferenceController.getAvailabilityStatus()).thenReturn(AVAILABLE); + mPreferenceController.setAvailability(AVAILABLE); assertThat(mPreferenceController.isSupported()).isTrue(); } @Test public void isSupported_availableStatusUnsupported_returnsFalse() { - when(mPreferenceController.getAvailabilityStatus()).thenReturn(DISABLED_UNSUPPORTED); + mPreferenceController.setAvailability(DISABLED_UNSUPPORTED); assertThat(mPreferenceController.isSupported()).isFalse(); } @Test - public void isSupported_availableStatusDisabled_returnsTrue() { - when(mPreferenceController.getAvailabilityStatus()).thenReturn(DISABLED_FOR_USER); + public void isSupported_availableStatusDisabledForUser_returnsTrue() { + mPreferenceController.setAvailability(DISABLED_FOR_USER); assertThat(mPreferenceController.isSupported()).isTrue(); } @Test public void isSupported_availableStatusDependentSetting_returnsTrue() { - when(mPreferenceController.getAvailabilityStatus()).thenReturn(DISABLED_DEPENDENT_SETTING); + mPreferenceController.setAvailability(DISABLED_DEPENDENT_SETTING); assertThat(mPreferenceController.isSupported()).isTrue(); } @Test public void isSupported_availableStatusUnavailable_returnsTrue() { - when(mPreferenceController.getAvailabilityStatus()).thenReturn(UNAVAILABLE_UNKNOWN); + mPreferenceController.setAvailability(UNAVAILABLE_UNKNOWN); assertThat(mPreferenceController.isSupported()).isTrue(); } @@ -129,4 +139,48 @@ public class BasePreferenceControllerTest { public void getSliceType_shouldReturnIntent() { assertThat(mPreferenceController.getSliceType()).isEqualTo(SliceData.SliceType.INTENT); } + + @Test + public void settingAvailable_disabledOnDisplayPreference_preferenceEnabled() { + final PreferenceScreen screen = mock(PreferenceScreen.class); + final Preference preference = new Preference(mContext); + preference.setEnabled(true); + when(screen.findPreference(anyString())).thenReturn(preference); + + mPreferenceController.displayPreference(screen); + + assertThat(preference.isEnabled()).isTrue(); + } + + @Test + public void disabledDependentSetting_disabledOnDisplayPreference_preferenceDisabled() { + final PreferenceScreen screen = mock(PreferenceScreen.class); + final Preference preference = new Preference(mContext); + preference.setEnabled(true); + when(screen.findPreference(anyString())).thenReturn(preference); + mPreferenceController.setAvailability(DISABLED_DEPENDENT_SETTING); + + mPreferenceController.displayPreference(screen); + + assertThat(preference.isEnabled()).isFalse(); + } + + private class FakeBasePreferenceController extends BasePreferenceController { + + public int mAvailable; + + public FakeBasePreferenceController(Context context, String preferenceKey) { + super(context, preferenceKey); + mAvailable = AVAILABLE; + } + + @Override + public int getAvailabilityStatus() { + return mAvailable; + } + + public void setAvailability(int availability) { + mAvailable = availability; + } + } } \ No newline at end of file