Merge "Expand isAvailable to include DISABLED_DEPENDENT" into pi-dev
diff --git a/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java
index 265754c..daf3992 100644
--- a/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java
@@ -109,7 +109,7 @@
         }
 
         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 fd17012..44305dd 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.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.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 @@
 
     private static final String TAG = "SettingsPrefController";
 
+    /**
+     * Denotes the availability of the Setting.
+     * <p>
+     * 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 @@
 
     /**
      * The setting is not supported by the device.
+     * <p>
+     * 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.
+     * <p>
+     * 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.
+     * <p>
+     * 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.
+     * <p>
+     * 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.
+     * <p>
+     * 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.
+     * <p>
+     * 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 @@
         return mPreferenceKey;
     }
 
+    /**
+     * @return {@code true} when the controller can be changed on the device.
+     *
+     * <p>
+     * Will return true for {@link #AVAILABLE} and {@link #DISABLED_DEPENDENT_SETTING}.
+     * <p>
+     * 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);
     }
 
     /**
@@ -151,6 +197,21 @@
     }
 
     /**
+     * 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.
      */
     @SliceData.SliceType
diff --git a/src/com/android/settings/widget/PreferenceCategoryController.java b/src/com/android/settings/widget/PreferenceCategoryController.java
index cdd814c..c6477f3 100644
--- a/src/com/android/settings/widget/PreferenceCategoryController.java
+++ b/src/com/android/settings/widget/PreferenceCategoryController.java
@@ -43,7 +43,7 @@
     @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 @@
                 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 6a586d19..64ad32b 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 @@
                 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 34fbb01..3074d9e 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_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 @@
 
     @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 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