Settings: Add SMS Mirroring.

Bug: 37546615

Test: Updated Robolectric suite with new unit tests.

Change-Id: I02e1723e1b125b004ff679d6242df14bca4f08ce
diff --git a/res/drawable/ic_compare_arrows_24dp.xml b/res/drawable/ic_compare_arrows_24dp.xml
new file mode 100644
index 0000000..361a930
--- /dev/null
+++ b/res/drawable/ic_compare_arrows_24dp.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9.01,14L2,14v2h7.01v3L13,15l-3.99,-4v3zM14.99,13v-3L22,10L22,8h-7.01L14.99,5L11,9l3.99,4z"/>
+</vector>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 95e6e18..835b7b4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -7747,6 +7747,9 @@
     <!-- Settings item summary for USB preference when set to entering MIDI mode [CHAR LIMIT=NONE] -->
     <string name="usb_summary_MIDI">Using device as MIDI</string>
 
+    <!-- Settings item title for SMS Mirroring preference [CHAR LIMIT=35] -->
+    <string name="sms_mirroring_pref">SMS Mirroring</string>
+
     <!-- Settings item title for background check prefs [CHAR LIMIT=35] -->
     <string name="background_check_pref">Background check</string>
 
diff --git a/res/xml/connected_devices.xml b/res/xml/connected_devices.xml
index ecbcbd1..abbeda6 100644
--- a/res/xml/connected_devices.xml
+++ b/res/xml/connected_devices.xml
@@ -39,10 +39,16 @@
         android:order="-4"/>
 
     <Preference
+        android:key="sms_mirroring"
+        android:title="@string/sms_mirroring_pref"
+        android:icon="@drawable/ic_compare_arrows_24dp"
+        android:order="-3"/>
+
+    <Preference
         android:key="usb_mode"
         android:title="@string/usb_pref"
         android:icon="@drawable/ic_usb"
-        android:order="-3">
+        android:order="-2">
         <intent android:action="android.intent.action.MAIN"
                 android:targetPackage="com.android.settings"
                 android:targetClass="com.android.settings.deviceinfo.UsbModeChooserActivity"/>
@@ -52,4 +58,4 @@
         android:key="dashboard_tile_placeholder"
         android:order="50"/>
 
-</PreferenceScreen>
\ No newline at end of file
+</PreferenceScreen>
diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
index e73da62..483b00c 100644
--- a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
@@ -30,6 +30,7 @@
 import com.android.settings.dashboard.SummaryLoader;
 import com.android.settings.deviceinfo.UsbBackend;
 import com.android.settings.nfc.NfcPreferenceController;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
 import com.android.settingslib.core.AbstractPreferenceController;
@@ -81,6 +82,12 @@
                         (SettingsActivity) getActivity());
         lifecycle.addObserver(bluetoothPreferenceController);
         controllers.add(bluetoothPreferenceController);
+
+        SmsMirroringFeatureProvider smsMirroringFeatureProvider =
+                FeatureFactory.getFactory(context).getSmsMirroringFeatureProvider();
+        AbstractPreferenceController smsMirroringController =
+                smsMirroringFeatureProvider.getController(context);
+        controllers.add(smsMirroringController);
         return controllers;
     }
 
@@ -143,6 +150,13 @@
                         keys.add(NfcPreferenceController.KEY_ANDROID_BEAM_SETTINGS);
                     }
                     keys.add(BluetoothMasterSwitchPreferenceController.KEY_TOGGLE_BLUETOOTH);
+
+                    SmsMirroringFeatureProvider smsMirroringFeatureProvider =
+                            FeatureFactory.getFactory(context).getSmsMirroringFeatureProvider();
+                    SmsMirroringPreferenceController smsMirroringController =
+                            smsMirroringFeatureProvider.getController(context);
+                    smsMirroringController.updateNonIndexableKeys(keys);
+
                     return keys;
                 }
             };
