diff --git a/res/drawable/ic_info_outline_24dp.xml b/res/drawable/ic_info_outline_24dp.xml new file mode 100644 index 00000000000..d1f27abd6e1 --- /dev/null +++ b/res/drawable/ic_info_outline_24dp.xml @@ -0,0 +1,32 @@ + + + + + + + diff --git a/res/layout/wifi_settings_scanning_required_view.xml b/res/layout/wifi_settings_scanning_required_view.xml new file mode 100644 index 00000000000..8dad9f455d8 --- /dev/null +++ b/res/layout/wifi_settings_scanning_required_view.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 2857e6d7491..b3ae59f09a7 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1916,9 +1916,11 @@ To turn on Wi\u2011Fi automatically, you first need to turn on Wi\u2011Fi scanning. - Wi\2011Fi scanning allows apps and services to scan for Wi\u2011Fi networks at any time, even when Wi\u2011Fi is off. This can be used, for example, to improve location\u2011based features and services. + Wi\u2011Fi scanning allows apps and services to scan for Wi\u2011Fi networks at any time, even when Wi\u2011Fi is off. This can be used, for example, to improve location\u2011based features and services. Turn on + + Wi\u2011Fi scanning turned on diff --git a/src/com/android/settings/wifi/ConfigureWifiSettings.java b/src/com/android/settings/wifi/ConfigureWifiSettings.java index 0938f67acc7..96b2d03c3dc 100644 --- a/src/com/android/settings/wifi/ConfigureWifiSettings.java +++ b/src/com/android/settings/wifi/ConfigureWifiSettings.java @@ -41,6 +41,7 @@ public class ConfigureWifiSettings extends DashboardFragment { private static final String TAG = "ConfigureWifiSettings"; public static final String KEY_IP_ADDRESS = "current_ip_address"; + public static final int WIFI_WAKEUP_REQUEST_CODE = 600; private WifiWakeupPreferenceController mWifiWakeupPreferenceController; private UseOpenWifiPreferenceController mUseOpenWifiPreferenceController; @@ -71,7 +72,7 @@ public class ConfigureWifiSettings extends DashboardFragment { @Override protected List createPreferenceControllers(Context context) { - mWifiWakeupPreferenceController = new WifiWakeupPreferenceController(context); + mWifiWakeupPreferenceController = new WifiWakeupPreferenceController(context, this); mUseOpenWifiPreferenceController = new UseOpenWifiPreferenceController(context, this, getLifecycle()); final WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE); @@ -87,10 +88,16 @@ public class ConfigureWifiSettings extends DashboardFragment { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (mUseOpenWifiPreferenceController == null || - !mUseOpenWifiPreferenceController.onActivityResult(requestCode, resultCode)) { - super.onActivityResult(requestCode, resultCode, data); + if (resultCode == WIFI_WAKEUP_REQUEST_CODE && mWifiWakeupPreferenceController != null) { + mWifiWakeupPreferenceController.onActivityResult(requestCode, resultCode); + return; } + if (resultCode == UseOpenWifiPreferenceController.REQUEST_CODE_OPEN_WIFI_AUTOMATICALLY + && mUseOpenWifiPreferenceController == null) { + mUseOpenWifiPreferenceController.onActivityResult(requestCode, resultCode); + return; + } + super.onActivityResult(requestCode, resultCode, data); } public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = diff --git a/src/com/android/settings/wifi/UseOpenWifiPreferenceController.java b/src/com/android/settings/wifi/UseOpenWifiPreferenceController.java index 350f9d1b440..5a21bb76e3a 100644 --- a/src/com/android/settings/wifi/UseOpenWifiPreferenceController.java +++ b/src/com/android/settings/wifi/UseOpenWifiPreferenceController.java @@ -36,9 +36,9 @@ import java.util.List; public class UseOpenWifiPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause { + public static final int REQUEST_CODE_OPEN_WIFI_AUTOMATICALLY = 400; + private static final String KEY_USE_OPEN_WIFI_AUTOMATICALLY = "use_open_wifi_automatically"; - @VisibleForTesting - static final int REQUEST_CODE_OPEN_WIFI_AUTOMATICALLY = 400; private final ContentResolver mContentResolver; private final Fragment mFragment; diff --git a/src/com/android/settings/wifi/WifiScanningRequiredFragment.java b/src/com/android/settings/wifi/WifiScanningRequiredFragment.java new file mode 100644 index 00000000000..e0b804aec26 --- /dev/null +++ b/src/com/android/settings/wifi/WifiScanningRequiredFragment.java @@ -0,0 +1,87 @@ +/* + * 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.wifi; + +import static com.android.settings.wifi.ConfigureWifiSettings.WIFI_WAKEUP_REQUEST_CODE; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.ContentResolver; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.provider.Settings; +import android.widget.Toast; + +import com.android.settings.R; +import com.android.settings.core.instrumentation.InstrumentedDialogFragment; + +public class WifiScanningRequiredFragment extends InstrumentedDialogFragment implements + DialogInterface.OnClickListener { + + public static WifiScanningRequiredFragment newInstance() { + WifiScanningRequiredFragment fragment = new WifiScanningRequiredFragment(); + return fragment; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return new AlertDialog.Builder(getContext()) + .setTitle(R.string.wifi_settings_scanning_required_title) + .setView(R.layout.wifi_settings_scanning_required_view) + .setNeutralButton(R.string.do_disclosure_learn_more, this) + .setPositiveButton(R.string.wifi_settings_scanning_required_turn_on, this) + .setNegativeButton(R.string.cancel, null) + .create(); + } + + @Override + public int getMetricsCategory() { + // TODO(b/67070896): add metric code + return 0; + } + + @Override + public void onClick(DialogInterface dialog, int which) { + Context context = getContext(); + ContentResolver contentResolver = context.getContentResolver(); + switch(which) { + case DialogInterface.BUTTON_POSITIVE: + Settings.Global.putInt(contentResolver, + Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 1); + Toast.makeText( + context, + context.getString(R.string.wifi_settings_scanning_required_enabled), + Toast.LENGTH_SHORT).show(); + getTargetFragment().onActivityResult( + getTargetRequestCode(), + Activity.RESULT_OK, + null); + break; + case DialogInterface.BUTTON_NEUTRAL: + openHelpPage(); + break; + case DialogInterface.BUTTON_NEGATIVE: + default: + // do nothing + } + } + + private void openHelpPage() { + // TODO(b/67070896): actually open help page on Pixel only + } +} diff --git a/src/com/android/settings/wifi/WifiWakeupPreferenceController.java b/src/com/android/settings/wifi/WifiWakeupPreferenceController.java index 5a9f6aea2ce..3eac471c08d 100644 --- a/src/com/android/settings/wifi/WifiWakeupPreferenceController.java +++ b/src/com/android/settings/wifi/WifiWakeupPreferenceController.java @@ -16,8 +16,15 @@ package com.android.settings.wifi; +import static com.android.settings.wifi.ConfigureWifiSettings.WIFI_WAKEUP_REQUEST_CODE; + +import android.app.Fragment; +import android.app.Service; import android.content.Context; +import android.content.Intent; +import android.location.LocationManager; import android.provider.Settings; +import android.support.annotation.VisibleForTesting; import android.support.v14.preference.SwitchPreference; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceScreen; @@ -25,24 +32,37 @@ import android.text.TextUtils; import com.android.settings.R; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.utils.AnnotationSpan; import com.android.settingslib.core.AbstractPreferenceController; /** * {@link PreferenceControllerMixin} that controls whether the Wi-Fi Wakeup feature should be * enabled. */ -public class WifiWakeupPreferenceController extends AbstractPreferenceController - implements PreferenceControllerMixin { +public class WifiWakeupPreferenceController extends AbstractPreferenceController { + private static final String TAG = "WifiWakeupPrefController"; private static final String KEY_ENABLE_WIFI_WAKEUP = "enable_wifi_wakeup"; - public WifiWakeupPreferenceController(Context context) { + private final Fragment mFragment; + + @VisibleForTesting + SwitchPreference mPreference; + @VisibleForTesting + LocationManager mLocationManager; + + public WifiWakeupPreferenceController(Context context, DashboardFragment fragment) { super(context); + mFragment = fragment; + mLocationManager = (LocationManager) context.getSystemService(Service.LOCATION_SERVICE); } @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); + mPreference = (SwitchPreference) screen.findPreference(KEY_ENABLE_WIFI_WAKEUP); + updateState(mPreference); } @Override @@ -58,9 +78,19 @@ public class WifiWakeupPreferenceController extends AbstractPreferenceController if (!(preference instanceof SwitchPreference)) { return false; } - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.WIFI_WAKEUP_ENABLED, - ((SwitchPreference) preference).isChecked() ? 1 : 0); + + if (!mLocationManager.isLocationEnabled()) { + final Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); + mFragment.startActivity(intent); + } else if (getWifiWakeupEnabled()) { + setWifiWakeupEnabled(false); + } else if (!getWifiScanningEnabled()) { + showScanningDialog(); + } else { + setWifiWakeupEnabled(true); + } + + updateState(mPreference); return true; } @@ -76,17 +106,51 @@ public class WifiWakeupPreferenceController extends AbstractPreferenceController } final SwitchPreference enableWifiWakeup = (SwitchPreference) preference; - enableWifiWakeup.setChecked(Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.WIFI_WAKEUP_ENABLED, 0) == 1); - - boolean wifiScanningEnabled = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1; - enableWifiWakeup.setEnabled(wifiScanningEnabled); - - if (wifiScanningEnabled) { - enableWifiWakeup.setSummary(R.string.wifi_wakeup_summary); + enableWifiWakeup.setChecked(getWifiWakeupEnabled() + && getWifiScanningEnabled() + && mLocationManager.isLocationEnabled()); + if (!mLocationManager.isLocationEnabled()) { + preference.setSummary(getNoLocationSummary()); } else { - enableWifiWakeup.setSummary(R.string.wifi_wakeup_summary_scanning_disabled); + preference.setSummary(R.string.wifi_wakeup_summary); } } + + @VisibleForTesting CharSequence getNoLocationSummary() { + AnnotationSpan.LinkInfo linkInfo = new AnnotationSpan.LinkInfo("link", null); + CharSequence locationText = mContext.getText(R.string.wifi_wakeup_summary_no_location); + return AnnotationSpan.linkify(locationText, linkInfo); + } + + public void onActivityResult(int requestCode, int resultCode) { + if (requestCode != WIFI_WAKEUP_REQUEST_CODE) { + return; + } + if (mLocationManager.isLocationEnabled()) { + setWifiWakeupEnabled(true); + } + updateState(mPreference); + } + + private boolean getWifiScanningEnabled() { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1; + } + + private void showScanningDialog() { + final WifiScanningRequiredFragment dialogFragment = + WifiScanningRequiredFragment.newInstance(); + dialogFragment.setTargetFragment(mFragment, WIFI_WAKEUP_REQUEST_CODE /* requestCode */); + dialogFragment.show(mFragment.getFragmentManager(), TAG); + } + + private boolean getWifiWakeupEnabled() { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.WIFI_WAKEUP_ENABLED, 0) == 1; + } + + private void setWifiWakeupEnabled(boolean enabled) { + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.WIFI_WAKEUP_ENABLED, + enabled ? 1 : 0); + } } diff --git a/tests/robotests/src/com/android/settings/wifi/WifiScanningRequiredFragmentTest.java b/tests/robotests/src/com/android/settings/wifi/WifiScanningRequiredFragmentTest.java new file mode 100644 index 00000000000..f9d2176c199 --- /dev/null +++ b/tests/robotests/src/com/android/settings/wifi/WifiScanningRequiredFragmentTest.java @@ -0,0 +1,84 @@ +/* + * 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.wifi; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.app.Fragment; +import android.content.ContentResolver; +import android.content.Context; +import android.content.DialogInterface; +import android.provider.Settings; + +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.shadow.SettingsShadowResources; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; + +@RunWith(SettingsRobolectricTestRunner.class) +public class WifiScanningRequiredFragmentTest { + + private WifiScanningRequiredFragment mFragment; + private Context mContext; + private ContentResolver mResolver; + @Mock + Fragment mCallbackFragment; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mFragment = spy(WifiScanningRequiredFragment.newInstance()); + mContext = RuntimeEnvironment.application; + mResolver = mContext.getContentResolver(); + + doReturn(mContext).when(mFragment).getContext(); + mFragment.setTargetFragment(mCallbackFragment, 1000); + Settings.Global.putInt(mResolver, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0); + } + + @After + public void tearDown() { + SettingsShadowResources.reset(); + } + + @Test + public void onClick_positiveButtonSetsWifiScanningOn() + throws Settings.SettingNotFoundException { + mFragment.onClick(null, DialogInterface.BUTTON_POSITIVE); + + assertThat(Settings.Global.getInt(mResolver, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE)) + .isEqualTo(1); + } + + @Test + public void onClick_positiveButtonCallsBackToActivity() { + mFragment.onClick(null, DialogInterface.BUTTON_POSITIVE); + + verify(mCallbackFragment).onActivityResult(anyInt(), anyInt(), isNull()); + } +} diff --git a/tests/robotests/src/com/android/settings/wifi/WifiWakeupPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/WifiWakeupPreferenceControllerTest.java index 7bc51e9ec59..d10b0e53676 100644 --- a/tests/robotests/src/com/android/settings/wifi/WifiWakeupPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/WifiWakeupPreferenceControllerTest.java @@ -19,15 +19,20 @@ package com.android.settings.wifi; import static android.provider.Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE; import static android.provider.Settings.Global.WIFI_WAKEUP_ENABLED; import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import android.app.Fragment; import android.content.Context; +import android.location.LocationManager; import android.provider.Settings; import android.support.v14.preference.SwitchPreference; import android.support.v7.preference.Preference; import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.SettingsShadowResources; @@ -35,21 +40,34 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; @RunWith(SettingsRobolectricTestRunner.class) public class WifiWakeupPreferenceControllerTest { + private static final String NO_LOCATION_STRING = + "Unavailable because location is turned off. Turn on location."; private Context mContext; private WifiWakeupPreferenceController mController; + @Mock + DashboardFragment mFragment; + @Mock + LocationManager mLocationManager; + @Mock + SwitchPreference mPreference; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; - mController = new WifiWakeupPreferenceController(mContext); + mController = new WifiWakeupPreferenceController(mContext, mFragment); + mController.mLocationManager = mLocationManager; + mController.mPreference = mPreference; + Settings.System.putInt(mContext.getContentResolver(), WIFI_SCAN_ALWAYS_AVAILABLE, 1); + doReturn(true).when(mLocationManager).isLocationEnabled(); } @After @@ -84,39 +102,72 @@ public class WifiWakeupPreferenceControllerTest { } @Test - public void updateState_preferenceSetCheckedAndSetEnabledWhenWakeupSettingEnabled() { + public void updateState_preferenceSetCheckedWhenWakeupSettingEnabled() { final SwitchPreference preference = mock(SwitchPreference.class); Settings.System.putInt(mContext.getContentResolver(), WIFI_WAKEUP_ENABLED, 1); mController.updateState(preference); verify(preference).setChecked(true); - verify(preference).setEnabled(true); verify(preference).setSummary(R.string.wifi_wakeup_summary); } @Test - public void updateState_preferenceSetUncheckedAndSetEnabledWhenWakeupSettingDisabled() { + public void updateState_preferenceSetUncheckedWhenWakeupSettingDisabled() { final SwitchPreference preference = mock(SwitchPreference.class); Settings.System.putInt(mContext.getContentResolver(), WIFI_WAKEUP_ENABLED, 0); mController.updateState(preference); verify(preference).setChecked(false); - verify(preference).setEnabled(true); verify(preference).setSummary(R.string.wifi_wakeup_summary); } @Test - public void updateState_preferenceSetUncheckedAndSetDisabledWhenWifiScanningDisabled() { + public void updateState_preferenceSetUncheckedWhenWifiScanningDisabled() { final SwitchPreference preference = mock(SwitchPreference.class); Settings.System.putInt(mContext.getContentResolver(), WIFI_WAKEUP_ENABLED, 1); Settings.System.putInt(mContext.getContentResolver(), WIFI_SCAN_ALWAYS_AVAILABLE, 0); mController.updateState(preference); - verify(preference).setChecked(true); - verify(preference).setEnabled(false); - verify(preference).setSummary(R.string.wifi_wakeup_summary_scanning_disabled); + verify(preference).setChecked(false); + } + + @Test + public void updateState_preferenceSetUncheckedWhenWakeupSettingEnabledNoLocation() { + final SwitchPreference preference = mock(SwitchPreference.class); + Settings.System.putInt(mContext.getContentResolver(), WIFI_WAKEUP_ENABLED, 1); + doReturn(false).when(mLocationManager).isLocationEnabled(); + + mController.updateState(preference); + + verify(preference).setChecked(false); + verify(preference).setSummary(mController.getNoLocationSummary()); + } + + @Test + public void updateState_preferenceSetUncheckedWhenWakeupSettingDisabledLocationEnabled() { + final SwitchPreference preference = mock(SwitchPreference.class); + Settings.System.putInt(mContext.getContentResolver(), WIFI_WAKEUP_ENABLED, 0); + doReturn(false).when(mLocationManager).isLocationEnabled(); + + mController.updateState(preference); + + verify(preference).setChecked(false); + verify(preference).setSummary(mController.getNoLocationSummary()); + } + + @Test + public void updateState_preferenceSetUncheckedWhenWifiScanningDisabledLocationEnabled() { + final SwitchPreference preference = mock(SwitchPreference.class); + Settings.System.putInt(mContext.getContentResolver(), WIFI_WAKEUP_ENABLED, 1); + Settings.System.putInt(mContext.getContentResolver(), WIFI_SCAN_ALWAYS_AVAILABLE, 0); + doReturn(false).when(mLocationManager).isLocationEnabled(); + + mController.updateState(preference); + + verify(preference).setChecked(false); + verify(preference).setSummary(mController.getNoLocationSummary()); } }