From 27a76c3988afa1ee47d235b589a5f29b92a8d338 Mon Sep 17 00:00:00 2001 From: Vadim Tryshev Date: Tue, 23 Oct 2018 12:25:28 -0700 Subject: [PATCH 1/9] Using intent action for DWB properties exposed as an API The intent opens digital wellbeing properties for an app. Bug: 118319143 Test: Building Android Change-Id: I0921ba5e8a6cb357ba12a08cafcab64503417dc9 --- .../appinfo/TimeSpentInAppPreferenceController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/applications/appinfo/TimeSpentInAppPreferenceController.java b/src/com/android/settings/applications/appinfo/TimeSpentInAppPreferenceController.java index a4cfebfcb25..70ffcb3f8e5 100644 --- a/src/com/android/settings/applications/appinfo/TimeSpentInAppPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/TimeSpentInAppPreferenceController.java @@ -21,6 +21,7 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.provider.Settings; import android.text.TextUtils; import androidx.annotation.VisibleForTesting; @@ -34,8 +35,7 @@ import java.util.List; public class TimeSpentInAppPreferenceController extends BasePreferenceController { @VisibleForTesting - static final Intent SEE_TIME_IN_APP_TEMPLATE = - new Intent("com.android.settings.action.TIME_SPENT_IN_APP"); + static final Intent SEE_TIME_IN_APP_TEMPLATE = new Intent(Settings.ACTION_APP_USAGE_SETTINGS); private final PackageManager mPackageManager; From 8228c6ee02f00cce3766f4589d60c02223044cb4 Mon Sep 17 00:00:00 2001 From: Beverly Date: Fri, 2 Nov 2018 13:13:13 -0400 Subject: [PATCH 2/9] Set modified=true when user changes zen rule name Test: make RunSettingsRoboTests -j40 Bug: 118789422 Change-Id: I3df3ce4c86a100c2c1287e3650d7fb331d5fa71c --- .../ZenAutomaticRuleHeaderPreferenceController.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/notification/ZenAutomaticRuleHeaderPreferenceController.java b/src/com/android/settings/notification/ZenAutomaticRuleHeaderPreferenceController.java index 18415c7df6e..41eb1113e1b 100644 --- a/src/com/android/settings/notification/ZenAutomaticRuleHeaderPreferenceController.java +++ b/src/com/android/settings/notification/ZenAutomaticRuleHeaderPreferenceController.java @@ -24,13 +24,10 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.service.notification.ZenModeConfig; +import android.text.TextUtils; import android.util.Slog; import android.view.View; -import androidx.fragment.app.Fragment; -import androidx.preference.Preference; -import androidx.preference.PreferenceFragmentCompat; - import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.applications.LayoutPreference; @@ -38,6 +35,10 @@ import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.core.lifecycle.Lifecycle; +import androidx.fragment.app.Fragment; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragmentCompat; + public class ZenAutomaticRuleHeaderPreferenceController extends AbstractZenModePreferenceController implements PreferenceControllerMixin { @@ -128,9 +129,13 @@ public class ZenAutomaticRuleHeaderPreferenceController extends AbstractZenModeP @Override public void onOk(String ruleName, Fragment parent) { + if (TextUtils.equals(ruleName, mRule.getName())) { + return; + } mMetricsFeatureProvider.action(mContext, MetricsProto.MetricsEvent.ACTION_ZEN_MODE_RULE_NAME_CHANGE_OK); mRule.setName(ruleName); + mRule.setModified(true); mBackend.setZenRule(mId, mRule); } } From 16c5d77df845ab06da0e288ada5aea991c20f7e5 Mon Sep 17 00:00:00 2001 From: jackqdyulei Date: Fri, 2 Nov 2018 11:25:37 -0700 Subject: [PATCH 3/9] Add null check for carrierConfig Since it may return null when Crash happens in CarrierConfigManager Bug: 118402844 Test: RunSettingsRoboTests Change-Id: I9db0f1a873b3e24936b021989f6716369569b17b --- .../network/telephony/ApnPreferenceController.java | 5 +++-- .../network/telephony/ApnPreferenceControllerTest.java | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/network/telephony/ApnPreferenceController.java b/src/com/android/settings/network/telephony/ApnPreferenceController.java index e02d9d4e8fb..f5cd1403172 100644 --- a/src/com/android/settings/network/telephony/ApnPreferenceController.java +++ b/src/com/android/settings/network/telephony/ApnPreferenceController.java @@ -62,12 +62,13 @@ public class ApnPreferenceController extends BasePreferenceController implements public int getAvailabilityStatus() { final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId); final boolean isCdmaApn = MobileNetworkUtils.isCdmaOptions(mContext, mSubId) + && carrierConfig != null && carrierConfig.getBoolean(CarrierConfigManager.KEY_SHOW_APN_SETTING_CDMA_BOOL); final boolean isGsmApn = MobileNetworkUtils.isGsmOptions(mContext, mSubId) + && carrierConfig != null && carrierConfig.getBoolean(CarrierConfigManager.KEY_APN_EXPAND_BOOL); - return carrierConfig != null - && (isCdmaApn || isGsmApn) + return isCdmaApn || isGsmApn ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; } diff --git a/tests/robotests/src/com/android/settings/network/telephony/ApnPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/ApnPreferenceControllerTest.java index dc277d2b4cc..96644285930 100644 --- a/tests/robotests/src/com/android/settings/network/telephony/ApnPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/network/telephony/ApnPreferenceControllerTest.java @@ -24,6 +24,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.Activity; import android.content.Context; @@ -114,6 +115,14 @@ public class ApnPreferenceControllerTest { assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); } + @Test + public void getAvailabilityStatus_carrierConfigNull_returnUnavailable() { + doReturn(PhoneConstants.PHONE_TYPE_GSM).when(mTelephonyManager).getPhoneType(); + when(mCarrierConfigManager.getConfigForSubId(SUB_ID)).thenReturn(null); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); + } + @Test public void handPreferenceTreeClick_fireIntent() { ArgumentCaptor captor = ArgumentCaptor.forClass(Intent.class); From f4627bb6082f7fdad187482d36e333b7e2d062b0 Mon Sep 17 00:00:00 2001 From: Rakesh Iyer Date: Fri, 2 Nov 2018 12:34:52 -0700 Subject: [PATCH 4/9] Disable controls during playback. The controls were enabled during playback earlier, however changing them did nothing to the current playout. Disable them during playout and re-enable them once playout is done. Bug: 77976922 Test: Repro case in linked bug. Change-Id: I0fbe1c97e794fccc49172ee7542018aeef956c8f --- .../android/settings/tts/TextToSpeechSettings.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/tts/TextToSpeechSettings.java b/src/com/android/settings/tts/TextToSpeechSettings.java index 866e5a8000b..cce871b4f33 100644 --- a/src/com/android/settings/tts/TextToSpeechSettings.java +++ b/src/com/android/settings/tts/TextToSpeechSettings.java @@ -251,15 +251,20 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment mTts.setOnUtteranceProgressListener(new UtteranceProgressListener() { @Override public void onStart(String utteranceId) { + updateWidgetState(false); } @Override public void onDone(String utteranceId) { + updateWidgetState(true); } @Override public void onError(String utteranceId) { Log.e(TAG, "Error while trying to synthesize sample text"); + // Re-enable just in case, although there isn't much hope that following synthesis + // requests are going to succeed. + updateWidgetState(true); } }); } @@ -696,9 +701,11 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment } private void updateWidgetState(boolean enable) { - mActionButtons.setButton1Enabled(enable); - mDefaultRatePref.setEnabled(enable); - mDefaultPitchPref.setEnabled(enable); + getActivity().runOnUiThread(() -> { + mActionButtons.setButton1Enabled(enable); + mDefaultRatePref.setEnabled(enable); + mDefaultPitchPref.setEnabled(enable); + }); } private void displayNetworkAlert() { From ef79de6ad95f9198acc08c72bd589d7c0f9090b6 Mon Sep 17 00:00:00 2001 From: jackqdyulei Date: Thu, 1 Nov 2018 13:09:33 -0700 Subject: [PATCH 5/9] Remove the unnecessary preferenceCategory In talkback, it counts the untitled preferenceCategory as an visiable item. This CL remove unnecessary cateogry and add divider line manually. Bug: 118796245 Test: RunSettingsRoboTests Change-Id: I77151fdce671cf91322b3c1badc06a3b7861dcc0 --- res/xml/wifi_settings.xml | 24 +++--- .../android/settings/wifi/WifiSettings.java | 62 ++++++++------- .../settings/wifi/WifiSettingsTest.java | 78 ++++++++++++++++++- 3 files changed, 121 insertions(+), 43 deletions(-) diff --git a/res/xml/wifi_settings.xml b/res/xml/wifi_settings.xml index 4ca92849788..516ac0a9e03 100644 --- a/res/xml/wifi_settings.xml +++ b/res/xml/wifi_settings.xml @@ -20,6 +20,9 @@ android:title="@string/wifi_settings" settings:keywords="@string/keywords_wifi"> + + @@ -28,17 +31,14 @@ android:key="access_points" android:layout="@layout/preference_category_no_label"/> - - + - - + diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java index 4837a88d8e9..f097d5b810c 100644 --- a/src/com/android/settings/wifi/WifiSettings.java +++ b/src/com/android/settings/wifi/WifiSettings.java @@ -112,9 +112,9 @@ public class WifiSettings extends RestrictedSettingsFragment private static final String PREF_KEY_EMPTY_WIFI_LIST = "wifi_empty_list"; private static final String PREF_KEY_CONNECTED_ACCESS_POINTS = "connected_access_point"; private static final String PREF_KEY_ACCESS_POINTS = "access_points"; - private static final String PREF_KEY_ADDITIONAL_SETTINGS = "additional_settings"; private static final String PREF_KEY_CONFIGURE_WIFI_SETTINGS = "configure_settings"; private static final String PREF_KEY_SAVED_NETWORKS = "saved_networks"; + private static final String PREF_KEY_STATUS_MESSAGE = "wifi_status_message"; private static boolean isVerboseLoggingEnabled() { return WifiTracker.sVerboseLogging || Log.isLoggable(TAG, Log.VERBOSE); @@ -167,17 +167,19 @@ public class WifiSettings extends RestrictedSettingsFragment private Bundle mAccessPointSavedState; private Bundle mWifiNfcDialogSavedState; - private WifiTracker mWifiTracker; + @VisibleForTesting + WifiTracker mWifiTracker; private String mOpenSsid; private AccessPointPreference.UserBadgeCache mUserBadgeCache; private PreferenceCategory mConnectedAccessPointPreferenceCategory; private PreferenceCategory mAccessPointsPreferenceCategory; - private PreferenceCategory mAdditionalSettingsPreferenceCategory; private Preference mAddPreference; - private Preference mConfigureWifiSettingsPreference; - private Preference mSavedNetworksPreference; + @VisibleForTesting + Preference mConfigureWifiSettingsPreference; + @VisibleForTesting + Preference mSavedNetworksPreference; private LinkablePreference mStatusMessagePreference; // For Search @@ -229,8 +231,6 @@ public class WifiSettings extends RestrictedSettingsFragment (PreferenceCategory) findPreference(PREF_KEY_CONNECTED_ACCESS_POINTS); mAccessPointsPreferenceCategory = (PreferenceCategory) findPreference(PREF_KEY_ACCESS_POINTS); - mAdditionalSettingsPreferenceCategory = - (PreferenceCategory) findPreference(PREF_KEY_ADDITIONAL_SETTINGS); mConfigureWifiSettingsPreference = findPreference(PREF_KEY_CONFIGURE_WIFI_SETTINGS); mSavedNetworksPreference = findPreference(PREF_KEY_SAVED_NETWORKS); @@ -238,7 +238,7 @@ public class WifiSettings extends RestrictedSettingsFragment mAddPreference = new Preference(prefContext); mAddPreference.setIcon(R.drawable.ic_menu_add); mAddPreference.setTitle(R.string.wifi_add_network); - mStatusMessagePreference = new LinkablePreference(prefContext); + mStatusMessagePreference = (LinkablePreference) findPreference(PREF_KEY_STATUS_MESSAGE); mUserBadgeCache = new AccessPointPreference.UserBadgeCache(getPackageManager()); } @@ -692,14 +692,14 @@ public class WifiSettings extends RestrictedSettingsFragment case WifiManager.WIFI_STATE_ENABLING: removeConnectedAccessPointPreference(); - mAccessPointsPreferenceCategory.removeAll(); + removeAccessPointPreference(); addMessagePreference(R.string.wifi_starting); setProgressBarVisible(true); break; case WifiManager.WIFI_STATE_DISABLING: removeConnectedAccessPointPreference(); - mAccessPointsPreferenceCategory.removeAll(); + removeAccessPointPreference(); addMessagePreference(R.string.wifi_stopping); break; @@ -746,7 +746,10 @@ public class WifiSettings extends RestrictedSettingsFragment } boolean hasAvailableAccessPoints = false; - mAccessPointsPreferenceCategory.removePreference(mStatusMessagePreference); + mStatusMessagePreference.setVisible(false); + mConnectedAccessPointPreferenceCategory.setVisible(true); + mAccessPointsPreferenceCategory.setVisible(true); + cacheRemoveAllPrefs(mAccessPointsPreferenceCategory); int index = @@ -960,26 +963,28 @@ public class WifiSettings extends RestrictedSettingsFragment unregisterCaptivePortalNetworkCallback(); } - private void setAdditionalSettingsSummaries() { - mAdditionalSettingsPreferenceCategory.addPreference(mConfigureWifiSettingsPreference); + private void removeAccessPointPreference() { + mAccessPointsPreferenceCategory.removeAll(); + mAccessPointsPreferenceCategory.setVisible(false); + } + + @VisibleForTesting + void setAdditionalSettingsSummaries() { mConfigureWifiSettingsPreference.setSummary(getString( isWifiWakeupEnabled() ? R.string.wifi_configure_settings_preference_summary_wakeup_on : R.string.wifi_configure_settings_preference_summary_wakeup_off)); - int numSavedNetworks = mWifiTracker.getNumSavedNetworks(); - if (numSavedNetworks > 0) { - mAdditionalSettingsPreferenceCategory.addPreference(mSavedNetworksPreference); - mSavedNetworksPreference.setSummary( - getResources().getQuantityString(R.plurals.wifi_saved_access_points_summary, - numSavedNetworks, numSavedNetworks)); - } else { - mAdditionalSettingsPreferenceCategory.removePreference(mSavedNetworksPreference); - } + final int numSavedNetworks = mWifiTracker.getNumSavedNetworks(); + mSavedNetworksPreference.setVisible(numSavedNetworks > 0); + mSavedNetworksPreference.setSummary( + getResources().getQuantityString(R.plurals.wifi_saved_access_points_summary, + numSavedNetworks, numSavedNetworks)); } private boolean isWifiWakeupEnabled() { - PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); - ContentResolver contentResolver = getContentResolver(); + final Context context = getContext(); + final PowerManager powerManager = context.getSystemService(PowerManager.class); + final ContentResolver contentResolver = context.getContentResolver(); return Settings.Global.getInt(contentResolver, Settings.Global.WIFI_WAKEUP_ENABLED, 0) == 1 && Settings.Global.getInt(contentResolver, @@ -1006,15 +1011,14 @@ public class WifiSettings extends RestrictedSettingsFragment .launch(); mStatusMessagePreference.setText(title, description, clickListener); removeConnectedAccessPointPreference(); - mAccessPointsPreferenceCategory.removeAll(); - mAccessPointsPreferenceCategory.addPreference(mStatusMessagePreference); + removeAccessPointPreference(); + mStatusMessagePreference.setVisible(true); } private void addMessagePreference(int messageId) { mStatusMessagePreference.setTitle(messageId); - removeConnectedAccessPointPreference(); - mAccessPointsPreferenceCategory.removeAll(); - mAccessPointsPreferenceCategory.addPreference(mStatusMessagePreference); + mStatusMessagePreference.setVisible(true); + } protected void setProgressBarVisible(boolean visible) { diff --git a/tests/robotests/src/com/android/settings/wifi/WifiSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/WifiSettingsTest.java index e9daec325d2..c9e6c1e1944 100644 --- a/tests/robotests/src/com/android/settings/wifi/WifiSettingsTest.java +++ b/tests/robotests/src/com/android/settings/wifi/WifiSettingsTest.java @@ -20,20 +20,31 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.Activity; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.os.PowerManager; +import android.provider.Settings; +import androidx.preference.Preference; + +import com.android.settings.R; import com.android.settings.search.SearchIndexableRaw; import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.wifi.WifiTracker; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.Robolectric; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @@ -42,18 +53,33 @@ import java.util.List; @RunWith(SettingsRobolectricTestRunner.class) public class WifiSettingsTest { + private static final int NUM_NETWORKS = 4; + + @Mock + private WifiTracker mWifiTracker; + @Mock + private PowerManager mPowerManager; private Context mContext; + private WifiSettings mWifiSettings; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); + + mWifiSettings = spy(new WifiSettings()); + doReturn(mContext).when(mWifiSettings).getContext(); + doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class); + mWifiSettings.mSavedNetworksPreference = new Preference(mContext); + mWifiSettings.mConfigureWifiSettingsPreference = new Preference(mContext); + mWifiSettings.mWifiTracker = mWifiTracker; } @Test public void testSearchIndexProvider_shouldIndexFragmentTitle() { final List indexRes = - WifiSettings.SEARCH_INDEX_DATA_PROVIDER.getRawDataToIndex(mContext, true /* enabled */); + WifiSettings.SEARCH_INDEX_DATA_PROVIDER.getRawDataToIndex(mContext, + true /* enabled */); assertThat(indexRes).isNotNull(); assertThat(indexRes.get(0).key).isEqualTo(WifiSettings.DATA_KEY_REFERENCE); @@ -63,7 +89,8 @@ public class WifiSettingsTest { @Config(qualifiers = "mcc999") public void testSearchIndexProvider_ifWifiSettingsNotVisible_shouldNotIndexFragmentTitle() { final List indexRes = - WifiSettings.SEARCH_INDEX_DATA_PROVIDER.getRawDataToIndex(mContext, true /* enabled */); + WifiSettings.SEARCH_INDEX_DATA_PROVIDER.getRawDataToIndex(mContext, + true /* enabled */); assertThat(indexRes).isEmpty(); } @@ -78,4 +105,51 @@ public class WifiSettingsTest { verify(wifiSettings).handleAddNetworkRequest(anyInt(), any(Intent.class)); } + + @Test + public void setAdditionalSettingsSummaries_hasSavedNetwork_preferenceVisible() { + when(mWifiTracker.getNumSavedNetworks()).thenReturn(NUM_NETWORKS); + + mWifiSettings.setAdditionalSettingsSummaries(); + + assertThat(mWifiSettings.mSavedNetworksPreference.isVisible()).isTrue(); + assertThat(mWifiSettings.mSavedNetworksPreference.getSummary()).isEqualTo( + mContext.getResources().getQuantityString( + R.plurals.wifi_saved_access_points_summary, + NUM_NETWORKS, NUM_NETWORKS)); + } + + @Test + public void setAdditionalSettingsSummaries_noSavedNetwork_preferenceInvisible() { + when(mWifiTracker.getNumSavedNetworks()).thenReturn(0); + + mWifiSettings.setAdditionalSettingsSummaries(); + + assertThat(mWifiSettings.mSavedNetworksPreference.isVisible()).isFalse(); + } + + @Test + public void setAdditionalSettingsSummaries_wifiWakeupEnabled_displayOn() { + final ContentResolver contentResolver = mContext.getContentResolver(); + Settings.Global.putInt(contentResolver, Settings.Global.WIFI_WAKEUP_ENABLED, 1); + Settings.Global.putInt(contentResolver, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 1); + Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0); + when(mPowerManager.isPowerSaveMode()).thenReturn(false); + + mWifiSettings.setAdditionalSettingsSummaries(); + + assertThat(mWifiSettings.mConfigureWifiSettingsPreference.getSummary()).isEqualTo( + mContext.getString(R.string.wifi_configure_settings_preference_summary_wakeup_on)); + } + + @Test + public void setAdditionalSettingsSummaries_wifiWakeupDisabled_displayOff() { + final ContentResolver contentResolver = mContext.getContentResolver(); + Settings.Global.putInt(contentResolver, Settings.Global.WIFI_WAKEUP_ENABLED, 0); + + mWifiSettings.setAdditionalSettingsSummaries(); + + assertThat(mWifiSettings.mConfigureWifiSettingsPreference.getSummary()).isEqualTo( + mContext.getString(R.string.wifi_configure_settings_preference_summary_wakeup_off)); + } } \ No newline at end of file From 7d1fc0d3db5f1e41aef71f081a01702c0867f6fc Mon Sep 17 00:00:00 2001 From: Emily Chuang Date: Wed, 31 Oct 2018 17:38:20 +0800 Subject: [PATCH 6/9] Update the layout of contextual cards. Fixes: 118849830 Test: visual Change-Id: Ia3f7f155271a22f4f6214fad861475442c23c2f6 --- res/layout/horizontal_divider.xml | 6 ++++-- res/layout/settings_homepage.xml | 27 --------------------------- res/values/dimens.xml | 9 +++++++-- 3 files changed, 11 insertions(+), 31 deletions(-) diff --git a/res/layout/horizontal_divider.xml b/res/layout/horizontal_divider.xml index e4a277dbca7..a13442337ee 100644 --- a/res/layout/horizontal_divider.xml +++ b/res/layout/horizontal_divider.xml @@ -19,5 +19,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/divider" android:layout_width="match_parent" - android:layout_height=".75dp" - android:background="?android:attr/dividerHorizontal" /> \ No newline at end of file + android:layout_height="@dimen/horizontal_divider_height" + android:layout_marginTop="@dimen/horizontal_divider_margin_top" + android:layout_marginBottom="@dimen/horizontal_divider_margin_bottom" + android:background="?android:attr/dividerHorizontal"/> \ No newline at end of file diff --git a/res/layout/settings_homepage.xml b/res/layout/settings_homepage.xml index bd54717367b..0140cedc117 100644 --- a/res/layout/settings_homepage.xml +++ b/res/layout/settings_homepage.xml @@ -27,32 +27,5 @@ android:layout_height="match_parent" android:layoutAnimation="@anim/layout_animation_fall_down"/> - - - - - - - diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 9b33388e3b7..f8b3e9b6840 100755 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -327,11 +327,16 @@ 8dp 2dp - 6dp - 16dp + 4dp + 8dp 16dp 16dp + + 4dp + 8dp + .75dp + 24dp From 6dc1cc2a7752f1efe642f677520febf10440756a Mon Sep 17 00:00:00 2001 From: Sunny Shao Date: Tue, 30 Oct 2018 11:55:15 +0800 Subject: [PATCH 7/9] Added the icon displayed on no account in search bar Mofidied the search_bar layout and added this icon in the right side of the search_bar. Added the hasAccount api in AvatarViewMixin and display this icon when there is no any account in device. Added the test case of the AvatarViewMixin. Bug: 117509285 Test: robotest Change-Id: I6ceaa457af4fb20acd0211ca840df5b530c47e0f --- res/drawable/ic_account_circle_24dp.xml | 27 ++++++ res/layout/search_bar.xml | 7 ++ res/values/dimens.xml | 3 + .../settings/accounts/AvatarViewMixin.java | 60 ++++++++++++ .../homepage/SettingsHomepageActivity.java | 6 ++ .../accounts/AvatarViewMixinTest.java | 94 +++++++++++++++++++ 6 files changed, 197 insertions(+) create mode 100644 res/drawable/ic_account_circle_24dp.xml create mode 100644 src/com/android/settings/accounts/AvatarViewMixin.java create mode 100644 tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java diff --git a/res/drawable/ic_account_circle_24dp.xml b/res/drawable/ic_account_circle_24dp.xml new file mode 100644 index 00000000000..8445adc5870 --- /dev/null +++ b/res/drawable/ic_account_circle_24dp.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/res/layout/search_bar.xml b/res/layout/search_bar.xml index 9e64db7295d..1ba64ad1ce0 100644 --- a/res/layout/search_bar.xml +++ b/res/layout/search_bar.xml @@ -45,5 +45,12 @@ android:layout_height="wrap_content" android:text="@string/search_menu"/> + \ No newline at end of file diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 9b33388e3b7..43cae81c384 100755 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -103,6 +103,9 @@ 16dp 2dp 64dp + 32dp + 4dp + 16dp 16dp diff --git a/src/com/android/settings/accounts/AvatarViewMixin.java b/src/com/android/settings/accounts/AvatarViewMixin.java new file mode 100644 index 00000000000..d7f6f8aa836 --- /dev/null +++ b/src/com/android/settings/accounts/AvatarViewMixin.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2018 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.accounts; + +import android.accounts.Account; +import android.content.Context; +import android.widget.ImageView; + +import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleObserver; +import androidx.lifecycle.OnLifecycleEvent; + +import com.android.settings.R; +import com.android.settings.homepage.SettingsHomepageActivity; +import com.android.settings.overlay.FeatureFactory; + +/** + * Avatar related work to the onStart method of registered observable classes + * in {@link SettingsHomepageActivity}. + */ +public class AvatarViewMixin implements LifecycleObserver { + private Context mContext; + private ImageView mAvatarView; + + public AvatarViewMixin(Context context, ImageView avatarView) { + mContext = context.getApplicationContext(); + mAvatarView = avatarView; + } + + @OnLifecycleEvent(Lifecycle.Event.ON_START) + public void onStart() { + if (hasAccount()) { + //TODO(b/117509285): To migrate account icon on search bar + } else { + mAvatarView.setImageResource(R.drawable.ic_account_circle_24dp); + } + } + + @VisibleForTesting + boolean hasAccount() { + final Account accounts[] = FeatureFactory.getFactory( + mContext).getAccountFeatureProvider().getAccounts(mContext); + return (accounts != null) && (accounts.length > 0); + } +} diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java index c10543dab6d..d3f11a0cc0b 100644 --- a/src/com/android/settings/homepage/SettingsHomepageActivity.java +++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java @@ -19,6 +19,7 @@ package com.android.settings.homepage; import android.content.Intent; import android.os.Bundle; import android.util.FeatureFlagUtils; +import android.widget.ImageView; import android.widget.Toolbar; import androidx.fragment.app.Fragment; @@ -27,6 +28,7 @@ import androidx.fragment.app.FragmentTransaction; import com.android.settings.R; import com.android.settings.SettingsActivity; +import com.android.settings.accounts.AvatarViewMixin; import com.android.settings.core.FeatureFlags; import com.android.settings.core.SettingsBaseActivity; import com.android.settings.homepage.contextualcards.ContextualCardsFragment; @@ -51,6 +53,10 @@ public class SettingsHomepageActivity extends SettingsBaseActivity { FeatureFactory.getFactory(this).getSearchFeatureProvider() .initSearchToolbar(this, toolbar); + final ImageView avatarView = findViewById(R.id.account_avatar); + final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(this, avatarView); + getLifecycle().addObserver(avatarViewMixin); + showFragment(new ContextualCardsFragment(), R.id.contextual_cards_content); showFragment(new TopLevelSettings(), R.id.main_content); } diff --git a/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java b/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java new file mode 100644 index 00000000000..c72561ed59c --- /dev/null +++ b/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2018 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.accounts; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.accounts.Account; +import android.content.Context; +import android.widget.ImageView; + +import com.android.settings.homepage.SettingsHomepageActivity; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.robolectric.Robolectric; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.android.controller.ActivityController; +import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +@RunWith(SettingsRobolectricTestRunner.class) +public class AvatarViewMixinTest { + private static final String DUMMY_ACCOUNT = "test@domain.com"; + private static final String DUMMY_DOMAIN = "domain.com"; + + private Context mContext; + private ImageView mImageView; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + mImageView = new ImageView(mContext); + } + + @Test + public void hasAccount_useDefaultAccountData_returnFalse() { + final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(mContext, mImageView); + assertThat(avatarViewMixin.hasAccount()).isFalse(); + } + + @Test + @Config(shadows = ShadowAccountFeatureProviderImpl.class) + public void hasAccount_useShadowAccountData_returnTrue() { + final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(mContext, mImageView); + assertThat(avatarViewMixin.hasAccount()).isTrue(); + } + + @Test + public void onStart_useMockAvatarViewMixin_shouldBeExecuted() { + final AvatarViewMixin mockAvatar = spy(new AvatarViewMixin(mContext, mImageView)); + + final ActivityController controller = Robolectric.buildActivity( + SettingsHomepageActivity.class).create(); + final SettingsHomepageActivity settingsHomepageActivity = + (SettingsHomepageActivity) controller.get(); + settingsHomepageActivity.getLifecycle().addObserver(mockAvatar); + controller.start(); + + verify(mockAvatar).onStart(); + } + + @Implements(AccountFeatureProviderImpl.class) + public static class ShadowAccountFeatureProviderImpl { + + @Implementation + public Account[] getAccounts(Context context) { + Account[] accounts = {new Account(DUMMY_ACCOUNT, DUMMY_DOMAIN)}; + return accounts; + } + } +} From 0c0f54f48f841cd76a5f2f822e7924c626571ba7 Mon Sep 17 00:00:00 2001 From: Beverly Date: Mon, 5 Nov 2018 10:55:07 -0500 Subject: [PATCH 8/9] Clarify notification dots/icons strings In the Custom Notification DND options, clarify what notification dots and notification icons are by adding more information in the strings Test: manual Change-Id: I54ef19d46e54ec10da7179fd142043e3a3af0a40 Fixes: 112188372 --- res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 0f3b18d7b71..37d94fecd00 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -7272,9 +7272,9 @@ Don\'t pop notifications on screen - Hide status bar icons + Hide status bar icons at top of screen - Hide notification dots + Hide notification dots on app icons Don\'t wake for notifications From a68f81540e842e432df7586c41ec3893c80e802f Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Mon, 29 Oct 2018 17:58:55 +0800 Subject: [PATCH 9/9] Enforce all the SliceBackgroundWorkers being singletons at syntax level - Create workers via reflection in SliceBackgroundWorker - Store the workers in a static container and release then at shutdown() Fixes: 118228009 Test: robolectric Change-Id: I564277d3a12b2d7d3b50cef091bdfedb3397c145 --- proguard.flags | 7 +- .../settings/slices/CustomSliceable.java | 7 +- .../slices/SettingsSliceProvider.java | 36 ++++----- .../slices/SliceBackgroundWorker.java | 65 +++++++++++++++-- src/com/android/settings/wifi/WifiSlice.java | 26 ++----- .../slices/SettingsSliceProviderTest.java | 73 +++++++++---------- 6 files changed, 124 insertions(+), 90 deletions(-) diff --git a/proguard.flags b/proguard.flags index 82e8e58d92b..b66a7862df2 100644 --- a/proguard.flags +++ b/proguard.flags @@ -60,4 +60,9 @@ # Keep classes that implements CustomSliceable, which are used by reflection. -keepclasseswithmembers class * implements com.android.settings.slices.CustomSliceable { public (android.content.Context); -} \ No newline at end of file +} + +# Keep classes that extends SliceBackgroundWorker, which are used by reflection. +-keepclasseswithmembers class * extends com.android.settings.slices.SliceBackgroundWorker { + public (android.content.Context, android.net.Uri); +} diff --git a/src/com/android/settings/slices/CustomSliceable.java b/src/com/android/settings/slices/CustomSliceable.java index e09cc7386f7..b538b898252 100644 --- a/src/com/android/settings/slices/CustomSliceable.java +++ b/src/com/android/settings/slices/CustomSliceable.java @@ -91,11 +91,12 @@ public interface CustomSliceable { /** * Settings Slices which can represent component lists that are updatable by the - * {@link SliceBackgroundWorker} returned here. + * {@link SliceBackgroundWorker} class returned here. * - * @return a {@link SliceBackgroundWorker} for fetching the list of results in the background. + * @return a {@link SliceBackgroundWorker} class for fetching the list of results in the + * background. */ - default SliceBackgroundWorker getBackgroundWorker() { + default Class getBackgroundWorkerClass() { return null; } diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java index 39116037949..33576f5ddcd 100644 --- a/src/com/android/settings/slices/SettingsSliceProvider.java +++ b/src/com/android/settings/slices/SettingsSliceProvider.java @@ -52,7 +52,6 @@ import com.android.settings.wifi.calling.WifiCallingSliceHelper; import com.android.settingslib.SliceBroadcastRelay; import com.android.settingslib.utils.ThreadUtils; -import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; @@ -134,8 +133,7 @@ public class SettingsSliceProvider extends SliceProvider { final Set mRegisteredUris = new ArraySet<>(); - final Map mWorkerMap = new ArrayMap<>(); - final Set mLiveWorkers = new ArraySet<>(); + final Map mPinnedWorkers = new ArrayMap<>(); public SettingsSliceProvider() { super(READ_SEARCH_INDEXABLES); @@ -365,45 +363,37 @@ public class SettingsSliceProvider extends SliceProvider { } private void startBackgroundWorker(CustomSliceable sliceable) { - final SliceBackgroundWorker worker = sliceable.getBackgroundWorker(); - if (worker == null) { + final Class workerClass = sliceable.getBackgroundWorkerClass(); + if (workerClass == null) { return; } final Uri uri = sliceable.getUri(); - if (mWorkerMap.containsKey(uri)) { + if (mPinnedWorkers.containsKey(uri)) { return; } Log.d(TAG, "Starting background worker for: " + uri); - mWorkerMap.put(uri, worker); - if (!mLiveWorkers.contains(worker)) { - mLiveWorkers.add(worker); - } + final SliceBackgroundWorker worker = SliceBackgroundWorker.getInstance( + getContext(), sliceable); + mPinnedWorkers.put(uri, worker); worker.onSlicePinned(); } private void stopBackgroundWorker(Uri uri) { - final SliceBackgroundWorker worker = mWorkerMap.get(uri); + final SliceBackgroundWorker worker = mPinnedWorkers.get(uri); if (worker != null) { Log.d(TAG, "Stopping background worker for: " + uri); worker.onSliceUnpinned(); - mWorkerMap.remove(uri); + mPinnedWorkers.remove(uri); } } @Override public void shutdown() { - for (SliceBackgroundWorker worker : mLiveWorkers) { - ThreadUtils.postOnMainThread(() -> { - try { - worker.close(); - } catch (IOException e) { - Log.w(TAG, "Exception when shutting down worker", e); - } - }); - } - mLiveWorkers.clear(); + ThreadUtils.postOnMainThread(() -> { + SliceBackgroundWorker.shutdown(); + }); } private List buildUrisFromKeys(List keys, String authority) { @@ -532,4 +522,4 @@ public class SettingsSliceProvider extends SliceProvider { } return new String[0]; } -} \ No newline at end of file +} diff --git a/src/com/android/settings/slices/SliceBackgroundWorker.java b/src/com/android/settings/slices/SliceBackgroundWorker.java index 422fcc7b921..a663eceb1df 100644 --- a/src/com/android/settings/slices/SliceBackgroundWorker.java +++ b/src/com/android/settings/slices/SliceBackgroundWorker.java @@ -17,35 +17,86 @@ package com.android.settings.slices; import android.annotation.MainThread; -import android.content.ContentResolver; +import android.content.Context; import android.net.Uri; +import android.util.ArrayMap; +import android.util.Log; import java.io.Closeable; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * The Slice background worker is used to make Settings Slices be able to work with data that is * changing continuously, e.g. available Wi-Fi networks. * - * The background worker will be started at {@link SettingsSliceProvider#onSlicePinned(Uri)}, and be - * stopped at {@link SettingsSliceProvider#onSliceUnpinned(Uri)}. + * The background worker will be started at {@link SettingsSliceProvider#onSlicePinned(Uri)}, be + * stopped at {@link SettingsSliceProvider#onSliceUnpinned(Uri)}, and be closed at {@link + * SettingsSliceProvider#shutdown()}. * * {@link SliceBackgroundWorker} caches the results, uses the cache to compare if there is any data * changed, and then notifies the Slice {@link Uri} to update. + * + * It also stores all instances of all workers to ensure each worker is a Singleton. */ public abstract class SliceBackgroundWorker implements Closeable { - private final ContentResolver mContentResolver; + private static final String TAG = "SliceBackgroundWorker"; + + private static final Map LIVE_WORKERS = new ArrayMap<>(); + + private final Context mContext; private final Uri mUri; private List mCachedResults; - protected SliceBackgroundWorker(ContentResolver cr, Uri uri) { - mContentResolver = cr; + protected SliceBackgroundWorker(Context context, Uri uri) { + mContext = context; mUri = uri; } + /** + * Returns the singleton instance of the {@link SliceBackgroundWorker} for specified {@link + * CustomSliceable} + */ + public static SliceBackgroundWorker getInstance(Context context, CustomSliceable sliceable) { + final Uri uri = sliceable.getUri(); + final Class workerClass = + sliceable.getBackgroundWorkerClass(); + SliceBackgroundWorker worker = LIVE_WORKERS.get(uri); + if (worker == null) { + worker = createInstance(context, uri, workerClass); + LIVE_WORKERS.put(uri, worker); + } + return worker; + } + + private static SliceBackgroundWorker createInstance(Context context, Uri uri, + Class clazz) { + Log.d(TAG, "create instance: " + clazz); + try { + return clazz.getConstructor(Context.class, Uri.class).newInstance(context, uri); + } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | + InvocationTargetException e) { + throw new IllegalStateException( + "Invalid slice background worker: " + clazz, e); + } + } + + static void shutdown() { + for (SliceBackgroundWorker worker : LIVE_WORKERS.values()) { + try { + worker.close(); + } catch (IOException e) { + Log.w(TAG, "Shutting down worker failed", e); + } + } + LIVE_WORKERS.clear(); + } + /** * Called when the Slice is pinned. This is the place to register callbacks or initialize scan * tasks. @@ -83,7 +134,7 @@ public abstract class SliceBackgroundWorker implements Closeable { if (needNotify) { mCachedResults = results; - mContentResolver.notifyChange(mUri, null); + mContext.getContentResolver().notifyChange(mUri, null); } } } diff --git a/src/com/android/settings/wifi/WifiSlice.java b/src/com/android/settings/wifi/WifiSlice.java index 43f5372981e..d06d830af39 100644 --- a/src/com/android/settings/wifi/WifiSlice.java +++ b/src/com/android/settings/wifi/WifiSlice.java @@ -121,7 +121,7 @@ public class WifiSlice implements CustomSliceable { return listBuilder.build(); } - List results = getBackgroundWorker().getResults(); + List results = SliceBackgroundWorker.getInstance(mContext, this).getResults(); if (results == null) { results = new ArrayList<>(); } @@ -264,36 +264,27 @@ public class WifiSlice implements CustomSliceable { } @Override - public SliceBackgroundWorker getBackgroundWorker() { - return WifiScanWorker.getInstance(mContext, WIFI_URI); + public Class getBackgroundWorkerClass() { + return WifiScanWorker.class; } - private static class WifiScanWorker extends SliceBackgroundWorker + public static class WifiScanWorker extends SliceBackgroundWorker implements WifiTracker.WifiListener { - // TODO: enforce all the SliceBackgroundWorkers being singletons at syntax level - private static WifiScanWorker mWifiScanWorker; - private final Context mContext; private WifiTracker mWifiTracker; - private WifiScanWorker(Context context, Uri uri) { - super(context.getContentResolver(), uri); + public WifiScanWorker(Context context, Uri uri) { + super(context, uri); mContext = context; } - public static WifiScanWorker getInstance(Context context, Uri uri) { - if (mWifiScanWorker == null) { - mWifiScanWorker = new WifiScanWorker(context, uri); - } - return mWifiScanWorker; - } - @Override protected void onSlicePinned() { if (mWifiTracker == null) { - mWifiTracker = new WifiTracker(mContext, this, true, true); + mWifiTracker = new WifiTracker(mContext, this /* wifiListener */, + true /* includeSaved */, true /* includeScans */); } mWifiTracker.onStart(); onAccessPointsChanged(); @@ -307,7 +298,6 @@ public class WifiSlice implements CustomSliceable { @Override public void close() { mWifiTracker.onDestroy(); - mWifiScanWorker = null; } @Override diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java index a0fd21be3d0..3c2cbdbb036 100644 --- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java +++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java @@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -60,6 +61,7 @@ import com.android.settings.testutils.shadow.ShadowThreadUtils; import com.android.settings.testutils.shadow.ShadowUserManager; import com.android.settings.testutils.shadow.ShadowUtils; import com.android.settings.wifi.WifiSlice; +import com.android.settingslib.wifi.WifiTracker; import org.junit.After; import org.junit.Before; @@ -75,7 +77,6 @@ import org.robolectric.annotation.Resetter; import org.robolectric.shadow.api.Shadow; import org.robolectric.shadows.ShadowAccessibilityManager; -import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -91,7 +92,8 @@ import java.util.Set; @RunWith(SettingsRobolectricTestRunner.class) @Config(shadows = {ShadowUserManager.class, ShadowThreadUtils.class, ShadowUtils.class, SlicesDatabaseAccessorTest.ShadowApplicationPackageManager.class, - ShadowBluetoothAdapter.class, ShadowLockPatternUtils.class}) + ShadowBluetoothAdapter.class, ShadowLockPatternUtils.class, + SettingsSliceProviderTest.ShadowWifiScanWorker.class}) public class SettingsSliceProviderTest { private static final String KEY = "KEY"; @@ -135,7 +137,7 @@ public class SettingsSliceProviderTest { mProvider.mSliceWeakDataCache = new HashMap<>(); mProvider.mSliceDataCache = new HashMap<>(); mProvider.mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(mContext); - mProvider.mCustomSliceManager = spy(new CustomSliceManager(mContext)); + mProvider.mCustomSliceManager = new CustomSliceManager(mContext); when(mProvider.getContext()).thenReturn(mContext); SlicesDatabaseHelper.getInstance(mContext).setIndexedState(); @@ -497,57 +499,52 @@ public class SettingsSliceProviderTest { mProvider.onSlicePinned(uri); } - private SliceBackgroundWorker initBackgroundWorker(Uri uri) { - final SliceBackgroundWorker worker = spy(new SliceBackgroundWorker( - mContext.getContentResolver(), uri) { - @Override - protected void onSlicePinned() { - } + @Implements(WifiSlice.WifiScanWorker.class) + public static class ShadowWifiScanWorker { + private static WifiTracker mWifiTracker; - @Override - protected void onSliceUnpinned() { - } + @Implementation + protected void onSlicePinned() { + mWifiTracker = mock(WifiTracker.class); + mWifiTracker.onStart(); + } - @Override - public void close() { - } - }); - final WifiSlice wifiSlice = spy(new WifiSlice(mContext)); - when(wifiSlice.getBackgroundWorker()).thenReturn(worker); - when(mProvider.mCustomSliceManager.getSliceableFromUri(uri)).thenReturn(wifiSlice); - return worker; + @Implementation + protected void onSliceUnpinned() { + mWifiTracker.onStop(); + } + + @Implementation + public void close() { + mWifiTracker.onDestroy(); + } + + static WifiTracker getWifiTracker() { + return mWifiTracker; + } } @Test public void onSlicePinned_backgroundWorker_started() { - final Uri uri = WifiSlice.WIFI_URI; - final SliceBackgroundWorker worker = initBackgroundWorker(uri); + mProvider.onSlicePinned(WifiSlice.WIFI_URI); - mProvider.onSlicePinned(uri); - - verify(worker).onSlicePinned(); + verify(ShadowWifiScanWorker.getWifiTracker()).onStart(); } @Test public void onSlicePinned_backgroundWorker_stopped() { - final Uri uri = WifiSlice.WIFI_URI; - final SliceBackgroundWorker worker = initBackgroundWorker(uri); + mProvider.onSlicePinned(WifiSlice.WIFI_URI); + mProvider.onSliceUnpinned(WifiSlice.WIFI_URI); - mProvider.onSlicePinned(uri); - mProvider.onSliceUnpinned(uri); - - verify(worker).onSliceUnpinned(); + verify(ShadowWifiScanWorker.getWifiTracker()).onStop(); } @Test - public void shutdown_backgroundWorker_closed() throws IOException { - final Uri uri = WifiSlice.WIFI_URI; - final SliceBackgroundWorker worker = initBackgroundWorker(uri); - - mProvider.onSlicePinned(uri); + public void shutdown_backgroundWorker_closed() { + mProvider.onSlicePinned(WifiSlice.WIFI_URI); mProvider.shutdown(); - verify(worker).close(); + verify(ShadowWifiScanWorker.getWifiTracker()).onDestroy(); } @Test @@ -630,4 +627,4 @@ public class SettingsSliceProviderTest { return sSetThreadPolicyCount != 0; } } -} \ No newline at end of file +}