Add Night Light Suggestion and tests

Night Light settings suggestion will only show when the user
has not previously interacted with Night Light.

Bug: 37207263
Test: make ROBOTEST_FILTER=\
"(SettingsSuggestionsTest|SuggestionFeatureProviderImplTest)"\
RunSettingsRoboTests
Change-Id: I432d5fef19f5e4a52503da136b044598cb82164a
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index cbf1eae..ef77563 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -921,6 +921,26 @@
                 android:value="true" />
         </activity>
 
+        <activity android:name="Settings$NightDisplaySuggestionActivity"
+            android:enabled="@*android:bool/config_nightDisplayAvailable"
+            android:taskAffinity=""
+            android:icon="@drawable/ic_settings_night_display">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.android.settings.suggested.category.FIRST_IMPRESSION" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.dismiss"
+                android:value="6,10,30" />
+            <meta-data android:name="com.android.settings.title"
+                android:resource="@string/night_display_suggestion_title" />
+            <meta-data android:name="com.android.settings.summary"
+                android:resource="@string/night_display_suggestion_summary" />
+            <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
+                android:value="true" />
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                android:value="com.android.settings.display.NightDisplaySettings" />
+        </activity>
+
         <activity android:name="Settings$DeviceInfoSettingsActivity"
                 android:label="@string/device_info_settings"
                 android:icon="@drawable/ic_settings_about"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 65d688a..b9910f0 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -8108,6 +8108,12 @@
     <!-- Summary of condition that work mode is off [CHAR LIMIT=NONE] -->
     <string name="condition_work_summary">Apps, background sync, and other features related to your work profile are turned off.</string>
 
+    <!--  Night display: Title for the night display option Suggestion (renamed "Night Light" with title caps). [CHAR LIMIT=30] -->
+    <string name="night_display_suggestion_title">Set Night Light schedule</string>
+
+    <!--  Night display: Summary for the night display option Suggestion (renamed "Night Light" with title caps). [CHAR LIMIT=NONE] -->
+    <string name="night_display_suggestion_summary">Tint screen amber to help you fall asleep</string>
+
     <!-- Title of condition that night display is on (renamed "Night Light" with title caps). [CHAR LIMIT=30] -->
     <string name="condition_night_display_title">Night Light is on</string>
 
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 38b6e1b..7892011 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -52,6 +52,7 @@
     public static class UserDictionarySettingsActivity extends SettingsActivity { /* empty */ }
     public static class DisplaySettingsActivity extends SettingsActivity { /* empty */ }
     public static class NightDisplaySettingsActivity extends SettingsActivity { /* empty */ }
+    public static class NightDisplaySuggestionActivity extends NightDisplaySettingsActivity { /* empty */ }
     public static class DeviceInfoSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ApplicationSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ManageApplicationsActivity extends SettingsActivity { /* empty */ }
@@ -65,7 +66,7 @@
                 return true;
             }
             return super.isValidFragment(className);
-            }
+        }
     }
     public static class BackgroundCheckSummaryActivity extends SettingsActivity { /* empty */ }
     public static class StorageUseActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
index 638f85f..4b4e03f 100644
--- a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
+++ b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
@@ -21,9 +21,13 @@
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.support.annotation.NonNull;
+import android.content.ContentResolver;
+import android.provider.Settings.Secure;
+import android.support.annotation.VisibleForTesting;
 import android.util.Log;
 
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.Settings.NightDisplaySuggestionActivity;
 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.support.NewDeviceIntroSuggestionActivity;
@@ -50,6 +54,9 @@
     @Override
     public boolean isSuggestionCompleted(Context context, @NonNull ComponentName component) {
         final String className = component.getClassName();
+        if (className.equals(NightDisplaySuggestionActivity.class.getName())) {
+            return hasUsedNightDisplay(context);
+        }
         if (className.equals(NewDeviceIntroSuggestionActivity.class.getName())) {
             return NewDeviceIntroSuggestionActivity.isSuggestionComplete(context);
         }
@@ -119,4 +126,11 @@
         return packageName;
     }
 
