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
diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java
index 0e47556..e7b23b9 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.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 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 dc08547..9105c10 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.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 @@
     private FaceFeatureProvider mFaceFeatureProvider;
     private WifiTrackerLibProvider mWifiTrackerLibProvider;
     private ExtraAppInfoFeatureProvider mExtraAppInfoFeatureProvider;
+    private SecuritySettingsFeatureProvider mSecuritySettingsFeatureProvider;
 
     @Override
     public SupportFeatureProvider getSupportFeatureProvider(Context context) {
@@ -313,4 +316,12 @@
         }
         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 0000000..78e4bc7
--- /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 0000000..8aba523
--- /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 4efc620..349a91d 100644
--- a/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java
+++ b/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java
@@ -19,12 +19,17 @@
 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 @@
             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 7bd7813..b7e6ffd 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.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 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 @@
         mFaceFeatureProvider = mock(FaceFeatureProvider.class);
         wifiTrackerLibProvider = mock(WifiTrackerLibProvider.class);
         extraAppInfoFeatureProvider = mock(ExtraAppInfoFeatureProvider.class);
+        securitySettingsFeatureProvider = mock(SecuritySettingsFeatureProvider.class);
     }
 
     @Override
@@ -256,4 +259,9 @@
     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 0000000..5b0c7b5
--- /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 0000000..a9acd2a
--- /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<Intent> 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 a90c9bf..b6f330b 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.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 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 @@
         mFaceFeatureProvider = mock(FaceFeatureProvider.class);
         wifiTrackerLibProvider = mock(WifiTrackerLibProvider.class);
         extraAppInfoFeatureProvider = mock(ExtraAppInfoFeatureProvider.class);
+        securitySettingsFeatureProvider = mock(SecuritySettingsFeatureProvider.class);
     }
 
     @Override
@@ -242,4 +245,9 @@
     public ExtraAppInfoFeatureProvider getExtraAppInfoFeatureProvider() {
         return extraAppInfoFeatureProvider;
     }
+
+    @Override
+    public SecuritySettingsFeatureProvider getSecuritySettingsFeatureProvider() {
+        return securitySettingsFeatureProvider;
+    }
 }