Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8c48defb authored by Matthew Fritze's avatar Matthew Fritze
Browse files

Expand isAvailable to include DISABLED_DEPENDENT

isAvailable will now return true when getAvailabilityStatus
returns DISABLED_DEPENDENT_SETTING. This is because the setting
should be displayed in the Fragment even if it has a dependent setting,
which matches existing behaviour. Slices will still display the warning
slice without inline content, where the main action brings you to the
setting page. For now, we have to assume the user will be able to figure
out how to enable the setting. In Q, we would like to build a more
intelligent flow so that we can prompt or even help the user fix the
dependency (we just ran out of time in P).

The only setting that had previously used DISABLE_DEPENDENT_SETTING
was a developer option.

Change-Id: I1f774a2e09cb60de01388cf6c35785c8b5dea176
Fixes: 77334915
Test: robotests
parent e35e0071
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -109,7 +109,7 @@ public class AppMemoryPreferenceController extends BasePreferenceController
        }

        return DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)
                ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
                ? AVAILABLE : DISABLED_UNSUPPORTED;
    }

    @Override
+63 −2
Original line number Diff line number Diff line
@@ -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.
     * <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 @@ public abstract class BasePreferenceController extends AbstractPreferenceControl

    /**
     * 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 @@ public abstract class BasePreferenceController extends AbstractPreferenceControl
        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);
    }

    /**
@@ -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.
     */
+2 −2
Original line number Diff line number Diff line
@@ -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
+1 −1
Original line number Diff line number Diff line
@@ -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
+72 −18
Original line number Diff line number Diff line
@@ -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