From 2bc94d62713df640559ea9d21a3d54eb28886f5a Mon Sep 17 00:00:00 2001 From: Sean Stout Date: Thu, 18 May 2017 13:39:25 -0700 Subject: [PATCH] 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 --- AndroidManifest.xml | 20 +++++ res/values/strings.xml | 6 ++ src/com/android/settings/Settings.java | 3 +- .../SuggestionFeatureProviderImpl.java | 14 +++ ...stFlashScreenPreferenceControllerTest.java | 11 ++- ...DefaultAssistPreferenceControllerTest.java | 4 +- .../SuggestionFeatureProviderImplTest.java | 35 +++++++- .../suggestions/SuggestionsChecksTest.java | 3 +- .../DoubleTwistPreferenceControllerTest.java | 6 -- .../LanguageAndInputSettingsTest.java | 10 ++- .../suggestions/SettingsSuggestionsTest.java | 7 ++ .../shadow/ShadowSecureSettings.java | 87 ++++++++++++------- 12 files changed, 154 insertions(+), 52 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index cbf1eae63d7..ef775631b15 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -921,6 +921,26 @@ android:value="true" /> + + + + + + + + + + + + Apps, background sync, and other features related to your work profile are turned off. + + Set Night Light schedule + + + Tint screen amber to help you fall asleep + Night Light is on diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index 38b6e1b625e..7892011c17b 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -52,6 +52,7 @@ public class Settings extends SettingsActivity { 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 @@ public class Settings extends SettingsActivity { 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 638f85f94f2..4b4e03f0bc7 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.Context; 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 @@ public class SuggestionFeatureProviderImpl implements SuggestionFeatureProvider @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 @@ public class SuggestionFeatureProviderImpl implements SuggestionFeatureProvider 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 7fa11cf8418..d68229fb981 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 @@ public class AssistFlashScreenPreferenceControllerTest { @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 @@ public class AssistFlashScreenPreferenceControllerTest { @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 @@ public class AssistFlashScreenPreferenceControllerTest { @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 037bd10f37a..f382b9034b8 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 @@ public class DefaultAssistPreferenceControllerTest { @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 @@ public class DefaultAssistPreferenceControllerTest { @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 d5d87b1b34f..e263e2cc0ec 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.verifyZeroInteractions; 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 @@ public class SuggestionFeatureProviderImplTest { 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 5f3c0f0c38b..b46ee087c2c 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 @@ public class SuggestionsChecksTest { assertThat(mSuggestionsChecks.isSuggestionComplete(tile)).isFalse(); } - @Test public void testFingerprintEnrollmentIntroductionIsCompleteWhenFingerprintNotSupported() { stubFingerprintSupported(false); @@ -115,7 +114,7 @@ public class SuggestionsChecksTest { } 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 a06395a0775..4d8509211e0 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.testutils.SettingsRobolectricTestRunner; 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 @@ public class DoubleTwistPreferenceControllerTest { 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 b3af6b83b40..e229653ea29 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 @@ package com.android.settings.language; 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 @@ public class LanguageAndInputSettingsTest { } @Test - public void testGetPreferenceControllers_shouldAllBeCreated() { final List controllers = mFragment.getPreferenceControllers(mActivity); @@ -135,7 +135,8 @@ public class LanguageAndInputSettingsTest { 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 @@ public class LanguageAndInputSettingsTest { 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 574894ea168..4cc4148e826 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 @@ public class SettingsSuggestionsTest { 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 64e188eccfd..1ae5398657e 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 @@ package com.android.settings.testutils.shadow; 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 mValueMap = new HashMap<>(); + private static final Map> 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 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 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 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 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 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 userTable = getUserTable(resolver); + synchronized (userTable) { + final Object object = userTable.get(userHandle, name); + return object instanceof Long ? (Long) object : def; + } + } + + private static Table getUserTable(ContentResolver contentResolver) { + synchronized (sUserDataMap) { + Table table = sUserDataMap.get(contentResolver); + if (table == null) { + table = HashBasedTable.create(); + sUserDataMap.put(contentResolver, table); + } + return table; + } } }