+    @VisibleForTesting
+    boolean hasUsedNightDisplay(Context context) {
+        final ContentResolver cr = context.getContentResolver();
+        final long lastActivatedTimeMillis = Secure.getLong(cr,
+            Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, -1);
+        return lastActivatedTimeMillis > 0;
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/applications/assist/AssistFlashScreenPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/assist/AssistFlashScreenPreferenceControllerTest.java
index 7fa11cf..d68229f 100644
--- a/tests/robotests/src/com/android/settings/applications/assist/AssistFlashScreenPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/assist/AssistFlashScreenPreferenceControllerTest.java
@@ -78,8 +78,8 @@
     @Config(shadows = {ShadowSecureSettings.class})
     public void isAvailable_hasAssistantAndAllowDisclosure_shouldReturnTrue() {
         ReflectionHelpers.setField(mController, "mContext", mMockContext);
-        ShadowSecureSettings.putString(null, Settings.Secure.ASSISTANT,
-                "com.android.settings/assist");
+        final ContentResolver cr = mContext.getContentResolver();
+        Settings.Secure.putString(cr, Settings.Secure.ASSISTANT, "com.android.settings/assist");
         doReturn(true).when(mController).allowDisablingAssistDisclosure();
 
         assertThat(mController.isAvailable()).isTrue();
@@ -89,8 +89,8 @@
     @Config(shadows = {ShadowSecureSettings.class})
     public void isAvailable_hasAssistantAndDisallowDisclosure_shouldReturnTrue() {
         ReflectionHelpers.setField(mController, "mContext", mMockContext);
-        ShadowSecureSettings.putString(null, Settings.Secure.ASSISTANT,
-                "com.android.settings/assist");
+        final ContentResolver cr = mContext.getContentResolver();
+        Settings.Secure.putString(cr, Settings.Secure.ASSISTANT, "com.android.settings/assist");
         doReturn(false).when(mController).allowDisablingAssistDisclosure();
 
         assertThat(mController.isAvailable()).isFalse();
@@ -98,8 +98,7 @@
 
     @Test
     public void isAvailable_hasNoAssistant_shouldReturnFalse() {
-        Settings.Secure.putString(mContext.getContentResolver(),
-                Settings.Secure.ASSISTANT, "");
+        Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.ASSISTANT, "");
 
         assertThat(mController.isAvailable()).isFalse();
     }
diff --git a/tests/robotests/src/com/android/settings/applications/assist/DefaultAssistPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/assist/DefaultAssistPreferenceControllerTest.java
index 037bd10..f382b90 100644
--- a/tests/robotests/src/com/android/settings/applications/assist/DefaultAssistPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/assist/DefaultAssistPreferenceControllerTest.java
@@ -78,7 +78,7 @@
     @Config(shadows = {ShadowSecureSettings.class})
     public void getDefaultAppInfo_hasDefaultAssist_shouldReturnKey() {
         final String flattenKey = "com.android.settings/assist";
-        ShadowSecureSettings.putString(null, Settings.Secure.ASSISTANT, flattenKey);
+        Settings.Secure.putString(null, Settings.Secure.ASSISTANT, flattenKey);
         DefaultAppInfo appInfo = mController.getDefaultAppInfo();
 
         assertThat(appInfo.getKey()).isEqualTo(flattenKey);
@@ -87,7 +87,7 @@
     @Test
     public void getSettingIntent_noSettingsActivity_shouldNotCrash() {
         final String flattenKey = "com.android.settings/assist";
-        ShadowSecureSettings.putString(null, Settings.Secure.ASSISTANT, flattenKey);
+        Settings.Secure.putString(null, Settings.Secure.ASSISTANT, flattenKey);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         DefaultAssistPreferenceController controller =
             spy(new DefaultAssistPreferenceController(mContext));
diff --git a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java
index d5d87b1..e263e2c 100644
--- a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java
@@ -16,14 +16,19 @@
 
 package com.android.settings.dashboard.suggestions;
 
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 
+import android.provider.Settings.Secure;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.Settings.NightDisplaySuggestionActivity;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.Settings;
 import com.android.settings.TestConfig;
 import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.shadow.ShadowSecureSettings;
 import com.android.settingslib.drawer.Tile;
 import com.android.settingslib.suggestions.SuggestionParser;
 
@@ -50,7 +55,9 @@
 import static org.mockito.Mockito.when;
 
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH,
+    sdk = TestConfig.SDK_VERSION,
+    shadows = ShadowSecureSettings.class)
 public class SuggestionFeatureProviderImplTest {
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -172,4 +179,30 @@
 
         assertThat(suggestions).hasSize(3);
     }
+
+    @Test
+    public void hasUsedNightDisplay_returnsFalse_byDefault() {
+        assertThat(mProvider.hasUsedNightDisplay(mContext)).isFalse();
+    }
+
+    @Test
+    public void hasUsedNightDisplay_returnsTrue_ifPreviouslyActivated() {
+        Secure.putLong(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, 1L);
+        assertThat(mProvider.hasUsedNightDisplay(mContext)).isTrue();
+    }
+
+    @Test
+    public void nightDisplaySuggestion_isCompleted_ifPreviouslyActivated() {
+        Secure.putLong(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, 1L);
+        final ComponentName componentName =
+            new ComponentName(mContext, NightDisplaySuggestionActivity.class);
+        assertThat(mProvider.isSuggestionCompleted(mContext, componentName)).isTrue();
+    }
+
+    @Test
+    public void nightDisplaySuggestion_isNotCompleted_byDefault() {
+        final ComponentName componentName =
+            new ComponentName(mContext, NightDisplaySuggestionActivity.class);
+        assertThat(mProvider.isSuggestionCompleted(mContext, componentName)).isFalse();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionsChecksTest.java b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionsChecksTest.java
index 5f3c0f0..b46ee08 100644
--- a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionsChecksTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionsChecksTest.java
@@ -90,7 +90,6 @@
         assertThat(mSuggestionsChecks.isSuggestionComplete(tile)).isFalse();
     }
 
-
     @Test
     public void testFingerprintEnrollmentIntroductionIsCompleteWhenFingerprintNotSupported() {
         stubFingerprintSupported(false);
@@ -115,7 +114,7 @@
     }
 
     private Tile createFingerprintTile() {
-        Tile tile = new Tile();
+        final Tile tile = new Tile();
         tile.intent = new Intent();
         tile.intent.setComponent(new ComponentName(mContext,
                 Settings.FingerprintEnrollSuggestionActivity.class));
diff --git a/tests/robotests/src/com/android/settings/gestures/DoubleTwistPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/DoubleTwistPreferenceControllerTest.java
index a06395a..4d85092 100644
--- a/tests/robotests/src/com/android/settings/gestures/DoubleTwistPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/gestures/DoubleTwistPreferenceControllerTest.java
@@ -26,7 +26,6 @@
 import com.android.settings.TestConfig;
 import com.android.settings.testutils.shadow.ShadowSecureSettings;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -65,11 +64,6 @@
         mController = new DoubleTwistPreferenceController(mContext, null, KEY_DOUBLE_TWIST);
     }
 
-    @After
-    public void tearDown() {
-        ShadowSecureSettings.clear();
-    }
-
     @Test
     public void isAvailable_hasSensor_shouldReturnTrue() {
         // Mock sensors
diff --git a/tests/robotests/src/com/android/settings/language/LanguageAndInputSettingsTest.java b/tests/robotests/src/com/android/settings/language/LanguageAndInputSettingsTest.java
index b3af6b8..e229653 100644
--- a/tests/robotests/src/com/android/settings/language/LanguageAndInputSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/language/LanguageAndInputSettingsTest.java
@@ -19,6 +19,7 @@
 import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.input.InputManager;
@@ -119,7 +120,6 @@
     }
 
     @Test
-
     public void testGetPreferenceControllers_shouldAllBeCreated() {
         final List<PreferenceController> controllers =
                 mFragment.getPreferenceControllers(mActivity);
@@ -135,7 +135,8 @@
         final Activity activity = mock(Activity.class);
         final SummaryLoader loader = mock(SummaryLoader.class);
         final ComponentName componentName = new ComponentName("pkg", "cls");
-        ShadowSecureSettings.putString(null, Settings.Secure.DEFAULT_INPUT_METHOD,
+        final ContentResolver cr = activity.getContentResolver();
+        Settings.Secure.putString(cr, Settings.Secure.DEFAULT_INPUT_METHOD,
                 componentName.flattenToString());
         when(activity.getSystemService(Context.INPUT_METHOD_SERVICE))
                 .thenReturn(mInputMethodManager);
@@ -168,11 +169,12 @@
         SummaryLoader.SummaryProvider provider = mFragment.SUMMARY_PROVIDER_FACTORY
                 .createSummaryProvider(mActivity, loader);
 
-        ShadowSecureSettings.putInt(null, Settings.Secure.ASSIST_GESTURE_ENABLED, 0);
+        final ContentResolver cr = mActivity.getContentResolver();
+        Settings.Secure.putInt(cr, Settings.Secure.ASSIST_GESTURE_ENABLED, 0);
         provider.setListening(true);
         verify(mActivity).getString(R.string.language_input_gesture_summary_off);
 
-        ShadowSecureSettings.putInt(null, Settings.Secure.ASSIST_GESTURE_ENABLED, 1);
+        Settings.Secure.putInt(cr, Settings.Secure.ASSIST_GESTURE_ENABLED, 1);
         provider.setListening(true);
         verify(mActivity).getString(R.string.language_input_gesture_summary_on_with_assist);
     }
diff --git a/tests/robotests/src/com/android/settings/suggestions/SettingsSuggestionsTest.java b/tests/robotests/src/com/android/settings/suggestions/SettingsSuggestionsTest.java
index 574894e..4cc4148 100644
--- a/tests/robotests/src/com/android/settings/suggestions/SettingsSuggestionsTest.java
+++ b/tests/robotests/src/com/android/settings/suggestions/SettingsSuggestionsTest.java
@@ -79,6 +79,13 @@
                 R.string.wifi_calling_suggestion_title, R.string.wifi_calling_suggestion_summary);
     }
 
+    @Test
+    public void nightDisplaySuggestion_isValid() {
+        assertSuggestionEquals("Settings$NightDisplaySuggestionActivity",
+            CATEGORY_FIRST_IMPRESSION,
+            R.string.night_display_suggestion_title, R.string.night_display_suggestion_summary);
+    }
+
     private void assertSuggestionEquals(String activityName, String category, @StringRes int title,
             @StringRes int summary) {
         final AndroidManifest androidManifest = ShadowApplication.getInstance().getAppManifest();
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java
index 64e188e..1ae5398 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java
@@ -18,58 +18,85 @@
 
 import android.content.ContentResolver;
 import android.provider.Settings;
-
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Table;
+import java.util.Map;
+import java.util.WeakHashMap;
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
 
-import java.util.HashMap;
-import java.util.Map;
-
 @Implements(Settings.Secure.class)
 public class ShadowSecureSettings {
 
-    private static final Map<String, Object> mValueMap = new HashMap<>();
+    private static final Map<ContentResolver, Table<Integer, String, Object>> sUserDataMap =
+        new WeakHashMap<>();
 
     @Implementation
-    public static boolean putInt(ContentResolver resolver, String name, int value) {
-        mValueMap.put(name, value);
-        return true;
-    }
-
-    @Implementation
-    public static boolean putString(ContentResolver resolver, String name, String value) {
-        mValueMap.put(name, value);
-        return true;
-    }
-
-    @Implementation
-    public static String getString(ContentResolver resolver, String name) {
-        return (String) mValueMap.get(name);
+    public static boolean putStringForUser(ContentResolver resolver, String name, String value,
+        int userHandle) {
+        final Table<Integer, String, Object> userTable = getUserTable(resolver);
+        synchronized (userTable) {
+            userTable.put(userHandle, name, value);
+            return true;
+        }
     }
 
     @Implementation
     public static String getStringForUser(ContentResolver resolver, String name, int userHandle) {
-        return getString(resolver, name);
+        final Table<Integer, String, Object> userTable = getUserTable(resolver);
+        synchronized (userTable) {
+            return (String) userTable.get(userHandle, name);
+        }
     }
 
     @Implementation
-    public static boolean putIntForUser(ContentResolver cr, String name, int value,
-            int userHandle) {
-        return putInt(cr, name, value);
+    public static boolean putIntForUser(ContentResolver resolver, String name, int value,
+        int userHandle) {
+        final Table<Integer, String, Object> userTable = getUserTable(resolver);
+        synchronized (userTable) {
+            userTable.put(userHandle, name, value);
+            return true;
+        }
     }
 
     @Implementation
-    public static int getIntForUser(ContentResolver cr, String name, int def, int userHandle) {
-        return getInt(cr, name, def);
+    public static int getIntForUser(ContentResolver resolver, String name, int def,
+        int userHandle) {
+        final Table<Integer, String, Object> userTable = getUserTable(resolver);
+        synchronized (userTable) {
+            final Object object = userTable.get(userHandle, name);
+            return object instanceof Integer ? (Integer) object : def;
+        }
     }
 
     @Implementation
-    public static int getInt(ContentResolver resolver, String name, int defaultValue) {
-        Integer value = (Integer) mValueMap.get(name);
-        return value == null ? defaultValue : value;
+    public static boolean putLongForUser(ContentResolver resolver, String name, long value,
+        int userHandle) {
+        final Table<Integer, String, Object> userTable = getUserTable(resolver);
+        synchronized (userTable) {
+            userTable.put(userHandle, name, value);
+            return true;
+        }
     }
 
-    public static void clear() {
-        mValueMap.clear();
+    @Implementation
+    public static long getLongForUser(ContentResolver resolver, String name, long def,
+        int userHandle) {
+        final Table<Integer, String, Object> userTable = getUserTable(resolver);
+        synchronized (userTable) {
+            final Object object = userTable.get(userHandle, name);
+            return object instanceof Long ? (Long) object : def;
+        }
+    }
+
+    private static Table<Integer, String, Object> getUserTable(ContentResolver contentResolver) {
+        synchronized (sUserDataMap) {
+            Table<Integer, String, Object> table = sUserDataMap.get(contentResolver);
+            if (table == null) {
+                table = HashBasedTable.create();
+                sUserDataMap.put(contentResolver, table);
+            }
+            return table;
+        }
     }
 }