diff --git a/src/com/android/settings/connecteddevice/SmsMirroringFeatureProvider.java b/src/com/android/settings/connecteddevice/SmsMirroringFeatureProvider.java
new file mode 100644
index 0000000..9064c81
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/SmsMirroringFeatureProvider.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 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.connecteddevice;
+
+import android.content.Context;
+
+public interface SmsMirroringFeatureProvider {
+
+    /** Returns whether to show SMS mirroring. */
+    boolean shouldShowSmsMirroring(Context context);
+
+    /** Returns a preference controller for SMS mirroring. */
+    SmsMirroringPreferenceController getController(Context context);
+}
diff --git a/src/com/android/settings/connecteddevice/SmsMirroringFeatureProviderImpl.java b/src/com/android/settings/connecteddevice/SmsMirroringFeatureProviderImpl.java
new file mode 100644
index 0000000..c41d8f5
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/SmsMirroringFeatureProviderImpl.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 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.connecteddevice;
+
+import android.content.Context;
+
+public class SmsMirroringFeatureProviderImpl implements SmsMirroringFeatureProvider {
+    @Override
+    public boolean shouldShowSmsMirroring(Context context) {
+        return false;
+    }
+
+    @Override
+    public SmsMirroringPreferenceController getController(Context context) {
+        return new SmsMirroringPreferenceController(context);
+    }
+}
+
diff --git a/src/com/android/settings/connecteddevice/SmsMirroringPreferenceController.java b/src/com/android/settings/connecteddevice/SmsMirroringPreferenceController.java
new file mode 100644
index 0000000..c293481
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/SmsMirroringPreferenceController.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 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.connecteddevice;
+
+import android.content.Context;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+public class SmsMirroringPreferenceController extends AbstractPreferenceController
+        implements PreferenceControllerMixin {
+
+    static final String KEY_SMS_MIRRORING = "sms_mirroring";
+
+    private SmsMirroringFeatureProvider mFeatureProvider;
+
+    public SmsMirroringPreferenceController(Context context) {
+        super(context);
+        mFeatureProvider = FeatureFactory.getFactory(context).getSmsMirroringFeatureProvider();
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mFeatureProvider.shouldShowSmsMirroring(mContext);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_SMS_MIRRORING;
+    }
+}
diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java
index c426b58..1d0f24f 100644
--- a/src/com/android/settings/overlay/FeatureFactory.java
+++ b/src/com/android/settings/overlay/FeatureFactory.java
@@ -23,6 +23,7 @@
 import com.android.settings.R;
 import com.android.settings.applications.ApplicationFeatureProvider;
 import com.android.settings.bluetooth.BluetoothFeatureProvider;
+import com.android.settings.connecteddevice.SmsMirroringFeatureProvider;
 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.dashboard.DashboardFeatureProvider;
 import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
@@ -103,6 +104,8 @@
 
     public abstract DataPlanFeatureProvider getDataPlanFeatureProvider();
 
+    public abstract SmsMirroringFeatureProvider getSmsMirroringFeatureProvider();
+
     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 38cd635..36c3bc9 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.java
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java
@@ -29,6 +29,8 @@
 import com.android.settings.applications.PackageManagerWrapperImpl;
 import com.android.settings.bluetooth.BluetoothFeatureProvider;
 import com.android.settings.bluetooth.BluetoothFeatureProviderImpl;
+import com.android.settings.connecteddevice.SmsMirroringFeatureProvider;
+import com.android.settings.connecteddevice.SmsMirroringFeatureProviderImpl;
 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.dashboard.DashboardFeatureProvider;
 import com.android.settings.dashboard.DashboardFeatureProviderImpl;
@@ -72,6 +74,7 @@
     private UserFeatureProvider mUserFeatureProvider;
     private BluetoothFeatureProvider mBluetoothFeatureProvider;
     private DataPlanFeatureProvider mDataPlanFeatureProvider;
+    private SmsMirroringFeatureProvider mSmsMirroringFeatureProvider;
 
     @Override
     public SupportFeatureProvider getSupportFeatureProvider(Context context) {
@@ -197,4 +200,12 @@
         }
         return mAssistGestureFeatureProvider;
     }
+
+    @Override
+    public SmsMirroringFeatureProvider getSmsMirroringFeatureProvider() {
+        if (mSmsMirroringFeatureProvider == null) {
+            mSmsMirroringFeatureProvider = new SmsMirroringFeatureProviderImpl();
+        }
+        return mSmsMirroringFeatureProvider;
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java
index dbeecaa..4cb853e 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java
@@ -22,16 +22,19 @@
 import android.provider.SearchIndexableResource;
 
 import com.android.settings.R;
+import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.nfc.NfcPreferenceController;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settings.dashboard.SummaryLoader;
+import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.XmlTestUtils;
 import com.android.settingslib.drawer.CategoryKey;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
@@ -50,19 +53,45 @@
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class ConnectedDeviceDashboardFragmentTest {
 
-    @Mock
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     Context mContext;
 
     @Mock
     private PackageManager mManager;
 
+    private FakeFeatureFactory mFeatureFactory;
+    private SmsMirroringFeatureProvider mFeatureProvider;
     private ConnectedDeviceDashboardFragment mFragment;
+    private TestSmsMirroringPreferenceController mSmsMirroringPreferenceController;
+
+    private static final class TestSmsMirroringPreferenceController
+            extends SmsMirroringPreferenceController implements PreferenceControllerMixin {
+
+        private boolean mIsAvailable;
+
+        public TestSmsMirroringPreferenceController(Context context) {
+            super(context);
+        }
+
+        @Override
+        public boolean isAvailable() {
+            return mIsAvailable;
+        }
+    }
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        FakeFeatureFactory.setupForTest(mContext);
+        mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
+        mFeatureProvider = mFeatureFactory.smsMirroringFeatureProvider;
+
         mFragment = new ConnectedDeviceDashboardFragment();
         when(mContext.getPackageManager()).thenReturn(mManager);
+
+        mSmsMirroringPreferenceController = new TestSmsMirroringPreferenceController(mContext);
+        when(mFeatureProvider.getController(mContext)).thenReturn(
+                mSmsMirroringPreferenceController);
     }
 
     @Test
@@ -103,11 +132,35 @@
     }
 
     @Test
