diff --git a/res/values/strings.xml b/res/values/strings.xml index cc9d7513749..ac2ad8c9802 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -11938,18 +11938,27 @@ Show cards, passes, and device controls + + Lock screen + Don\u2019t show any content - Privacy + Sensitive content Show cards and controls when locked + + Show controls when locked + Hide cards and controls when locked + + To use, first set a screen lock + Show device controls diff --git a/res/xml/power_menu_settings.xml b/res/xml/power_menu_settings.xml index 453a7e6a1f8..c7fba713183 100644 --- a/res/xml/power_menu_settings.xml +++ b/res/xml/power_menu_settings.xml @@ -31,4 +31,13 @@ android:title="@string/device_controls_sentence" android:fragment="com.android.settings.gestures.DeviceControlsSettings" settings:controller="com.android.settings.gestures.DeviceControlsPreferenceController" /> + + + + + diff --git a/src/com/android/settings/gestures/PowerMenuPrivacyPreferenceController.java b/src/com/android/settings/gestures/PowerMenuPrivacyPreferenceController.java new file mode 100644 index 00000000000..29c6176a4f8 --- /dev/null +++ b/src/com/android/settings/gestures/PowerMenuPrivacyPreferenceController.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2020 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.gestures; + +import android.content.ContentResolver; +import android.content.Context; +import android.os.UserHandle; +import android.provider.Settings; + +import androidx.preference.Preference; + +import com.android.internal.widget.LockPatternUtils; +import com.android.settings.R; +import com.android.settings.core.TogglePreferenceController; +import com.android.settings.overlay.FeatureFactory; + +public class PowerMenuPrivacyPreferenceController extends TogglePreferenceController { + + private static final String SETTING_KEY = Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT; + private static final String CARDS_AVAILABLE_KEY = + Settings.Secure.GLOBAL_ACTIONS_PANEL_AVAILABLE; + private static final String CARDS_ENABLED_KEY = Settings.Secure.GLOBAL_ACTIONS_PANEL_ENABLED; + private static final String CONTROLS_ENABLED_KEY = Settings.Secure.CONTROLS_ENABLED; + + + public PowerMenuPrivacyPreferenceController(Context context, + String preferenceKey) { + super(context, preferenceKey); + } + + @Override + public boolean isChecked() { + return Settings.Secure.getInt(mContext.getContentResolver(), SETTING_KEY, 0) != 0; + } + + @Override + public boolean setChecked(boolean isChecked) { + return Settings.Secure.putInt(mContext.getContentResolver(), SETTING_KEY, + isChecked ? 1 : 0); + } + + @Override + public CharSequence getSummary() { + boolean cardsAvailable = Settings.Secure.getInt(mContext.getContentResolver(), + CARDS_AVAILABLE_KEY, 0) != 0; + final int res; + if (!isSecure()) { + res = R.string.power_menu_privacy_not_secure; + } else if (cardsAvailable) { + res = R.string.power_menu_privacy_show; + } else { + res = R.string.power_menu_privacy_show_controls; + } + return mContext.getText(res); + } + + @Override + public int getAvailabilityStatus() { + // hide if lockscreen isn't secure for this user + + return isEnabled() && isSecure() ? AVAILABLE : DISABLED_DEPENDENT_SETTING; + } + + @Override + public void updateState(Preference preference) { + super.updateState(preference); + preference.setEnabled(getAvailabilityStatus() != DISABLED_DEPENDENT_SETTING); + refreshSummary(preference); + } + + private boolean isEnabled() { + final ContentResolver resolver = mContext.getContentResolver(); + boolean cardsAvailable = Settings.Secure.getInt(resolver, CARDS_AVAILABLE_KEY, 0) != 0; + boolean cardsEnabled = Settings.Secure.getInt(resolver, CARDS_ENABLED_KEY, 0) != 0; + boolean controlsEnabled = Settings.Secure.getInt(resolver, CONTROLS_ENABLED_KEY, 0) != 0; + return (cardsAvailable && cardsEnabled) || controlsEnabled; + } + + private boolean isSecure() { + final LockPatternUtils utils = FeatureFactory.getFactory(mContext) + .getSecurityFeatureProvider() + .getLockPatternUtils(mContext); + int userId = UserHandle.myUserId(); + return utils.isSecure(userId); + } +} diff --git a/tests/robotests/src/com/android/settings/gestures/PowerMenuPrivacyPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/PowerMenuPrivacyPreferenceControllerTest.java new file mode 100644 index 00000000000..7891d2f9427 --- /dev/null +++ b/tests/robotests/src/com/android/settings/gestures/PowerMenuPrivacyPreferenceControllerTest.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2020 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.gestures; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.content.Context; +import android.provider.Settings; + +import androidx.preference.Preference; + +import com.android.internal.widget.LockPatternUtils; +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.testutils.FakeFeatureFactory; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class PowerMenuPrivacyPreferenceControllerTest { + + private static final String TEST_KEY = "test_key"; + private static final String SETTING_KEY = Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT; + private static final String CARDS_AVAILABLE_KEY = + Settings.Secure.GLOBAL_ACTIONS_PANEL_AVAILABLE; + private static final String CARDS_ENABLED_KEY = Settings.Secure.GLOBAL_ACTIONS_PANEL_ENABLED; + private static final String CONTROLS_ENABLED_KEY = Settings.Secure.CONTROLS_ENABLED; + + private Context mContext; + private ContentResolver mContentResolver; + private PowerMenuPrivacyPreferenceController mController; + + @Mock + private Preference mPreference; + @Mock + private LockPatternUtils mLockPatternUtils; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + mContentResolver = mContext.getContentResolver(); + FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest(); + when(featureFactory.securityFeatureProvider.getLockPatternUtils(mContext)) + .thenReturn(mLockPatternUtils); + when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true); + + mController = new PowerMenuPrivacyPreferenceController(mContext, TEST_KEY); + } + + @Test + public void isChecked_POWER_MENU_LOCKED_SHOW_CONTENTIs1_returnTrue() { + Settings.Secure.putInt(mContentResolver, SETTING_KEY, 1); + + assertThat(mController.isChecked()).isTrue(); + } + + @Test + public void isChecked_POWER_MENU_LOCKED_SHOW_CONTENTIs0_returnFalse() { + Settings.Secure.putInt(mContentResolver, SETTING_KEY, 0); + + assertThat(mController.isChecked()).isFalse(); + } + + @Test + public void isChecked_POWER_MENU_LOCKED_SHOW_CONTENTIsNotSet_returnFalse() { + Settings.Secure.putString(mContentResolver, SETTING_KEY, null); + + assertThat(mController.isChecked()).isFalse(); + } + + @Test + public void setChecked_true_POWER_MENU_LOCKED_SHOW_CONTENTIsNot0() { + mController.setChecked(true); + + assertThat(Settings.Secure.getInt(mContentResolver, SETTING_KEY, 0)).isNotEqualTo(0); + } + + @Test + public void setChecked_false_POWER_MENU_LOCKED_SHOW_CONTENTIs0() { + mController.setChecked(false); + + assertThat(Settings.Secure.getInt(mContentResolver, SETTING_KEY, 0)).isEqualTo(0); + } + + @Test + public void getSummary_notSecureLock_isPower_menu_privacy_not_secureString() { + when(mLockPatternUtils.isSecure(anyInt())).thenReturn(false); + + assertThat(mController.getSummary()).isEqualTo( + mContext.getText(R.string.power_menu_privacy_not_secure)); + } + + @Test + public void getSummary_cardsAvailable_isPower_menu_privacy_showString() { + Settings.Secure.putInt(mContentResolver, CARDS_AVAILABLE_KEY, 1); + + assertThat(mController.getSummary()).isEqualTo( + mContext.getText(R.string.power_menu_privacy_show)); + } + + @Test + public void getSummary_cardsUnavailable_isPower_menu_privacy_show_controlsString() { + Settings.Secure.putInt(mContentResolver, CARDS_AVAILABLE_KEY, 0); + + assertThat(mController.getSummary()).isEqualTo( + mContext.getText(R.string.power_menu_privacy_show_controls)); + } + + @Test + public void updateState_onPreferenceRefreshed_preferenceEnabledAndSummaryChanged() { + mController.updateState(mPreference); + + verify(mPreference).setEnabled(anyBoolean()); + verify(mPreference, atLeastOnce()).setSummary(mController.getSummary()); + } + + @Test + public void getAvailabilityStatus_allOff_returnsDisabled() { + Settings.Secure.putInt(mContentResolver, CARDS_AVAILABLE_KEY, 1); + Settings.Secure.putInt(mContentResolver, CARDS_ENABLED_KEY, 0); + Settings.Secure.putInt(mContentResolver, CONTROLS_ENABLED_KEY, 0); + + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.DISABLED_DEPENDENT_SETTING); + } + + @Test + public void getAvailabilityStatus_cardsUnavailableControlsOff_returnsDisabled() { + Settings.Secure.putInt(mContentResolver, CARDS_AVAILABLE_KEY, 0); + Settings.Secure.putInt(mContentResolver, CONTROLS_ENABLED_KEY, 0); + + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.DISABLED_DEPENDENT_SETTING); + } + + @Test + public void testAvailabilityStatus_cardsOnControlsOff_returnsAvailable() { + Settings.Secure.putInt(mContentResolver, CARDS_AVAILABLE_KEY, 1); + Settings.Secure.putInt(mContentResolver, CARDS_ENABLED_KEY, 1); + Settings.Secure.putInt(mContentResolver, CONTROLS_ENABLED_KEY, 0); + + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.AVAILABLE); + } + + @Test + public void getAvailabilityStatus_cardsOffControlsOn_returnsAvailable() { + Settings.Secure.putInt(mContentResolver, CARDS_AVAILABLE_KEY, 1); + Settings.Secure.putInt(mContentResolver, CARDS_ENABLED_KEY, 0); + Settings.Secure.putInt(mContentResolver, CONTROLS_ENABLED_KEY, 1); + + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.AVAILABLE); + } + + @Test + public void getAvailabilityStatus_allOn_returnsAvailable() { + Settings.Secure.putInt(mContentResolver, CARDS_AVAILABLE_KEY, 1); + Settings.Secure.putInt(mContentResolver, CARDS_ENABLED_KEY, 1); + Settings.Secure.putInt(mContentResolver, CONTROLS_ENABLED_KEY, 1); + + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.AVAILABLE); + } + + @Test + public void getAvailabilityStatus_allOnNotSecure_returnsDisabled() { + when(mLockPatternUtils.isSecure(anyInt())).thenReturn(false); + + Settings.Secure.putInt(mContentResolver, CARDS_AVAILABLE_KEY, 1); + Settings.Secure.putInt(mContentResolver, CARDS_ENABLED_KEY, 1); + Settings.Secure.putInt(mContentResolver, CONTROLS_ENABLED_KEY, 1); + + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.DISABLED_DEPENDENT_SETTING); + } +}