From 4b302a166e81de17f1c6e24f4b827eb9b82dc6cc Mon Sep 17 00:00:00 2001 From: Marie Matheson Date: Tue, 23 Mar 2021 08:45:49 +0000 Subject: [PATCH] Adds SecuritySetting feature for providing an alternative SecuritySettings fragment Bug: 181764224 Test: New tests added to SettingsUnitTests. Test: Tested manually with and without feature-provided fragment. Test: No new failures in SettingsUnitTests, or robolectric. No failures in security directory of robolectric tests. Change-Id: I21038baf3098c18e1713a332085a5efc376f73da --- .../settings/overlay/FeatureFactory.java | 6 + .../settings/overlay/FeatureFactoryImpl.java | 11 ++ .../SecuritySettingsFeatureProvider.java | 27 ++++ .../SecuritySettingsFeatureProviderImpl.java | 31 +++++ ...evelSecurityEntryPreferenceController.java | 29 ++++ .../testutils/FakeFeatureFactory.java | 8 ++ ...curitySettingsFeatureProviderImplTest.java | 49 +++++++ ...SecurityEntryPreferenceControllerTest.java | 124 ++++++++++++++++++ .../testutils/FakeFeatureFactory.java | 8 ++ 9 files changed, 293 insertions(+) create mode 100644 src/com/android/settings/security/SecuritySettingsFeatureProvider.java create mode 100644 src/com/android/settings/security/SecuritySettingsFeatureProviderImpl.java create mode 100644 tests/unit/src/com/android/settings/security/SecuritySettingsFeatureProviderImplTest.java create mode 100644 tests/unit/src/com/android/settings/security/TopLevelSecurityEntryPreferenceControllerTest.java diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java index 0e47556565e..e7b23b9ab4b 100644 --- a/src/com/android/settings/overlay/FeatureFactory.java +++ b/src/com/android/settings/overlay/FeatureFactory.java @@ -41,6 +41,7 @@ import com.android.settings.localepicker.LocaleFeatureProvider; import com.android.settings.panel.PanelFeatureProvider; import com.android.settings.search.SearchFeatureProvider; import com.android.settings.security.SecurityFeatureProvider; +import com.android.settings.security.SecuritySettingsFeatureProvider; import com.android.settings.slices.SlicesFeatureProvider; import com.android.settings.users.UserFeatureProvider; import com.android.settings.wifi.WifiTrackerLibProvider; @@ -162,6 +163,11 @@ public abstract class FeatureFactory { */ public abstract ExtraAppInfoFeatureProvider getExtraAppInfoFeatureProvider(); + /** + * Retrieve implementation for SecuritySettings feature. + */ + public abstract SecuritySettingsFeatureProvider getSecuritySettingsFeatureProvider(); + public static final class FactoryNotFoundException extends RuntimeException { public FactoryNotFoundException(Throwable throwable) { super("Unable to create factory. Did you misconfigure Proguard?", throwable); diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java index dc085477244..9105c10df66 100644 --- a/src/com/android/settings/overlay/FeatureFactoryImpl.java +++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java @@ -63,6 +63,8 @@ import com.android.settings.search.SearchFeatureProvider; import com.android.settings.search.SearchFeatureProviderImpl; import com.android.settings.security.SecurityFeatureProvider; import com.android.settings.security.SecurityFeatureProviderImpl; +import com.android.settings.security.SecuritySettingsFeatureProvider; +import com.android.settings.security.SecuritySettingsFeatureProviderImpl; import com.android.settings.slices.SlicesFeatureProvider; import com.android.settings.slices.SlicesFeatureProviderImpl; import com.android.settings.users.UserFeatureProvider; @@ -100,6 +102,7 @@ public class FeatureFactoryImpl extends FeatureFactory { private FaceFeatureProvider mFaceFeatureProvider; private WifiTrackerLibProvider mWifiTrackerLibProvider; private ExtraAppInfoFeatureProvider mExtraAppInfoFeatureProvider; + private SecuritySettingsFeatureProvider mSecuritySettingsFeatureProvider; @Override public SupportFeatureProvider getSupportFeatureProvider(Context context) { @@ -313,4 +316,12 @@ public class FeatureFactoryImpl extends FeatureFactory { } return mExtraAppInfoFeatureProvider; } + + @Override + public SecuritySettingsFeatureProvider getSecuritySettingsFeatureProvider() { + if (mSecuritySettingsFeatureProvider == null) { + mSecuritySettingsFeatureProvider = new SecuritySettingsFeatureProviderImpl(); + } + return mSecuritySettingsFeatureProvider; + } } diff --git a/src/com/android/settings/security/SecuritySettingsFeatureProvider.java b/src/com/android/settings/security/SecuritySettingsFeatureProvider.java new file mode 100644 index 00000000000..78e4bc774c5 --- /dev/null +++ b/src/com/android/settings/security/SecuritySettingsFeatureProvider.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2021 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.security; + +/** FeatureProvider for security settings. */ +public interface SecuritySettingsFeatureProvider { + + /** Returns whether an alternative SecuritySettings fragment is available. */ + boolean hasAlternativeSecuritySettingsFragment(); + + /** Returns the alternative SecuritySettings fragment name if available. */ + String getAlternativeSecuritySettingsFragmentClassname(); +} diff --git a/src/com/android/settings/security/SecuritySettingsFeatureProviderImpl.java b/src/com/android/settings/security/SecuritySettingsFeatureProviderImpl.java new file mode 100644 index 00000000000..8aba5236e3b --- /dev/null +++ b/src/com/android/settings/security/SecuritySettingsFeatureProviderImpl.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2021 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.security; + +/** Implementation for {@code SecuritySettingsFeatureProvider}. */ +public class SecuritySettingsFeatureProviderImpl implements SecuritySettingsFeatureProvider { + + @Override + public boolean hasAlternativeSecuritySettingsFragment() { + return false; + } + + @Override + public String getAlternativeSecuritySettingsFragmentClassname() { + return null; + } +} diff --git a/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java b/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java index 4efc6200025..349a91d36b0 100644 --- a/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java +++ b/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java @@ -19,12 +19,17 @@ package com.android.settings.security; import android.content.Context; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; +import android.text.TextUtils; import android.util.FeatureFlagUtils; +import androidx.preference.Preference; + import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.core.BasePreferenceController; import com.android.settings.core.FeatureFlags; +import com.android.settings.core.SubSettingLauncher; +import com.android.settings.overlay.FeatureFactory; public class TopLevelSecurityEntryPreferenceController extends BasePreferenceController { @@ -56,4 +61,28 @@ public class TopLevelSecurityEntryPreferenceController extends BasePreferenceCon return mContext.getText(R.string.security_dashboard_summary_no_fingerprint); } } + + @Override + public boolean handlePreferenceTreeClick(Preference preference) { + if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) { + return super.handlePreferenceTreeClick(preference); + } + + SecuritySettingsFeatureProvider securitySettingsFeatureProvider = + FeatureFactory.getFactory(mContext).getSecuritySettingsFeatureProvider(); + if (securitySettingsFeatureProvider.hasAlternativeSecuritySettingsFragment()) { + String alternativeFragmentClassname = + securitySettingsFeatureProvider + .getAlternativeSecuritySettingsFragmentClassname(); + if (alternativeFragmentClassname != null) { + new SubSettingLauncher(mContext) + .setDestination(alternativeFragmentClassname) + .setSourceMetricsCategory(getMetricsCategory()) + .launch(); + return true; + } + } + + return super.handlePreferenceTreeClick(preference); + } } diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java index 7bd78138a5f..b7e6ffd1309 100644 --- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java +++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java @@ -43,6 +43,7 @@ import com.android.settings.overlay.SurveyFeatureProvider; import com.android.settings.panel.PanelFeatureProvider; import com.android.settings.search.SearchFeatureProvider; import com.android.settings.security.SecurityFeatureProvider; +import com.android.settings.security.SecuritySettingsFeatureProvider; import com.android.settings.slices.SlicesFeatureProvider; import com.android.settings.users.UserFeatureProvider; import com.android.settings.wifi.WifiTrackerLibProvider; @@ -83,6 +84,7 @@ public class FakeFeatureFactory extends FeatureFactory { public WifiTrackerLibProvider wifiTrackerLibProvider; public ExtraAppInfoFeatureProvider extraAppInfoFeatureProvider; + public SecuritySettingsFeatureProvider securitySettingsFeatureProvider; /** * Call this in {@code @Before} method of the test class to use fake factory. @@ -130,6 +132,7 @@ public class FakeFeatureFactory extends FeatureFactory { mFaceFeatureProvider = mock(FaceFeatureProvider.class); wifiTrackerLibProvider = mock(WifiTrackerLibProvider.class); extraAppInfoFeatureProvider = mock(ExtraAppInfoFeatureProvider.class); + securitySettingsFeatureProvider = mock(SecuritySettingsFeatureProvider.class); } @Override @@ -256,4 +259,9 @@ public class FakeFeatureFactory extends FeatureFactory { public ExtraAppInfoFeatureProvider getExtraAppInfoFeatureProvider() { return extraAppInfoFeatureProvider; } + + @Override + public SecuritySettingsFeatureProvider getSecuritySettingsFeatureProvider() { + return securitySettingsFeatureProvider; + } } diff --git a/tests/unit/src/com/android/settings/security/SecuritySettingsFeatureProviderImplTest.java b/tests/unit/src/com/android/settings/security/SecuritySettingsFeatureProviderImplTest.java new file mode 100644 index 00000000000..5b0c7b5f9c3 --- /dev/null +++ b/tests/unit/src/com/android/settings/security/SecuritySettingsFeatureProviderImplTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2021 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.security; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class SecuritySettingsFeatureProviderImplTest { + + private SecuritySettingsFeatureProviderImpl mSecuritySettingsFeatureProvider; + + @Before + public void setUp() { + mSecuritySettingsFeatureProvider = new SecuritySettingsFeatureProviderImpl(); + } + + @Test + public void hasAlternativeSecuritySettingsFragment_returnsFalse() { + assertThat(mSecuritySettingsFeatureProvider.hasAlternativeSecuritySettingsFragment()) + .isFalse(); + } + + @Test + public void getAlternativeSecuritySettingsFragmentClassname_returnsNull() { + String alternativeFragmentClassname = + mSecuritySettingsFeatureProvider.getAlternativeSecuritySettingsFragmentClassname(); + assertThat(alternativeFragmentClassname).isNull(); + } +} diff --git a/tests/unit/src/com/android/settings/security/TopLevelSecurityEntryPreferenceControllerTest.java b/tests/unit/src/com/android/settings/security/TopLevelSecurityEntryPreferenceControllerTest.java new file mode 100644 index 00000000000..a9acd2a6037 --- /dev/null +++ b/tests/unit/src/com/android/settings/security/TopLevelSecurityEntryPreferenceControllerTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2021 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.security; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.Intent; + +import androidx.preference.Preference; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.settings.SettingsActivity; +import com.android.settings.testutils.FakeFeatureFactory; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +public class TopLevelSecurityEntryPreferenceControllerTest { + + private static final String PREFERENCE_KEY = "top_level_security"; + private static final String ALTERNATIVE_FRAGMENT_CLASSNAME = "AlternativeFragmentClassname"; + + private TopLevelSecurityEntryPreferenceController mTopLevelSecurityEntryPreferenceController; + private Preference mPreference; + private FakeFeatureFactory mFeatureFactory; + private SecuritySettingsFeatureProvider mSecuritySettingsFeatureProvider; + + @Mock + private Context mContext; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mFeatureFactory = FakeFeatureFactory.setupForTest(); + mSecuritySettingsFeatureProvider = mFeatureFactory.getSecuritySettingsFeatureProvider(); + + mPreference = new Preference(ApplicationProvider.getApplicationContext()); + mPreference.setKey(PREFERENCE_KEY); + + doNothing().when(mContext).startActivity(any(Intent.class)); + mTopLevelSecurityEntryPreferenceController = + new TopLevelSecurityEntryPreferenceController(mContext, PREFERENCE_KEY); + } + + @Test + public void handlePreferenceTreeClick_forDifferentPreferenceKey_isNotHandled() { + Preference preference = new Preference(ApplicationProvider.getApplicationContext()); + preference.setKey("some_other_preference"); + + boolean preferenceHandled = + mTopLevelSecurityEntryPreferenceController.handlePreferenceTreeClick(preference); + + assertThat(preferenceHandled).isFalse(); + } + + @Test + public void handlePreferenceTreeClick_withAlternativeFragment_launchesAlternativeFragment() { + when(mSecuritySettingsFeatureProvider.hasAlternativeSecuritySettingsFragment()) + .thenReturn(true); + when(mSecuritySettingsFeatureProvider.getAlternativeSecuritySettingsFragmentClassname()) + .thenReturn(ALTERNATIVE_FRAGMENT_CLASSNAME); + final ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); + + boolean preferenceHandled = + mTopLevelSecurityEntryPreferenceController.handlePreferenceTreeClick(mPreference); + + assertThat(preferenceHandled).isTrue(); + verify(mContext).startActivity(intentCaptor.capture()); + assertThat(intentCaptor.getValue().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)) + .isEqualTo(ALTERNATIVE_FRAGMENT_CLASSNAME); + } + + @Test + public void handlePreferenceTreeClick_withDisabledAlternative_isNotHandled() { + when(mSecuritySettingsFeatureProvider.hasAlternativeSecuritySettingsFragment()) + .thenReturn(false); + when(mSecuritySettingsFeatureProvider.getAlternativeSecuritySettingsFragmentClassname()) + .thenReturn(ALTERNATIVE_FRAGMENT_CLASSNAME); + + boolean preferenceHandled = + mTopLevelSecurityEntryPreferenceController.handlePreferenceTreeClick(mPreference); + + assertThat(preferenceHandled).isFalse(); + } + + @Test + public void handlePreferenceTreeClick_withoutAlternativeFragmentName_isNotHandled() { + when(mSecuritySettingsFeatureProvider.hasAlternativeSecuritySettingsFragment()) + .thenReturn(true); + when(mSecuritySettingsFeatureProvider.getAlternativeSecuritySettingsFragmentClassname()) + .thenReturn(null); + + boolean preferenceHandled = + mTopLevelSecurityEntryPreferenceController.handlePreferenceTreeClick(mPreference); + + assertThat(preferenceHandled).isFalse(); + } +} diff --git a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java index a90c9bfebc1..b6f330b7c85 100644 --- a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java +++ b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java @@ -41,6 +41,7 @@ import com.android.settings.overlay.SurveyFeatureProvider; import com.android.settings.panel.PanelFeatureProvider; import com.android.settings.search.SearchFeatureProvider; import com.android.settings.security.SecurityFeatureProvider; +import com.android.settings.security.SecuritySettingsFeatureProvider; import com.android.settings.slices.SlicesFeatureProvider; import com.android.settings.users.UserFeatureProvider; import com.android.settings.wifi.WifiTrackerLibProvider; @@ -78,6 +79,7 @@ public class FakeFeatureFactory extends FeatureFactory { public WifiTrackerLibProvider wifiTrackerLibProvider; public ExtraAppInfoFeatureProvider extraAppInfoFeatureProvider; + public SecuritySettingsFeatureProvider securitySettingsFeatureProvider; /** * Call this in {@code @Before} method of the test class to use fake factory. @@ -116,6 +118,7 @@ public class FakeFeatureFactory extends FeatureFactory { mFaceFeatureProvider = mock(FaceFeatureProvider.class); wifiTrackerLibProvider = mock(WifiTrackerLibProvider.class); extraAppInfoFeatureProvider = mock(ExtraAppInfoFeatureProvider.class); + securitySettingsFeatureProvider = mock(SecuritySettingsFeatureProvider.class); } @Override @@ -242,4 +245,9 @@ public class FakeFeatureFactory extends FeatureFactory { public ExtraAppInfoFeatureProvider getExtraAppInfoFeatureProvider() { return extraAppInfoFeatureProvider; } + + @Override + public SecuritySettingsFeatureProvider getSecuritySettingsFeatureProvider() { + return securitySettingsFeatureProvider; + } }