+    public void testSearchIndexProvider_NoSmsMirroring_KeyAdded() {
+        when(mFeatureProvider.shouldShowSmsMirroring(mContext)).thenReturn(false);
+        mSmsMirroringPreferenceController.mIsAvailable = false;
+
+        final List<String> keys = mFragment.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(
+                mContext);
+
+        assertThat(keys).isNotNull();
+        assertThat(keys).contains(mSmsMirroringPreferenceController.getPreferenceKey());
+    }
+
+    @Test
+    public void testSearchIndexProvider_SmsMirroring_KeyNotAdded() {
+        when(mFeatureProvider.shouldShowSmsMirroring(mContext)).thenReturn(true);
+        mSmsMirroringPreferenceController.mIsAvailable = true;
+
+        final List<String> keys = mFragment.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(
+                mContext);
+
+        assertThat(keys).isNotNull();
+        assertThat(keys).doesNotContain(mSmsMirroringPreferenceController.getPreferenceKey());
+    }
+
+    @Test
     public void testNonIndexableKeys_existInXmlLayout() {
         final Context context = RuntimeEnvironment.application;
         when(mManager.hasSystemFeature(PackageManager.FEATURE_NFC)).thenReturn(false);
         final List<String> niks = ConnectedDeviceDashboardFragment.SEARCH_INDEX_DATA_PROVIDER
-                .getNonIndexableKeys(context);
+                .getNonIndexableKeys(mContext);
         final int xmlId = (new ConnectedDeviceDashboardFragment()).getPreferenceScreenResId();
 
         final List<String> keys = XmlTestUtils.getKeysFromPreferenceXml(context, xmlId);
@@ -141,6 +194,7 @@
         final SummaryLoader summaryLoader = mock(SummaryLoader.class);
 
         when(mContext.getApplicationContext()).thenReturn(mContext);
+        when(mContext.getSystemService(NFC_SERVICE)).thenReturn(null);
 
         SummaryLoader.SummaryProvider provider =
                 new ConnectedDeviceDashboardFragment.SummaryProvider(mContext, summaryLoader);
diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
index fe7ccbe..6da7a66 100644
--- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -19,6 +19,7 @@
 
 import com.android.settings.applications.ApplicationFeatureProvider;
 import com.android.settings.bluetooth.BluetoothFeatureProvider;
+import com.android.settings.connecteddevice.SmsMirroringFeatureProvider;
 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.dashboard.DashboardFeatureProvider;
 import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
@@ -59,6 +60,7 @@
     public final AssistGestureFeatureProvider assistGestureFeatureProvider;
     public final BluetoothFeatureProvider bluetoothFeatureProvider;
     public final DataPlanFeatureProvider dataPlanFeatureProvider;
+    public final SmsMirroringFeatureProvider smsMirroringFeatureProvider;
 
     /**
      * Call this in {@code @Before} method of the test class to use fake factory.
@@ -97,6 +99,7 @@
         assistGestureFeatureProvider = mock(AssistGestureFeatureProvider.class);
         bluetoothFeatureProvider = mock(BluetoothFeatureProvider.class);
         dataPlanFeatureProvider = mock(DataPlanFeatureProvider.class);
+        smsMirroringFeatureProvider = mock(SmsMirroringFeatureProvider.class);
     }
 
     @Override
@@ -173,4 +176,9 @@
     public AssistGestureFeatureProvider getAssistGestureFeatureProvider() {
         return assistGestureFeatureProvider;
     }
+
+    @Override
+    public SmsMirroringFeatureProvider getSmsMirroringFeatureProvider() {
+        return smsMirroringFeatureProvider;
+    }
 }