diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 92819d86ccd..6d8e408235f 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -742,6 +742,28 @@ android:value="true" /> + + + + + + + + + + + + + + + - + android:viewportHeight="24.0" + android:viewportWidth="24.0" + android:height="24dp" + android:width="24dp" > + android:fillColor="?android:attr/colorControlActivated" + android:pathData="M 12 2 C 6.48 2 2 6.48 2 12 s 4.48 10 10 10 10 -4.48 10 -10 S 17.52 2 12 2 z m 4 11 H 8 c -.55 0 -1 -.45 -1 -1 s .45 -1 1 -1 h 8c.55 0 1 .45 1 1 s -.45 1 -1 1z" /> diff --git a/res/values/strings.xml b/res/values/strings.xml index 252a601c794..9e5db226670 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -402,11 +402,6 @@ Allow your phone to communicate with nearby Bluetooth devices - - Disable in-band ringing - - Don’t play custom phone ringtones on Bluetooth headsets - Available media devices @@ -1696,15 +1691,10 @@ Remember settings - Maximum connected Bluetooth audio devices Select maximum number of connected Bluetooth audio devices - - Disable Bluetooth AVDTP delay reports - - Disallow receiving Bluetooth AVDTP delay reports Cast @@ -2355,6 +2345,17 @@ Learn more about Private DNS features + + Setting managed by carrier + + Activate Wi\u2011Fi Calling + + Turn on Wi\u2011Fi calling + + Wi\u2011Fi calling is not supported for %1$s + + Carrier Display @@ -4922,6 +4923,8 @@ Limit background activity? If you limit background activity for an app, it may misbehave + + Since this app is not set to optimize battery, you can\u2019t restrict it.\n\nTo restrict the app, first turn on battery optimization. Screen usage since full charge @@ -7930,6 +7933,9 @@ Calls + + Allow calls + When Do Not Disturb is on, incoming calls are blocked. You can adjust settings to allow your friends, family, or other contacts to reach you. @@ -7945,6 +7951,9 @@ Messages + + Allow messages + Messages @@ -7984,9 +7993,15 @@ Reminders + + Allow reminders + Events + + Allow events + anyone @@ -7999,8 +8014,11 @@ Repeat callers + + Allow repeat callers + - From %1$s only + From %1$s From %1$s and %2$s @@ -9882,6 +9900,12 @@ + + Update Do Not Disturb + + + Hide notifications to stay focused + What\'s new and exciting? diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml index 52d8969241f..2e549046be1 100644 --- a/res/xml/development_settings.xml +++ b/res/xml/development_settings.xml @@ -254,16 +254,6 @@ android:title="@string/bluetooth_disable_absolute_volume" android:summary="@string/bluetooth_disable_absolute_volume_summary" /> - - - - + android:summary="@string/summary_placeholder" + settings:controller="com.android.settings.deviceinfo.DeviceModelPreferenceController" /> + android:summary="@string/summary_placeholder" + settings:controller= + "com.android.settings.deviceinfo.DeviceModelPreferenceController" /> + android:positiveButtonText="@string/save" + android:negativeButtonText="@android:string/cancel" /> diff --git a/res/xml/tether_prefs.xml b/res/xml/tether_prefs.xml index 2a841d71608..70a57d7c98a 100644 --- a/res/xml/tether_prefs.xml +++ b/res/xml/tether_prefs.xml @@ -17,6 +17,7 @@ + android:summary="@string/usb_tethering_subtext" + settings:keywords="@string/keywords_hotspot_tethering" /> + android:summary="@string/bluetooth_tethering_subtext" + settings:keywords="@string/keywords_hotspot_tethering" /> @@ -43,4 +44,4 @@ android:dialogTitle="@string/wifi_hotspot_ap_band_title" android:negativeButtonText="@string/cancel" android:positiveButtonText="@string/apply"/> - \ No newline at end of file + diff --git a/res/xml/zen_mode_calls_settings.xml b/res/xml/zen_mode_calls_settings.xml index dd6fcc50c0b..2da022c56a1 100644 --- a/res/xml/zen_mode_calls_settings.xml +++ b/res/xml/zen_mode_calls_settings.xml @@ -26,7 +26,7 @@ @@ -37,7 +37,7 @@ + android:title="@string/zen_mode_repeat_callers_title" /> diff --git a/res/xml/zen_mode_msg_event_reminder_settings.xml b/res/xml/zen_mode_msg_event_reminder_settings.xml index d12fc624461..8fc6b4717be 100644 --- a/res/xml/zen_mode_msg_event_reminder_settings.xml +++ b/res/xml/zen_mode_msg_event_reminder_settings.xml @@ -26,7 +26,7 @@ @@ -37,12 +37,12 @@ + android:title="@string/zen_mode_reminders_title"/> + android:title="@string/zen_mode_events_title"/> diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java index 8cb8efc27df..0be2cfd5e78 100644 --- a/src/com/android/settings/TetherSettings.java +++ b/src/com/android/settings/TetherSettings.java @@ -34,27 +34,41 @@ import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.UserManager; -import androidx.preference.SwitchPreference; +import android.provider.SearchIndexableResource; +import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; +import androidx.preference.SwitchPreference; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.datausage.DataSaverBackend; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settings.search.Indexable; import com.android.settings.wifi.tether.WifiTetherPreferenceController; import com.android.settingslib.TetherUtil; +import com.android.settingslib.search.SearchIndexable; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.concurrent.atomic.AtomicReference; /* * Displays preferences for Tethering. */ +@SearchIndexable public class TetherSettings extends RestrictedSettingsFragment implements DataSaverBackend.Listener { - private static final String USB_TETHER_SETTINGS = "usb_tether_settings"; - private static final String ENABLE_BLUETOOTH_TETHERING = "enable_bluetooth_tethering"; - private static final String DATA_SAVER_FOOTER = "disabled_on_data_saver"; + @VisibleForTesting + static final String KEY_TETHER_PREFS_SCREEN = "tether_prefs_screen"; + @VisibleForTesting + static final String KEY_WIFI_TETHER = "wifi_tether"; + @VisibleForTesting + static final String KEY_USB_TETHER_SETTINGS = "usb_tether_settings"; + @VisibleForTesting + static final String KEY_ENABLE_BLUETOOTH_TETHERING = "enable_bluetooth_tethering"; + private static final String KEY_DATA_SAVER_FOOTER = "disabled_on_data_saver"; private static final String TAG = "TetheringSettings"; @@ -110,7 +124,7 @@ public class TetherSettings extends RestrictedSettingsFragment mDataSaverBackend = new DataSaverBackend(getContext()); mDataSaverEnabled = mDataSaverBackend.isDataSaverEnabled(); - mDataSaverFooter = findPreference(DATA_SAVER_FOOTER); + mDataSaverFooter = findPreference(KEY_DATA_SAVER_FOOTER); setIfOnlyAvailableForAdmins(true); if (isUiRestricted()) { @@ -126,8 +140,8 @@ public class TetherSettings extends RestrictedSettingsFragment BluetoothProfile.PAN); } - mUsbTether = (SwitchPreference) findPreference(USB_TETHER_SETTINGS); - mBluetoothTether = (SwitchPreference) findPreference(ENABLE_BLUETOOTH_TETHERING); + mUsbTether = (SwitchPreference) findPreference(KEY_USB_TETHER_SETTINGS); + mBluetoothTether = (SwitchPreference) findPreference(KEY_ENABLE_BLUETOOTH_TETHERING); mDataSaverBackend.addListener(this); @@ -387,7 +401,6 @@ public class TetherSettings extends RestrictedSettingsFragment if (adapter.getState() == BluetoothAdapter.STATE_OFF) { mBluetoothEnableForTether = true; adapter.enable(); - mBluetoothTether.setSummary(R.string.bluetooth_turning_on); mBluetoothTether.setEnabled(false); return; } @@ -433,6 +446,42 @@ public class TetherSettings extends RestrictedSettingsFragment } }; + public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider() { + @Override + public List getXmlResourcesToIndex( + Context context, boolean enabled) { + final SearchIndexableResource sir = new SearchIndexableResource(context); + sir.xmlResId = R.xml.tether_prefs; + return Arrays.asList(sir); + } + + @Override + public List getNonIndexableKeys(Context context) { + final List keys = super.getNonIndexableKeys(context); + final ConnectivityManager cm = + context.getSystemService(ConnectivityManager.class); + + if (!TetherUtil.isTetherAvailable(context)) { + keys.add(KEY_TETHER_PREFS_SCREEN); + keys.add(KEY_WIFI_TETHER); + } + + final boolean usbAvailable = + cm.getTetherableUsbRegexs().length != 0; + if (!usbAvailable || Utils.isMonkeyRunning()) { + keys.add(KEY_USB_TETHER_SETTINGS); + } + + final boolean bluetoothAvailable = + cm.getTetherableBluetoothRegexs().length != 0; + if (!bluetoothAvailable) { + keys.add(KEY_ENABLE_BLUETOOTH_TETHERING); + } + return keys; + } + }; + private static final class OnStartTetheringCallback extends ConnectivityManager.OnStartTetheringCallback { final WeakReference mTetherSettings; diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java index a9c7713e4d8..e4c99cd87ed 100644 --- a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java +++ b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java @@ -21,6 +21,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.SharedPreferences; import android.service.settings.suggestions.Suggestion; + import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import android.util.Log; @@ -32,6 +33,8 @@ import com.android.settings.Settings.NightDisplaySuggestionActivity; import com.android.settings.display.NightDisplayPreferenceController; import com.android.settings.fingerprint.FingerprintEnrollSuggestionActivity; import com.android.settings.fingerprint.FingerprintSuggestionActivity; +import com.android.settings.notification.ZenOnboardingActivity; +import com.android.settings.notification.ZenSuggestionActivity; import com.android.settings.overlay.FeatureFactory; import com.android.settings.password.ScreenLockSuggestionActivity; import com.android.settings.support.NewDeviceIntroSuggestionActivity; @@ -88,6 +91,8 @@ public class SuggestionFeatureProviderImpl implements SuggestionFeatureProvider return NightDisplayPreferenceController.isSuggestionComplete(context); } else if (className.equals(NewDeviceIntroSuggestionActivity.class.getName())) { return NewDeviceIntroSuggestionActivity.isSuggestionComplete(context); + } else if (className.equals(ZenSuggestionActivity.class.getName())) { + return ZenOnboardingActivity.isSuggestionComplete(context); } return false; } diff --git a/src/com/android/settings/development/BluetoothDelayReportsPreferenceController.java b/src/com/android/settings/development/BluetoothDelayReportsPreferenceController.java deleted file mode 100644 index 3556424e144..00000000000 --- a/src/com/android/settings/development/BluetoothDelayReportsPreferenceController.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.development; - -import android.content.Context; -import android.os.SystemProperties; -import androidx.annotation.VisibleForTesting; -import androidx.preference.SwitchPreference; -import androidx.preference.Preference; - -import com.android.settings.core.PreferenceControllerMixin; -import com.android.settingslib.development.DeveloperOptionsPreferenceController; - -public class BluetoothDelayReportsPreferenceController extends DeveloperOptionsPreferenceController - implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin { - - private static final String BLUETOOTH_DISABLE_AVDTP_DELAY_REPORT_KEY = - "bluetooth_disable_avdtp_delay_reports"; - @VisibleForTesting - static final String BLUETOOTH_DISABLE_AVDTP_DELAY_REPORTS_PROPERTY = - "persist.bluetooth.disabledelayreports"; - - public BluetoothDelayReportsPreferenceController(Context context) { - super(context); - } - - @Override - public String getPreferenceKey() { - return BLUETOOTH_DISABLE_AVDTP_DELAY_REPORT_KEY; - } - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - final boolean isDisabled = (Boolean) newValue; - SystemProperties.set(BLUETOOTH_DISABLE_AVDTP_DELAY_REPORTS_PROPERTY, - isDisabled ? "true" : "false"); - return true; - } - - @Override - public void updateState(Preference preference) { - final boolean isDisabled = SystemProperties.getBoolean( - BLUETOOTH_DISABLE_AVDTP_DELAY_REPORTS_PROPERTY, false /* default */); - ((SwitchPreference) mPreference).setChecked(isDisabled); - } - - @Override - protected void onDeveloperOptionsSwitchDisabled() { - super.onDeveloperOptionsSwitchDisabled(); - // the default setting for this preference is the disabled state - ((SwitchPreference) mPreference).setChecked(false); - SystemProperties.set(BLUETOOTH_DISABLE_AVDTP_DELAY_REPORTS_PROPERTY, "false"); - } - -} diff --git a/src/com/android/settings/development/BluetoothInbandRingingPreferenceController.java b/src/com/android/settings/development/BluetoothInbandRingingPreferenceController.java deleted file mode 100644 index ec5124f5692..00000000000 --- a/src/com/android/settings/development/BluetoothInbandRingingPreferenceController.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.development; - -import android.bluetooth.BluetoothHeadset; -import android.content.Context; -import android.os.SystemProperties; -import androidx.annotation.VisibleForTesting; -import androidx.preference.SwitchPreference; -import androidx.preference.Preference; - -import com.android.settings.core.PreferenceControllerMixin; -import com.android.settingslib.development.DeveloperOptionsPreferenceController; - -public class BluetoothInbandRingingPreferenceController extends DeveloperOptionsPreferenceController - implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin { - - private static final String BLUETOOTH_DISABLE_INBAND_RINGING_KEY = - "bluetooth_disable_inband_ringing"; - @VisibleForTesting - static final String BLUETOOTH_DISABLE_INBAND_RINGING_PROPERTY = - "persist.bluetooth.disableinbandringing"; - - public BluetoothInbandRingingPreferenceController(Context context) { - super(context); - } - - @Override - public boolean isAvailable() { - return isInbandRingingSupported(); - } - - @Override - public String getPreferenceKey() { - return BLUETOOTH_DISABLE_INBAND_RINGING_KEY; - } - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - final boolean isChecked = (Boolean) newValue; - SystemProperties.set(BLUETOOTH_DISABLE_INBAND_RINGING_PROPERTY, - isChecked ? "true" : "false"); - return true; - } - - @Override - public void updateState(Preference preference) { - final boolean isEnabled = SystemProperties.getBoolean( - BLUETOOTH_DISABLE_INBAND_RINGING_PROPERTY, false /* default */); - ((SwitchPreference) mPreference).setChecked(isEnabled); - } - - @Override - protected void onDeveloperOptionsSwitchDisabled() { - super.onDeveloperOptionsSwitchDisabled(); - ((SwitchPreference) mPreference).setChecked(false); - SystemProperties.set(BLUETOOTH_DISABLE_INBAND_RINGING_PROPERTY, "false"); - } - - @VisibleForTesting - boolean isInbandRingingSupported() { - return BluetoothHeadset.isInbandRingingSupported(mContext); - } -} diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index c3b62e244f8..ba3653a9328 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -406,8 +406,6 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra controllers.add(new TetheringHardwareAccelPreferenceController(context)); controllers.add(new BluetoothDeviceNoNamePreferenceController(context)); controllers.add(new BluetoothAbsoluteVolumePreferenceController(context)); - controllers.add(new BluetoothInbandRingingPreferenceController(context)); - controllers.add(new BluetoothDelayReportsPreferenceController(context)); controllers.add(new BluetoothAvrcpVersionPreferenceController(context)); controllers.add(new BluetoothAudioCodecPreferenceController(context, lifecycle, bluetoothA2dpConfigStore)); diff --git a/src/com/android/settings/deviceinfo/DeviceInfoSettings.java b/src/com/android/settings/deviceinfo/DeviceInfoSettings.java index 44655f0d08a..e2df2c1f92c 100644 --- a/src/com/android/settings/deviceinfo/DeviceInfoSettings.java +++ b/src/com/android/settings/deviceinfo/DeviceInfoSettings.java @@ -21,7 +21,6 @@ import android.app.Fragment; import android.content.Context; import android.content.Intent; import android.provider.SearchIndexableResource; -import androidx.annotation.VisibleForTesting; import android.telephony.TelephonyManager; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -41,6 +40,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import androidx.annotation.VisibleForTesting; + @SearchIndexable public class DeviceInfoSettings extends DashboardFragment implements Indexable { @@ -57,6 +58,7 @@ public class DeviceInfoSettings extends DashboardFragment implements Indexable { public void onAttach(Context context) { super.onAttach(context); use(FirmwareVersionPreferenceController.class).setHost(this /*parent*/); + use(DeviceModelPreferenceController.class).setHost(this /* parent */); } @Override @@ -134,7 +136,6 @@ public class DeviceInfoSettings extends DashboardFragment implements Indexable { final List controllers = new ArrayList<>(); controllers.add(new PhoneNumberPreferenceController(context)); controllers.add(new SimStatusPreferenceController(context, fragment)); - controllers.add(new DeviceModelPreferenceController(context, fragment)); controllers.add(new ImeiInfoPreferenceController(context, fragment)); controllers.add(new IpAddressPreferenceController(context, lifecycle)); controllers.add(new WifiMacAddressPreferenceController(context, lifecycle)); diff --git a/src/com/android/settings/deviceinfo/DeviceModelPreferenceController.java b/src/com/android/settings/deviceinfo/DeviceModelPreferenceController.java index 1fc54cd6755..4fa7afc0a15 100644 --- a/src/com/android/settings/deviceinfo/DeviceModelPreferenceController.java +++ b/src/com/android/settings/deviceinfo/DeviceModelPreferenceController.java @@ -18,50 +18,54 @@ package com.android.settings.deviceinfo; import android.app.Fragment; import android.content.Context; import android.os.Build; -import androidx.preference.Preference; -import androidx.preference.PreferenceScreen; import android.text.TextUtils; +import android.util.Log; import com.android.settings.R; -import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.BasePreferenceController; import com.android.settingslib.DeviceInfoUtils; -import com.android.settingslib.core.AbstractPreferenceController; -public class DeviceModelPreferenceController extends AbstractPreferenceController implements - PreferenceControllerMixin { +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; - private static final String KEY_DEVICE_MODEL = "device_model"; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; - private final Fragment mHost; - public DeviceModelPreferenceController(Context context, Fragment host) { - super(context); - mHost = host; +public class DeviceModelPreferenceController extends BasePreferenceController { + + private static final String TAG = "DeviceModelPrefCtrl"; + + private Fragment mHost; + + public DeviceModelPreferenceController(Context context, String key) { + super(context, key); } - @Override - public boolean isAvailable() { - return mContext.getResources().getBoolean(R.bool.config_show_device_model); + public void setHost(Fragment fragment) { + mHost = fragment; } @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); - final Preference pref = screen.findPreference(KEY_DEVICE_MODEL); - if (pref != null) { - pref.setSummary(mContext.getResources().getString(R.string.model_summary, - getDeviceModel())); - } } @Override - public String getPreferenceKey() { - return KEY_DEVICE_MODEL; + public int getAvailabilityStatus() { + return mContext.getResources().getBoolean(R.bool.config_show_device_model) + ? AVAILABLE : UNSUPPORTED_ON_DEVICE; + } + + @Override + public CharSequence getSummary() { + return mContext.getResources().getString(R.string.model_summary, getDeviceModel()); } @Override public boolean handlePreferenceTreeClick(Preference preference) { - if (!TextUtils.equals(preference.getKey(), KEY_DEVICE_MODEL)) { + if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) { return false; } final HardwareInfoDialogFragment fragment = HardwareInfoDialogFragment.newInstance(); @@ -70,6 +74,25 @@ public class DeviceModelPreferenceController extends AbstractPreferenceControlle } public static String getDeviceModel() { - return Build.MODEL + DeviceInfoUtils.getMsvSuffix(); + FutureTask msvSuffixTask = new FutureTask(new Callable() { + @Override + public String call() { + return DeviceInfoUtils.getMsvSuffix(); + } + }); + + msvSuffixTask.run(); + try { + // Wait for msv suffix value. + final String msvSuffix = msvSuffixTask.get(); + return Build.MODEL + msvSuffix; + } catch (ExecutionException e) { + Log.e(TAG, "Execution error, so we only show model name"); + } catch (InterruptedException e) { + Log.e(TAG, "Interruption error, so we only show model name"); + } + // If we can't get an msv suffix value successfully, + // it's better to return model name. + return Build.MODEL; } } diff --git a/src/com/android/settings/deviceinfo/aboutphone/MyDeviceInfoFragment.java b/src/com/android/settings/deviceinfo/aboutphone/MyDeviceInfoFragment.java index bdafa82c95d..00be8be0a01 100644 --- a/src/com/android/settings/deviceinfo/aboutphone/MyDeviceInfoFragment.java +++ b/src/com/android/settings/deviceinfo/aboutphone/MyDeviceInfoFragment.java @@ -82,6 +82,7 @@ public class MyDeviceInfoFragment extends DashboardFragment public void onAttach(Context context) { super.onAttach(context); use(FirmwareVersionPreferenceController.class).setHost(this /*parent*/); + use(DeviceModelPreferenceController.class).setHost(this /* parent */); } @Override @@ -122,7 +123,6 @@ public class MyDeviceInfoFragment extends DashboardFragment } controllers.add(deviceNamePreferenceController); controllers.add(new SimStatusPreferenceController(context, fragment)); - controllers.add(new DeviceModelPreferenceController(context, fragment)); controllers.add(new ImeiInfoPreferenceController(context, fragment)); controllers.add(new IpAddressPreferenceController(context, lifecycle)); controllers.add(new WifiMacAddressPreferenceController(context, lifecycle)); diff --git a/src/com/android/settings/network/PrivateDnsModeDialogPreference.java b/src/com/android/settings/network/PrivateDnsModeDialogPreference.java index 598d5a820b6..8c41f192ccf 100644 --- a/src/com/android/settings/network/PrivateDnsModeDialogPreference.java +++ b/src/com/android/settings/network/PrivateDnsModeDialogPreference.java @@ -156,16 +156,18 @@ public class PrivateDnsModeDialogPreference extends CustomDialogPreference imple @Override public void onClick(DialogInterface dialog, int which) { - final Context context = getContext(); - if (mMode.equals(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) { - // Only clickable if hostname is valid, so we could save it safely - Settings.Global.putString(context.getContentResolver(), HOSTNAME_KEY, - mEditText.getText().toString()); - } + if (which == DialogInterface.BUTTON_POSITIVE) { + final Context context = getContext(); + if (mMode.equals(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) { + // Only clickable if hostname is valid, so we could save it safely + Settings.Global.putString(context.getContentResolver(), HOSTNAME_KEY, + mEditText.getText().toString()); + } - FeatureFactory.getFactory(context).getMetricsFeatureProvider().action(context, - MetricsProto.MetricsEvent.ACTION_PRIVATE_DNS_MODE, mMode); - Settings.Global.putString(context.getContentResolver(), MODE_KEY, mMode); + FeatureFactory.getFactory(context).getMetricsFeatureProvider().action(context, + MetricsProto.MetricsEvent.ACTION_PRIVATE_DNS_MODE, mMode); + Settings.Global.putString(context.getContentResolver(), MODE_KEY, mMode); + } } @Override diff --git a/src/com/android/settings/network/TetherPreferenceController.java b/src/com/android/settings/network/TetherPreferenceController.java index 636fe0cee07..3ab83cda0b5 100644 --- a/src/com/android/settings/network/TetherPreferenceController.java +++ b/src/com/android/settings/network/TetherPreferenceController.java @@ -17,7 +17,6 @@ package com.android.settings.network; import static android.os.UserManager.DISALLOW_CONFIG_TETHERING; import static com.android.settingslib.RestrictedLockUtils.checkIfRestrictionEnforced; -import static com.android.settingslib.RestrictedLockUtils.hasBaseUserRestriction; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothPan; @@ -40,6 +39,7 @@ import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.TetherSettings; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.TetherUtil; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; @@ -112,11 +112,7 @@ public class TetherPreferenceController extends AbstractPreferenceController imp @Override public boolean isAvailable() { - final boolean isBlocked = - (!mConnectivityManager.isTetheringSupported() && !mAdminDisallowedTetherConfig) - || hasBaseUserRestriction(mContext, DISALLOW_CONFIG_TETHERING, - UserHandle.myUserId()); - return !isBlocked; + return TetherUtil.isTetherAvailable(mContext); } @Override diff --git a/src/com/android/settings/notification/ZenModeBackend.java b/src/com/android/settings/notification/ZenModeBackend.java index cb8a048c90c..8c8f14df671 100644 --- a/src/com/android/settings/notification/ZenModeBackend.java +++ b/src/com/android/settings/notification/ZenModeBackend.java @@ -135,6 +135,9 @@ public class ZenModeBackend { } protected void saveVisualEffectsPolicy(int category, boolean suppress) { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.ZEN_SETTINGS_UPDATED, 1); + int suppressedEffects = getNewSuppressedEffects(suppress, category); savePolicy(mPolicy.priorityCategories, mPolicy.priorityCallSenders, mPolicy.priorityMessageSenders, suppressedEffects); diff --git a/src/com/android/settings/notification/ZenModeRestrictNotificationsSettings.java b/src/com/android/settings/notification/ZenModeRestrictNotificationsSettings.java index a2a0ba7f008..737c646f929 100644 --- a/src/com/android/settings/notification/ZenModeRestrictNotificationsSettings.java +++ b/src/com/android/settings/notification/ZenModeRestrictNotificationsSettings.java @@ -16,22 +16,15 @@ package com.android.settings.notification; -import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_ZEN_SHOW_CUSTOM; - import android.content.Context; import android.os.Bundle; import android.provider.SearchIndexableResource; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; -import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.Indexable; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.widget.FooterPreference; @@ -42,67 +35,21 @@ import java.util.List; @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) public class ZenModeRestrictNotificationsSettings extends ZenModeSettingsBase implements Indexable { - protected static final int APP_MENU_SHOW_CUSTOM = 1; - protected boolean mShowMenuSelected; - @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); } - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - menu.add(0, APP_MENU_SHOW_CUSTOM, 0, R.string.zen_mode_restrict_notifications_enable_custom) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); - } - - @Override - public boolean onOptionsItemSelected(MenuItem menuItem) { - if (menuItem.getItemId() == APP_MENU_SHOW_CUSTOM) { - final FeatureFactory featureFactory = FeatureFactory.getFactory(mContext); - MetricsFeatureProvider metrics = featureFactory.getMetricsFeatureProvider(); - - mShowMenuSelected = !mShowMenuSelected; - - ZenModeVisEffectsCustomPreferenceController custom = - use(ZenModeVisEffectsCustomPreferenceController.class); - custom.setShownByMenu(mShowMenuSelected); - custom.displayPreference(getPreferenceScreen()); - - if (mShowMenuSelected) { - metrics.action(mContext, ACTION_ZEN_SHOW_CUSTOM, true); - } else { - metrics.action(mContext, ACTION_ZEN_SHOW_CUSTOM, false); - } - - return true; - } - return false; - } - - @Override - public void onPrepareOptionsMenu(Menu menu) { - if (mShowMenuSelected) { - menu.findItem(APP_MENU_SHOW_CUSTOM) - .setTitle(R.string.zen_mode_restrict_notifications_disable_custom); - } else { - menu.findItem(APP_MENU_SHOW_CUSTOM) - .setTitle(R.string.zen_mode_restrict_notifications_enable_custom); - } - - if (mShowMenuSelected && use(ZenModeVisEffectsCustomPreferenceController.class) - .areCustomOptionsSelected()) { - menu.findItem(APP_MENU_SHOW_CUSTOM).setEnabled(false); - } else { - menu.findItem(APP_MENU_SHOW_CUSTOM).setEnabled(true); - } - } - @Override protected List createPreferenceControllers(Context context) { return buildPreferenceControllers(context, getLifecycle()); } + @Override + public int getHelpResource() { + return R.string.help_uri_interruptions; + } + private static List buildPreferenceControllers(Context context, Lifecycle lifecycle) { List controllers = new ArrayList<>(); diff --git a/src/com/android/settings/notification/ZenModeSettings.java b/src/com/android/settings/notification/ZenModeSettings.java index 3fb5a4b9be6..240ab68c93a 100644 --- a/src/com/android/settings/notification/ZenModeSettings.java +++ b/src/com/android/settings/notification/ZenModeSettings.java @@ -170,7 +170,7 @@ public class ZenModeSettings extends ZenModeSettingsBase { } else if (numCategories == 2) { return mContext.getString(R.string.join_two_items, enabledCategories.get(0), enabledCategories.get(1).toLowerCase()); - } else if (numCategories == 3) { + } else if (numCategories == 3){ final List summaries = new ArrayList<>(); summaries.add(enabledCategories.get(0)); summaries.add(enabledCategories.get(1).toLowerCase()); diff --git a/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceController.java b/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceController.java index 5baf0fac11d..5dd99f61568 100644 --- a/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceController.java +++ b/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceController.java @@ -30,7 +30,6 @@ import com.android.settingslib.core.lifecycle.Lifecycle; public class ZenModeVisEffectsCustomPreferenceController extends AbstractZenModePreferenceController { - protected boolean mShowMenuSelected; protected static final int INTERRUPTIVE_EFFECTS = Policy.SUPPRESSED_EFFECT_AMBIENT | Policy.SUPPRESSED_EFFECT_PEEK | Policy.SUPPRESSED_EFFECT_LIGHTS @@ -43,11 +42,7 @@ public class ZenModeVisEffectsCustomPreferenceController @Override public boolean isAvailable() { - if (mShowMenuSelected) { - return true; - } - - return areCustomOptionsSelected(); + return true; } @Override @@ -67,10 +62,6 @@ public class ZenModeVisEffectsCustomPreferenceController }); } - protected void setShownByMenu(boolean shown) { - mShowMenuSelected = shown; - } - protected boolean areCustomOptionsSelected() { boolean allEffectsSuppressed = Policy.areAllVisualEffectsSuppressed(mBackend.mPolicy.suppressedVisualEffects); diff --git a/src/com/android/settings/notification/ZenOnboardingActivity.java b/src/com/android/settings/notification/ZenOnboardingActivity.java index 2215a1d9d7c..c4cc355c95a 100644 --- a/src/com/android/settings/notification/ZenOnboardingActivity.java +++ b/src/com/android/settings/notification/ZenOnboardingActivity.java @@ -19,17 +19,34 @@ package com.android.settings.notification; import android.app.Activity; import android.app.NotificationManager; import android.app.NotificationManager.Policy; +import android.content.Context; +import android.content.SharedPreferences; import android.os.Bundle; import android.provider.Settings; -import androidx.annotation.VisibleForTesting; +import android.text.format.DateUtils; +import android.util.Log; import android.view.View; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; +import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; +import com.android.settings.overlay.FeatureFactory; + +import androidx.annotation.VisibleForTesting; public class ZenOnboardingActivity extends Activity { + private static final String TAG = "ZenOnboardingActivity"; + + @VisibleForTesting + static final String PREF_KEY_SUGGESTION_FIRST_DISPLAY_TIME = + "pref_zen_suggestion_first_display_time_ms"; + @VisibleForTesting + static final String PREF_KEY_SUGGESTION_VIEWED = "pref_zen_suggestion_viewed"; + @VisibleForTesting + static final long ALWAYS_SHOW_THRESHOLD = DateUtils.DAY_IN_MILLIS * 14; + private NotificationManager mNm; private MetricsLogger mMetrics; @@ -39,6 +56,10 @@ public class ZenOnboardingActivity extends Activity { setNotificationManager(getSystemService(NotificationManager.class)); setMetricsLogger(new MetricsLogger()); + Context context = getApplicationContext(); + Settings.Global.putInt(context.getContentResolver(), + Settings.Global.ZEN_SETTINGS_SUGGESTION_VIEWED, 1); + setupUI(); } @@ -61,20 +82,80 @@ public class ZenOnboardingActivity extends Activity { public void close(View button) { mMetrics.action(MetricsEvent.ACTION_ZEN_ONBOARDING_KEEP_CURRENT_SETTINGS); + + Settings.Global.putInt(getApplicationContext().getContentResolver(), + Settings.Global.ZEN_SETTINGS_UPDATED, 1); + finishAndRemoveTask(); } public void save(View button) { mMetrics.action(MetricsEvent.ACTION_ZEN_ONBOARDING_OK); - Policy policy = mNm.getNotificationPolicy(); + NotificationManager.Policy policy = mNm.getNotificationPolicy(); - Policy newPolicy = new NotificationManager.Policy( + NotificationManager.Policy newPolicy = new NotificationManager.Policy( Policy.PRIORITY_CATEGORY_REPEAT_CALLERS | policy.priorityCategories, Policy.PRIORITY_SENDERS_STARRED, policy.priorityMessageSenders, - Policy.getAllSuppressedVisualEffects()); + NotificationManager.Policy.getAllSuppressedVisualEffects()); mNm.setNotificationPolicy(newPolicy); + Settings.Global.putInt(getApplicationContext().getContentResolver(), + Settings.Global.ZEN_SETTINGS_UPDATED, 1); + finishAndRemoveTask(); } + + public static boolean isSuggestionComplete(Context context) { + if (wasZenUpdated(context)) { + return true; + } + + if (showSuggestion(context) || withinShowTimeThreshold(context)) { + return false; + } + + return true; + } + + private static boolean wasZenUpdated(Context context) { + // ZEN_SETTINGS_UPDATED is true for: + // - fresh P+ device + // - if zen visual effects values were changed by the user in Settings + return Settings.Global.getInt(context.getContentResolver(), + Settings.Global.ZEN_SETTINGS_UPDATED, 0) != 0; + } + + private static boolean showSuggestion(Context context) { + // SHOW_ZEN_SETTINGS_SUGGESTION is by default true, but false when: + // - user manually turns on dnd + + // SHOW_ZEN_SETTINGS_SUGGESTION is also true when: + // - automatic rule has started DND and user has not seen the first use dialog + return Settings.Global.getInt(context.getContentResolver(), + Settings.Global.SHOW_ZEN_SETTINGS_SUGGESTION, 0) != 0; + + } + + private static boolean withinShowTimeThreshold(Context context) { + final SuggestionFeatureProvider featureProvider = FeatureFactory.getFactory(context) + .getSuggestionFeatureProvider(context); + final SharedPreferences prefs = featureProvider.getSharedPrefs(context); + final long currentTimeMs = System.currentTimeMillis(); + final long firstDisplayTimeMs; + + if (!prefs.contains(PREF_KEY_SUGGESTION_FIRST_DISPLAY_TIME)) { + firstDisplayTimeMs = currentTimeMs; + prefs.edit().putLong(PREF_KEY_SUGGESTION_FIRST_DISPLAY_TIME, currentTimeMs).commit(); + } else { + firstDisplayTimeMs = prefs.getLong(PREF_KEY_SUGGESTION_FIRST_DISPLAY_TIME, -1); + } + + final long showTimeMs = firstDisplayTimeMs + ALWAYS_SHOW_THRESHOLD; + final boolean stillShow = currentTimeMs < showTimeMs; + + Log.d(TAG, "still show zen suggestion based on time: " + stillShow + " showTimeMs=" + + showTimeMs); + return stillShow; + } } diff --git a/src/com/android/settings/notification/ZenSuggestionActivity.java b/src/com/android/settings/notification/ZenSuggestionActivity.java new file mode 100644 index 00000000000..9d2148c7e50 --- /dev/null +++ b/src/com/android/settings/notification/ZenSuggestionActivity.java @@ -0,0 +1,24 @@ +package com.android.settings.notification; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.provider.Settings; + +public class ZenSuggestionActivity extends Activity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // start up zen settings activity + Intent settingsIntent = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS); + startActivity(settingsIntent); + + // start up onboarding activity + Intent onboardingActivity = new Intent(Settings.ZEN_MODE_ONBOARDING); + startActivity(onboardingActivity); + + finish(); + } +} diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java index add05e4e4a5..4d0165e1524 100644 --- a/src/com/android/settings/password/ChooseLockGeneric.java +++ b/src/com/android/settings/password/ChooseLockGeneric.java @@ -38,7 +38,6 @@ import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; -import android.security.KeyStore; import androidx.annotation.StringRes; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; @@ -97,7 +96,6 @@ public class ChooseLockGeneric extends SettingsActivity { public static class ChooseLockGenericFragment extends SettingsPreferenceFragment { private static final String TAG = "ChooseLockGenericFragment"; - private static final int MIN_PASSWORD_LENGTH = 4; private static final String KEY_SKIP_FINGERPRINT = "unlock_skip_fingerprint"; private static final String PASSWORD_CONFIRMED = "password_confirmed"; private static final String WAITING_FOR_CONFIRMATION = "waiting_for_confirmation"; @@ -136,7 +134,6 @@ public class ChooseLockGeneric extends SettingsActivity { private ChooseLockSettingsHelper mChooseLockSettingsHelper; private DevicePolicyManager mDPM; - private KeyStore mKeyStore; private boolean mHasChallenge = false; private long mChallenge; private boolean mPasswordConfirmed = false; @@ -168,7 +165,6 @@ public class ChooseLockGeneric extends SettingsActivity { String chooseLockAction = getActivity().getIntent().getAction(); mFingerprintManager = Utils.getFingerprintManagerOrNull(getActivity()); mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); - mKeyStore = KeyStore.getInstance(); mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this.getActivity()); mLockPatternUtils = new LockPatternUtils(getActivity()); mIsSetNewPassword = ACTION_SET_NEW_PARENT_PROFILE_PASSWORD.equals(chooseLockAction) @@ -585,11 +581,10 @@ public class ChooseLockGeneric extends SettingsActivity { return mManagedPasswordProvider.createIntent(false, password); } - protected Intent getLockPasswordIntent(int quality, int minLength, int maxLength) { + protected Intent getLockPasswordIntent(int quality) { ChooseLockPassword.IntentBuilder builder = new ChooseLockPassword.IntentBuilder(getContext()) .setPasswordQuality(quality) - .setPasswordLengthRange(minLength, maxLength) .setForFingerprint(mForFingerprint) .setUserId(mUserId); if (mHasChallenge) { @@ -668,12 +663,7 @@ public class ChooseLockGeneric extends SettingsActivity { if (quality >= DevicePolicyManager.PASSWORD_QUALITY_MANAGED) { intent = getLockManagedPasswordIntent(mUserPassword); } else if (quality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) { - int minLength = mDPM.getPasswordMinimumLength(null, mUserId); - if (minLength < MIN_PASSWORD_LENGTH) { - minLength = MIN_PASSWORD_LENGTH; - } - final int maxLength = mDPM.getPasswordMaximumLength(quality); - intent = getLockPasswordIntent(quality, minLength, maxLength); + intent = getLockPasswordIntent(quality); } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) { intent = getLockPatternIntent(); } diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java index 23c50226be9..1013c44700e 100644 --- a/src/com/android/settings/password/ChooseLockPassword.java +++ b/src/com/android/settings/password/ChooseLockPassword.java @@ -73,15 +73,6 @@ import java.util.ArrayList; import java.util.List; public class ChooseLockPassword extends SettingsActivity { - public static final String PASSWORD_MIN_KEY = "lockscreen.password_min"; - public static final String PASSWORD_MAX_KEY = "lockscreen.password_max"; - public static final String PASSWORD_MIN_LETTERS_KEY = "lockscreen.password_min_letters"; - public static final String PASSWORD_MIN_LOWERCASE_KEY = "lockscreen.password_min_lowercase"; - public static final String PASSWORD_MIN_UPPERCASE_KEY = "lockscreen.password_min_uppercase"; - public static final String PASSWORD_MIN_NUMERIC_KEY = "lockscreen.password_min_numeric"; - public static final String PASSWORD_MIN_SYMBOLS_KEY = "lockscreen.password_min_symbols"; - public static final String PASSWORD_MIN_NONLETTER_KEY = "lockscreen.password_min_nonletter"; - private static final String TAG = "ChooseLockPassword"; @Override @@ -113,12 +104,6 @@ public class ChooseLockPassword extends SettingsActivity { return this; } - public IntentBuilder setPasswordLengthRange(int min, int max) { - mIntent.putExtra(PASSWORD_MIN_KEY, min); - mIntent.putExtra(PASSWORD_MAX_KEY, max); - return this; - } - public IntentBuilder setUserId(int userId) { mIntent.putExtra(Intent.EXTRA_USER_ID, userId); return this; @@ -454,54 +439,32 @@ public class ChooseLockPassword extends SettingsActivity { } private void setupPasswordRequirementsView(View view) { - // Construct passwordRequirements and requirementDescriptions. - List passwordRequirements = new ArrayList<>(); - List requirementDescriptions = new ArrayList<>(); + final List passwordRequirements = new ArrayList<>(); if (mPasswordMinUpperCase > 0) { passwordRequirements.add(MIN_UPPER_LETTERS_IN_PASSWORD); - requirementDescriptions.add(getResources().getQuantityString( - R.plurals.lockpassword_password_requires_uppercase, mPasswordMinUpperCase, - mPasswordMinUpperCase)); } if (mPasswordMinLowerCase > 0) { passwordRequirements.add(MIN_LOWER_LETTERS_IN_PASSWORD); - requirementDescriptions.add(getResources().getQuantityString( - R.plurals.lockpassword_password_requires_lowercase, mPasswordMinLowerCase, - mPasswordMinLowerCase)); } if (mPasswordMinLetters > 0) { if (mPasswordMinLetters > mPasswordMinUpperCase + mPasswordMinLowerCase) { passwordRequirements.add(MIN_LETTER_IN_PASSWORD); - requirementDescriptions.add(getResources().getQuantityString( - R.plurals.lockpassword_password_requires_letters, mPasswordMinLetters, - mPasswordMinLetters)); } } if (mPasswordMinNumeric > 0) { passwordRequirements.add(MIN_NUMBER_IN_PASSWORD); - requirementDescriptions.add(getResources().getQuantityString( - R.plurals.lockpassword_password_requires_numeric, mPasswordMinNumeric, - mPasswordMinNumeric)); } if (mPasswordMinSymbols > 0) { passwordRequirements.add(MIN_SYMBOLS_IN_PASSWORD); - requirementDescriptions.add(getResources().getQuantityString( - R.plurals.lockpassword_password_requires_symbols, mPasswordMinSymbols, - mPasswordMinSymbols)); } if (mPasswordMinNonLetter > 0) { if (mPasswordMinNonLetter > mPasswordMinNumeric + mPasswordMinSymbols) { passwordRequirements.add(MIN_NON_LETTER_IN_PASSWORD); - requirementDescriptions.add(getResources().getQuantityString( - R.plurals.lockpassword_password_requires_nonletter, mPasswordMinNonLetter, - - mPasswordMinNonLetter)); } } // Convert list to array. mPasswordRequirements = passwordRequirements.stream().mapToInt(i -> i).toArray(); - mPasswordRestrictionView = - (RecyclerView) view.findViewById(R.id.password_requirements_view); + mPasswordRestrictionView = view.findViewById(R.id.password_requirements_view); mPasswordRestrictionView.setLayoutManager(new LinearLayoutManager(getActivity())); mPasswordRequirementAdapter = new PasswordRequirementAdapter(); mPasswordRestrictionView.setAdapter(mPasswordRequirementAdapter); @@ -582,29 +545,15 @@ public class ChooseLockPassword extends SettingsActivity { final int dpmPasswordQuality = mLockPatternUtils.getRequestedPasswordQuality(mUserId); mRequestedQuality = Math.max(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, mRequestedQuality), dpmPasswordQuality); - mPasswordMinLength = Math.max(Math.max( - LockPatternUtils.MIN_LOCK_PASSWORD_SIZE, - intent.getIntExtra(PASSWORD_MIN_KEY, mPasswordMinLength)), + mPasswordMinLength = Math.max(LockPatternUtils.MIN_LOCK_PASSWORD_SIZE, mLockPatternUtils.getRequestedMinimumPasswordLength(mUserId)); - mPasswordMaxLength = intent.getIntExtra(PASSWORD_MAX_KEY, mPasswordMaxLength); - mPasswordMinLetters = Math.max(intent.getIntExtra(PASSWORD_MIN_LETTERS_KEY, - mPasswordMinLetters), mLockPatternUtils.getRequestedPasswordMinimumLetters( - mUserId)); - mPasswordMinUpperCase = Math.max(intent.getIntExtra(PASSWORD_MIN_UPPERCASE_KEY, - mPasswordMinUpperCase), mLockPatternUtils.getRequestedPasswordMinimumUpperCase( - mUserId)); - mPasswordMinLowerCase = Math.max(intent.getIntExtra(PASSWORD_MIN_LOWERCASE_KEY, - mPasswordMinLowerCase), mLockPatternUtils.getRequestedPasswordMinimumLowerCase( - mUserId)); - mPasswordMinNumeric = Math.max(intent.getIntExtra(PASSWORD_MIN_NUMERIC_KEY, - mPasswordMinNumeric), mLockPatternUtils.getRequestedPasswordMinimumNumeric( - mUserId)); - mPasswordMinSymbols = Math.max(intent.getIntExtra(PASSWORD_MIN_SYMBOLS_KEY, - mPasswordMinSymbols), mLockPatternUtils.getRequestedPasswordMinimumSymbols( - mUserId)); - mPasswordMinNonLetter = Math.max(intent.getIntExtra(PASSWORD_MIN_NONLETTER_KEY, - mPasswordMinNonLetter), mLockPatternUtils.getRequestedPasswordMinimumNonLetter( - mUserId)); + mPasswordMaxLength = mLockPatternUtils.getMaximumPasswordLength(mRequestedQuality); + mPasswordMinLetters = mLockPatternUtils.getRequestedPasswordMinimumLetters(mUserId); + mPasswordMinUpperCase = mLockPatternUtils.getRequestedPasswordMinimumUpperCase(mUserId); + mPasswordMinLowerCase = mLockPatternUtils.getRequestedPasswordMinimumLowerCase(mUserId); + mPasswordMinNumeric = mLockPatternUtils.getRequestedPasswordMinimumNumeric(mUserId); + mPasswordMinSymbols = mLockPatternUtils.getRequestedPasswordMinimumSymbols(mUserId); + mPasswordMinNonLetter = mLockPatternUtils.getRequestedPasswordMinimumNonLetter(mUserId); // Modify the value based on dpm policy. switch (dpmPasswordQuality) { diff --git a/src/com/android/settings/password/SetupChooseLockGeneric.java b/src/com/android/settings/password/SetupChooseLockGeneric.java index a72047308c2..3edb08301b3 100644 --- a/src/com/android/settings/password/SetupChooseLockGeneric.java +++ b/src/com/android/settings/password/SetupChooseLockGeneric.java @@ -170,9 +170,9 @@ public class SetupChooseLockGeneric extends ChooseLockGeneric { } @Override - protected Intent getLockPasswordIntent(int quality, int minLength, int maxLength) { + protected Intent getLockPasswordIntent(int quality) { final Intent intent = SetupChooseLockPassword.modifyIntentForSetup( - getContext(), super.getLockPasswordIntent(quality, minLength, maxLength)); + getContext(), super.getLockPasswordIntent(quality)); SetupWizardUtils.copySetupExtras(getActivity().getIntent(), intent); return intent; } diff --git a/src/com/android/settings/search/DeviceIndexUpdateJobService.java b/src/com/android/settings/search/DeviceIndexUpdateJobService.java index 12a9cf00fcb..19b7d5e60b6 100644 --- a/src/com/android/settings/search/DeviceIndexUpdateJobService.java +++ b/src/com/android/settings/search/DeviceIndexUpdateJobService.java @@ -53,10 +53,12 @@ public class DeviceIndexUpdateJobService extends JobService { @Override public boolean onStartJob(JobParameters params) { if (DEBUG) Log.d(TAG, "onStartJob"); - mRunningJob = true; - Thread thread = new Thread(() -> updateIndex(params)); - thread.setPriority(Thread.MIN_PRIORITY); - thread.start(); + if (!mRunningJob) { + mRunningJob = true; + Thread thread = new Thread(() -> updateIndex(params)); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); + } return true; } @@ -122,7 +124,7 @@ public class DeviceIndexUpdateJobService extends JobService { } protected CharSequence findTitle(Slice loadedSlice, SliceMetadata metaData) { - ListContent content = new ListContent(this, loadedSlice); + ListContent content = new ListContent(null, loadedSlice); SliceItem headerItem = content.getHeaderItem(); if (headerItem == null) { if (content.getRowItems().size() != 0) { diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java index edeb2b5f28c..7eecee92cb7 100644 --- a/src/com/android/settings/slices/SettingsSliceProvider.java +++ b/src/com/android/settings/slices/SettingsSliceProvider.java @@ -18,6 +18,8 @@ package com.android.settings.slices; import static android.Manifest.permission.READ_SEARCH_INDEXABLES; +import static com.android.settings.wifi.calling.WifiCallingSliceHelper.PATH_WIFI_CALLING; + import android.app.PendingIntent; import android.app.slice.SliceManager; import android.content.ContentResolver; @@ -32,7 +34,13 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; +import androidx.slice.Slice; +import androidx.slice.SliceProvider; +import androidx.slice.builders.ListBuilder; +import androidx.slice.builders.SliceAction; + import com.android.settings.R; +import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.utils.ThreadUtils; import java.net.URISyntaxException; @@ -160,6 +168,11 @@ public class SettingsSliceProvider extends SliceProvider { switch (path) { case "/" + PATH_WIFI: return createWifiSlice(sliceUri); + case "/" + PATH_WIFI_CALLING: + return FeatureFactory.getFactory(getContext()) + .getSlicesFeatureProvider() + .getNewWifiCallingSliceHelper(getContext()) + .createWifiCallingSlice(sliceUri); } SliceData cachedSliceData = mSliceWeakDataCache.get(sliceUri); diff --git a/src/com/android/settings/slices/SliceBroadcastReceiver.java b/src/com/android/settings/slices/SliceBroadcastReceiver.java index 47a7f5a45bd..04097340ed3 100644 --- a/src/com/android/settings/slices/SliceBroadcastReceiver.java +++ b/src/com/android/settings/slices/SliceBroadcastReceiver.java @@ -21,6 +21,7 @@ import static com.android.settings.slices.SettingsSliceProvider.ACTION_TOGGLE_CH import static com.android.settings.slices.SettingsSliceProvider.ACTION_WIFI_CHANGED; import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_KEY; import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_PLATFORM_DEFINED; +import static com.android.settings.wifi.calling.WifiCallingSliceHelper.ACTION_WIFI_CALLING_CHANGED; import android.app.slice.Slice; import android.content.BroadcastReceiver; @@ -79,6 +80,12 @@ public class SliceBroadcastReceiver extends BroadcastReceiver { context.getContentResolver().notifyChange(uri, null); }, 1000); break; + case ACTION_WIFI_CALLING_CHANGED: + FeatureFactory.getFactory(context) + .getSlicesFeatureProvider() + .getNewWifiCallingSliceHelper(context) + .handleWifiCallingChanged(intent); + break; } } diff --git a/src/com/android/settings/slices/SlicesFeatureProvider.java b/src/com/android/settings/slices/SlicesFeatureProvider.java index e5bba617e48..8dd6547b398 100644 --- a/src/com/android/settings/slices/SlicesFeatureProvider.java +++ b/src/com/android/settings/slices/SlicesFeatureProvider.java @@ -2,6 +2,8 @@ package com.android.settings.slices; import android.content.Context; +import com.android.settings.wifi.calling.WifiCallingSliceHelper; + /** * Manages Slices in Settings. */ @@ -24,4 +26,9 @@ public interface SlicesFeatureProvider { * If the data is already indexed, the data will not change. */ void indexSliceData(Context context); -} \ No newline at end of file + + /** + * Gets new WifiCallingSliceHelper object + */ + WifiCallingSliceHelper getNewWifiCallingSliceHelper(Context context); +} diff --git a/src/com/android/settings/slices/SlicesFeatureProviderImpl.java b/src/com/android/settings/slices/SlicesFeatureProviderImpl.java index 8e5bc067150..16684bfb022 100644 --- a/src/com/android/settings/slices/SlicesFeatureProviderImpl.java +++ b/src/com/android/settings/slices/SlicesFeatureProviderImpl.java @@ -2,6 +2,7 @@ package com.android.settings.slices; import android.content.Context; +import com.android.settings.wifi.calling.WifiCallingSliceHelper; import com.android.settingslib.utils.ThreadUtils; /** @@ -39,4 +40,9 @@ public class SlicesFeatureProviderImpl implements SlicesFeatureProvider { SlicesIndexer indexer = getSliceIndexer(context); indexer.indexSliceData(); } -} \ No newline at end of file + + @Override + public WifiCallingSliceHelper getNewWifiCallingSliceHelper(Context context) { + return new WifiCallingSliceHelper(context); + } +} diff --git a/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java b/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java new file mode 100644 index 00000000000..3452af777b0 --- /dev/null +++ b/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java @@ -0,0 +1,363 @@ +/* + * 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.calling; + +import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; + +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.PersistableBundle; +import androidx.core.graphics.drawable.IconCompat; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Log; + +import androidx.slice.Slice; +import androidx.slice.builders.ListBuilder; +import androidx.slice.builders.SliceAction; + +import com.android.ims.ImsManager; +import com.android.internal.annotations.VisibleForTesting; +import com.android.settings.R; +import com.android.settings.slices.SliceBroadcastReceiver; +import com.android.settings.slices.SliceBuilderUtils; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + + +/** + * Helper class to control slices for wifi calling settings. + */ +public class WifiCallingSliceHelper { + + private static final String TAG = "WifiCallingSliceHelper"; + + /** + * Settings slice path to wifi calling setting. + */ + public static final String PATH_WIFI_CALLING = "wifi_calling"; + + /** + * Action passed for changes to wifi calling slice (toggle). + */ + public static final String ACTION_WIFI_CALLING_CHANGED = + "com.android.settings.wifi.calling.action.WIFI_CALLING_CHANGED"; + + /** + * Action for Wifi calling Settings activity which + * allows setting configuration for Wifi calling + * related settings + */ + public static final String ACTION_WIFI_CALLING_SETTINGS_ACTIVITY = + "android.settings.WIFI_CALLING_SETTINGS"; + + /** + * Timeout for querying wifi calling setting from ims manager. + */ + private static final int TIMEOUT_MILLIS = 2000; + + /** + * Time for which data contained in the slice can remain fresh. + */ + private static final int SLICE_TTL_MILLIS = 60000; + + protected SubscriptionManager mSubscriptionManager; + private final Context mContext; + + @VisibleForTesting + public WifiCallingSliceHelper(Context context) { + mContext = context; + } + + /** + * Returns Slice object for wifi calling settings. + * + * If wifi calling is being turned on and if wifi calling activation is needed for the current + * carrier, this method will return Slice with instructions to go to Settings App. + * + * If wifi calling is not supported for the current carrier, this method will return slice with + * not supported message. + * + * If wifi calling setting can be changed, this method will return the slice to toggle wifi + * calling option with ACTION_WIFI_CALLING_CHANGED as endItem. + */ + public Slice createWifiCallingSlice(Uri sliceUri) { + final int subId = getDefaultVoiceSubId(); + final String carrierName = getSimCarrierName(); + + if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + Log.d(TAG, "Invalid subscription Id"); + return getNonActionableWifiCallingSlice( + mContext.getString(R.string.wifi_calling_settings_title), + mContext.getString(R.string.wifi_calling_not_supported, carrierName), + sliceUri, SliceBuilderUtils.getSettingsIntent(mContext)); + } + + final ImsManager imsManager = getImsManager(subId); + + if (!imsManager.isWfcEnabledByPlatform() + || !imsManager.isWfcProvisionedOnDevice()) { + Log.d(TAG, "Wifi calling is either not provisioned or not enabled by Platform"); + return getNonActionableWifiCallingSlice( + mContext.getString(R.string.wifi_calling_settings_title), + mContext.getString(R.string.wifi_calling_not_supported, carrierName), + sliceUri, SliceBuilderUtils.getSettingsIntent(mContext)); + } + + try { + final boolean isWifiCallingEnabled = isWifiCallingEnabled(imsManager); + final Intent activationAppIntent = + getWifiCallingCarrierActivityIntent(subId); + + // Send this actionable wifi calling slice to toggle the setting + // only when there is no need for wifi calling activation with the server + if (activationAppIntent != null && !isWifiCallingEnabled) { + Log.d(TAG, "Needs Activation"); + // Activation needed for the next action of the user + // Give instructions to go to settings app + return getNonActionableWifiCallingSlice( + mContext.getString(R.string.wifi_calling_settings_title), + mContext.getString( + R.string.wifi_calling_settings_activation_instructions), + sliceUri, getActivityIntent(ACTION_WIFI_CALLING_SETTINGS_ACTIVITY)); + } + return getWifiCallingSlice(sliceUri, mContext, isWifiCallingEnabled); + } catch (InterruptedException | TimeoutException | ExecutionException e) { + Log.e(TAG, "Unable to read the current WiFi calling status", e); + return getNonActionableWifiCallingSlice( + mContext.getString(R.string.wifi_calling_settings_title), + mContext.getString(R.string.wifi_calling_turn_on), + sliceUri, getActivityIntent(ACTION_WIFI_CALLING_SETTINGS_ACTIVITY)); + } + } + + private boolean isWifiCallingEnabled(ImsManager imsManager) + throws InterruptedException, ExecutionException, TimeoutException { + final FutureTask isWifiOnTask = new FutureTask<>(new Callable() { + @Override + public Boolean call() { + return imsManager.isWfcEnabledByUser(); + } + }); + final ExecutorService executor = Executors.newSingleThreadExecutor(); + executor.execute(isWifiOnTask); + + Boolean isWifiEnabledByUser = false; + isWifiEnabledByUser = isWifiOnTask.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + + return isWifiEnabledByUser && imsManager.isNonTtyOrTtyOnVolteEnabled(); + } + + /** + * Builds a toggle slice where the intent takes you to the wifi calling page and the toggle + * enables/disables wifi calling. + */ + private Slice getWifiCallingSlice(Uri sliceUri, Context mContext, + boolean isWifiCallingEnabled) { + + final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.wifi_signal); + final String title = mContext.getString(R.string.wifi_calling_settings_title); + return new ListBuilder(mContext, sliceUri, SLICE_TTL_MILLIS) + .setColor(R.color.material_blue_500) + .addRow(b -> b + .setTitle(title) + .addEndItem( + new SliceAction( + getBroadcastIntent(ACTION_WIFI_CALLING_CHANGED), + null /* actionTitle */, isWifiCallingEnabled)) + .setPrimaryAction(new SliceAction( + getActivityIntent(ACTION_WIFI_CALLING_SETTINGS_ACTIVITY), + icon, + title))) + .build(); + } + + protected ImsManager getImsManager(int subId) { + return ImsManager.getInstance(mContext, SubscriptionManager.getPhoneId(subId)); + } + + private Integer getWfcMode(ImsManager imsManager) + throws InterruptedException, ExecutionException, TimeoutException { + FutureTask wfcModeTask = new FutureTask<>(new Callable() { + @Override + public Integer call() { + return imsManager.getWfcMode(false); + } + }); + ExecutorService executor = Executors.newSingleThreadExecutor(); + executor.execute(wfcModeTask); + return wfcModeTask.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } + + /** + * Handles wifi calling setting change from wifi calling slice and posts notification. Should be + * called when intent action is ACTION_WIFI_CALLING_CHANGED. Executed in @WorkerThread + * + * @param intent action performed + */ + public void handleWifiCallingChanged(Intent intent) { + final int subId = getDefaultVoiceSubId(); + + if (subId > SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + final ImsManager imsManager = getImsManager(subId); + if (imsManager.isWfcEnabledByPlatform() + || imsManager.isWfcProvisionedOnDevice()) { + final boolean currentValue = imsManager.isWfcEnabledByUser() + && imsManager.isNonTtyOrTtyOnVolteEnabled(); + final boolean newValue = intent.getBooleanExtra(EXTRA_TOGGLE_STATE, + currentValue); + final Intent activationAppIntent = + getWifiCallingCarrierActivityIntent(subId); + if (!newValue || activationAppIntent == null) { + // If either the action is to turn off wifi calling setting + // or there is no activation involved - Update the setting + if (newValue != currentValue) { + imsManager.setWfcSetting(newValue); + } + } + } + } + // notify change in slice in any case to get re-queried. This would result in displaying + // appropriate message with the updated setting. + final Uri uri = SliceBuilderUtils.getUri(PATH_WIFI_CALLING, false /*isPlatformSlice*/); + mContext.getContentResolver().notifyChange(uri, null); + } + + /** + * Returns Slice with the title and subtitle provided as arguments with wifi signal Icon. + * + * @param title Title of the slice + * @param subtitle Subtitle of the slice + * @param sliceUri slice uri + * @return Slice with title and subtitle + */ + // TODO(b/79548264) asses different scenarios and return null instead of non-actionable slice + private Slice getNonActionableWifiCallingSlice(String title, String subtitle, Uri sliceUri, + PendingIntent primaryActionIntent) { + final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.wifi_signal); + return new ListBuilder(mContext, sliceUri, SLICE_TTL_MILLIS) + .setColor(R.color.material_blue_500) + .addRow(b -> b + .setTitle(title) + .setSubtitle(subtitle) + .setPrimaryAction(new SliceAction( + primaryActionIntent, icon, + title))) + .build(); + } + + /** + * Returns {@code true} when the key is enabled for the carrier, and {@code false} otherwise. + */ + private boolean isCarrierConfigManagerKeyEnabled(Context mContext, String key, + int subId, boolean defaultValue) { + final CarrierConfigManager configManager = getCarrierConfigManager(mContext); + boolean ret = false; + if (configManager != null) { + final PersistableBundle bundle = configManager.getConfigForSubId(subId); + if (bundle != null) { + ret = bundle.getBoolean(key, defaultValue); + } + } + return ret; + } + + protected CarrierConfigManager getCarrierConfigManager(Context mContext) { + return mContext.getSystemService(CarrierConfigManager.class); + } + + /** + * Returns the current default voice subId obtained from SubscriptionManager + */ + protected int getDefaultVoiceSubId() { + if (mSubscriptionManager == null) { + mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); + } + return SubscriptionManager.getDefaultVoiceSubscriptionId(); + } + + /** + * Returns Intent of the activation app required to activate wifi calling or null if there is no + * need for activation. + */ + protected Intent getWifiCallingCarrierActivityIntent(int subId) { + final CarrierConfigManager configManager = getCarrierConfigManager(mContext); + if (configManager == null) { + return null; + } + + final PersistableBundle bundle = configManager.getConfigForSubId(subId); + if (bundle == null) { + return null; + } + + final String carrierApp = bundle.getString( + CarrierConfigManager.KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING); + if (TextUtils.isEmpty(carrierApp)) { + return null; + } + + final ComponentName componentName = ComponentName.unflattenFromString(carrierApp); + if (componentName == null) { + return null; + } + + final Intent intent = new Intent(); + intent.setComponent(componentName); + return intent; + } + + private PendingIntent getBroadcastIntent(String action) { + final Intent intent = new Intent(action); + intent.setClass(mContext, SliceBroadcastReceiver.class); + return PendingIntent.getBroadcast(mContext, 0 /* requestCode */, intent, + PendingIntent.FLAG_CANCEL_CURRENT); + } + + /** + * Returns PendingIntent to start activity specified by action + */ + private PendingIntent getActivityIntent(String action) { + final Intent intent = new Intent(action); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */); + } + + /** + * Returns carrier id name of the current Subscription + */ + private String getSimCarrierName() { + final TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class); + final CharSequence carrierName = telephonyManager.getSimCarrierIdName(); + if (carrierName == null) { + return mContext.getString(R.string.carrier); + } + return carrierName.toString(); + } + +} diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java index 7c50f0c2cdb..5e202d63471 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherSettings.java +++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java @@ -27,28 +27,42 @@ import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.UserManager; -import androidx.annotation.VisibleForTesting; +import android.provider.SearchIndexableResource; import android.text.TextUtils; import android.util.Log; +import androidx.annotation.VisibleForTesting; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.dashboard.RestrictedDashboardFragment; +import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.widget.SwitchBar; import com.android.settings.widget.SwitchBarController; +import com.android.settingslib.TetherUtil; import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.search.SearchIndexable; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.UUID; +@SearchIndexable public class WifiTetherSettings extends RestrictedDashboardFragment implements WifiTetherBasePreferenceController.OnTetherConfigUpdateListener { private static final String TAG = "WifiTetherSettings"; private static final IntentFilter TETHER_STATE_CHANGE_FILTER; - private static final String KEY_WIFI_TETHER_AUTO_OFF = "wifi_tether_auto_turn_off"; + private static final String KEY_WIFI_TETHER_SCREEN = "wifi_tether_settings_screen"; + @VisibleForTesting + static final String KEY_WIFI_TETHER_NETWORK_NAME = "wifi_tether_network_name"; + @VisibleForTesting + static final String KEY_WIFI_TETHER_NETWORK_PASSWORD = "wifi_tether_network_password"; + @VisibleForTesting + static final String KEY_WIFI_TETHER_AUTO_OFF = "wifi_tether_auto_turn_off"; + @VisibleForTesting + static final String KEY_WIFI_TETHER_NETWORK_AP_BAND = "wifi_tether_network_ap_band"; private WifiTetherSwitchBarController mSwitchBarController; private WifiTetherSSIDPreferenceController mSSIDPreferenceController; @@ -182,6 +196,33 @@ public class WifiTetherSettings extends RestrictedDashboardFragment .updateDisplay(); } + public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider() { + @Override + public List getXmlResourcesToIndex( + Context context, boolean enabled) { + final SearchIndexableResource sir = new SearchIndexableResource(context); + sir.xmlResId = R.xml.wifi_tether_settings; + return Arrays.asList(sir); + } + + @Override + public List getNonIndexableKeys(Context context) { + final List keys = super.getNonIndexableKeys(context); + + if (!TetherUtil.isTetherAvailable(context)) { + keys.add(KEY_WIFI_TETHER_NETWORK_NAME); + keys.add(KEY_WIFI_TETHER_NETWORK_PASSWORD); + keys.add(KEY_WIFI_TETHER_AUTO_OFF); + keys.add(KEY_WIFI_TETHER_NETWORK_AP_BAND); + } + + // Remove duplicate + keys.add(KEY_WIFI_TETHER_SCREEN); + return keys; + } + }; + @VisibleForTesting class TetherChangeReceiver extends BroadcastReceiver { @Override diff --git a/tests/robotests/assets/grandfather_not_implementing_index_provider b/tests/robotests/assets/grandfather_not_implementing_index_provider index 8bd4f823840..31e3a54ee26 100644 --- a/tests/robotests/assets/grandfather_not_implementing_index_provider +++ b/tests/robotests/assets/grandfather_not_implementing_index_provider @@ -21,7 +21,6 @@ com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionMi com.android.settings.enterprise.ApplicationListFragment$EnterpriseInstalledPackages com.android.settings.enterprise.EnterpriseSetDefaultAppsListFragment com.android.settings.inputmethod.KeyboardLayoutPickerFragment -com.android.settings.wifi.tether.WifiTetherSettings com.android.settings.wifi.SavedAccessPointsWifiSettings com.android.settings.notification.ZenModeEventRuleSettings com.android.settings.notification.ZenModeScheduleRuleSettings diff --git a/tests/robotests/src/com/android/settings/TetherSettingsTest.java b/tests/robotests/src/com/android/settings/TetherSettingsTest.java new file mode 100644 index 00000000000..1ccd958cfb3 --- /dev/null +++ b/tests/robotests/src/com/android/settings/TetherSettingsTest.java @@ -0,0 +1,150 @@ +/* + * 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; + +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.when; + +import android.content.Context; +import android.os.UserHandle; +import android.os.UserManager; +import android.net.ConnectivityManager; + +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +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; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(SettingsRobolectricTestRunner.class) +public class TetherSettingsTest { + + private Context mContext; + + @Mock + private ConnectivityManager mConnectivityManager; + @Mock + private UserManager mUserManager; + + @Before + public void setUp() { + mContext = spy(RuntimeEnvironment.application); + + MockitoAnnotations.initMocks(this); + doReturn(mConnectivityManager) + .when(mContext).getSystemService(Context.CONNECTIVITY_SERVICE); + doReturn(mUserManager) + .when(mContext).getSystemService(Context.USER_SERVICE); + + setupIsTetherAvailable(true); + + when(mConnectivityManager.getTetherableUsbRegexs()).thenReturn(new String[]{}); + when(mConnectivityManager.getTetherableBluetoothRegexs()).thenReturn(new String[]{}); + } + + @Test + public void testTetherNonIndexableKeys_tetherAvailable_keysNotReturned() { + // To let TetherUtil.isTetherAvailable return true, select one of the combinations + setupIsTetherAvailable(true); + + final List niks = + TetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); + + assertThat(niks).doesNotContain(TetherSettings.KEY_TETHER_PREFS_SCREEN); + assertThat(niks).doesNotContain(TetherSettings.KEY_WIFI_TETHER); + } + + @Test + public void testTetherNonIndexableKeys_tetherNotAvailable_keysReturned() { + // To let TetherUtil.isTetherAvailable return false, select one of the combinations + setupIsTetherAvailable(false); + + final List niks = + TetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); + + assertThat(niks).contains(TetherSettings.KEY_TETHER_PREFS_SCREEN); + assertThat(niks).contains(TetherSettings.KEY_WIFI_TETHER); + } + + @Test + public void testTetherNonIndexableKeys_usbNotAvailable_usbKeyReturned() { + when(mConnectivityManager.getTetherableUsbRegexs()).thenReturn(new String[]{}); + + final List niks = + TetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); + + assertThat(niks).contains(TetherSettings.KEY_USB_TETHER_SETTINGS); + } + + @Test + public void testTetherNonIndexableKeys_usbAvailable_usbKeyNotReturned() { + // We can ignore the condition of Utils.isMonkeyRunning() + // In normal case, monkey and robotest should not execute at the same time + when(mConnectivityManager.getTetherableUsbRegexs()).thenReturn(new String[]{"dummyRegex"}); + + final List niks = + TetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); + + assertThat(niks).doesNotContain(TetherSettings.KEY_USB_TETHER_SETTINGS); + } + + @Test + public void testTetherNonIndexableKeys_bluetoothNotAvailable_bluetoothKeyReturned() { + when(mConnectivityManager.getTetherableBluetoothRegexs()).thenReturn(new String[]{}); + + final List niks = + TetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); + + assertThat(niks).contains(TetherSettings.KEY_ENABLE_BLUETOOTH_TETHERING); + } + + @Test + public void testTetherNonIndexableKeys_bluetoothAvailable_bluetoothKeyNotReturned() { + when(mConnectivityManager.getTetherableBluetoothRegexs()) + .thenReturn(new String[]{"dummyRegex"}); + + final List niks = + TetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); + + assertThat(niks).doesNotContain(TetherSettings.KEY_ENABLE_BLUETOOTH_TETHERING); + } + + private void setupIsTetherAvailable(boolean returnValue) { + when(mConnectivityManager.isTetheringSupported()).thenReturn(true); + + // For RestrictedLockUtils.checkIfRestrictionEnforced + final int userId = UserHandle.myUserId(); + List enforcingUsers = new ArrayList<>(); + when(mUserManager.getUserRestrictionSources( + UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.of(userId))) + .thenReturn(enforcingUsers); + + // For RestrictedLockUtils.hasBaseUserRestriction + when(mUserManager.hasBaseUserRestriction( + UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.of(userId))) + .thenReturn(!returnValue); + } +} diff --git a/tests/robotests/src/com/android/settings/development/BluetoothDelayReportsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothDelayReportsPreferenceControllerTest.java deleted file mode 100644 index cadb91e87e3..00000000000 --- a/tests/robotests/src/com/android/settings/development/BluetoothDelayReportsPreferenceControllerTest.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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.development; - -import static com.android.settings.development.BluetoothDelayReportsPreferenceController - .BLUETOOTH_DISABLE_AVDTP_DELAY_REPORTS_PROPERTY; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.os.SystemProperties; -import androidx.preference.SwitchPreference; -import androidx.preference.PreferenceScreen; - -import com.android.settings.testutils.SettingsRobolectricTestRunner; - -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 BluetoothDelayReportsPreferenceControllerTest { - - @Mock - private PreferenceScreen mPreferenceScreen; - - private Context mContext; - private SwitchPreference mPreference; - private BluetoothDelayReportsPreferenceController mController; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - mContext = RuntimeEnvironment.application; - mPreference = new SwitchPreference(mContext); - mController = spy(new BluetoothDelayReportsPreferenceController(mContext)); - when(mPreferenceScreen.findPreference(mController.getPreferenceKey())) - .thenReturn(mPreference); - mController.displayPreference(mPreferenceScreen); - } - - @Test - public void onPreferenceChanged_settingDisabled_turnOnDelayReports() { - mController.onPreferenceChange(mPreference, true /* new value */); - - final boolean mode = SystemProperties.getBoolean( - BLUETOOTH_DISABLE_AVDTP_DELAY_REPORTS_PROPERTY, false /* default */); - - assertThat(mode).isTrue(); - } - - @Test - public void onPreferenceChanged_settingDisabled_turnOffDelayReports() { - mController.onPreferenceChange(mPreference, false /* new value */); - - final boolean mode = SystemProperties.getBoolean( - BLUETOOTH_DISABLE_AVDTP_DELAY_REPORTS_PROPERTY, false /* default */); - - assertThat(mode).isFalse(); - } - - @Test - public void updateState_settingDisabled_preferenceShouldBeChecked() { - SystemProperties.set(BLUETOOTH_DISABLE_AVDTP_DELAY_REPORTS_PROPERTY, - Boolean.toString(true)); - mController.updateState(mPreference); - - assertThat(mPreference.isChecked()).isTrue(); - } - - @Test - public void updateState_settingDisabled_preferenceShouldNotBeChecked() { - SystemProperties.set(BLUETOOTH_DISABLE_AVDTP_DELAY_REPORTS_PROPERTY, - Boolean.toString(false)); - mController.updateState(mPreference); - - assertThat(mPreference.isChecked()).isFalse(); - } - - @Test - public void onDeveloperOptionsDisabled_shouldDisablePreference() { - mController.onDeveloperOptionsDisabled(); - - final boolean mode = SystemProperties.getBoolean( - BLUETOOTH_DISABLE_AVDTP_DELAY_REPORTS_PROPERTY, false /* default */); - - assertThat(mode).isFalse(); - assertThat(mPreference.isEnabled()).isFalse(); - assertThat(mPreference.isChecked()).isFalse(); - } -} diff --git a/tests/robotests/src/com/android/settings/development/BluetoothInbandRingingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothInbandRingingPreferenceControllerTest.java deleted file mode 100644 index 9ac0cce88e9..00000000000 --- a/tests/robotests/src/com/android/settings/development/BluetoothInbandRingingPreferenceControllerTest.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.development; - -import static com.android.settings.development.BluetoothInbandRingingPreferenceController.BLUETOOTH_DISABLE_INBAND_RINGING_PROPERTY; -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.content.Context; -import android.os.SystemProperties; -import androidx.preference.SwitchPreference; -import androidx.preference.PreferenceScreen; - -import com.android.settings.testutils.SettingsRobolectricTestRunner; - -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 BluetoothInbandRingingPreferenceControllerTest { - - @Mock - private SwitchPreference mPreference; - @Mock - private PreferenceScreen mPreferenceScreen; - - private Context mContext; - private BluetoothInbandRingingPreferenceController mController; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - mContext = RuntimeEnvironment.application; - mController = spy(new BluetoothInbandRingingPreferenceController(mContext)); - doReturn(true).when(mController).isInbandRingingSupported(); - when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn( - mPreference); - mController.displayPreference(mPreferenceScreen); - } - - @Test - public void isAvailable_inbandRingingNotSupported_shouldReturnFalse() { - doReturn(false).when(mController).isInbandRingingSupported(); - assertThat(mController.isAvailable()).isFalse(); - } - - @Test - public void isAvailable_inbandRingingSupported_shouldReturnTrue() { - assertThat(mController.isAvailable()).isTrue(); - } - - @Test - public void onPreferenceChanged_settingEnabled_turnOnBluetoothSnoopLog() { - mController.onPreferenceChange(mPreference, true /* new value */); - - final boolean mode = SystemProperties - .getBoolean(BLUETOOTH_DISABLE_INBAND_RINGING_PROPERTY, false /* default */); - - assertThat(mode).isTrue(); - } - - @Test - public void onPreferenceChanged_settingDisabled_turnOffBluetoothSnoopLog() { - mController.onPreferenceChange(mPreference, false /* new value */); - - final boolean mode = SystemProperties - .getBoolean(BLUETOOTH_DISABLE_INBAND_RINGING_PROPERTY, false /* default */); - - assertThat(mode).isFalse(); - } - - @Test - public void updateState_settingEnabled_preferenceShouldBeChecked() { - SystemProperties.set(BLUETOOTH_DISABLE_INBAND_RINGING_PROPERTY, Boolean.toString(true)); - mController.updateState(mPreference); - - verify(mPreference).setChecked(true); - } - - @Test - public void updateState_settingDisabled_preferenceShouldNotBeChecked() { - SystemProperties.set(BLUETOOTH_DISABLE_INBAND_RINGING_PROPERTY, Boolean.toString(false)); - mController.updateState(mPreference); - - verify(mPreference).setChecked(false); - } - - @Test - public void onDeveloperOptionsDisabled_shouldDisablePreference() { - mController.onDeveloperOptionsDisabled(); - - final boolean mode = SystemProperties - .getBoolean(BLUETOOTH_DISABLE_INBAND_RINGING_PROPERTY, false /* default */); - - assertThat(mode).isFalse(); - verify(mPreference).setEnabled(false); - verify(mPreference).setChecked(false); - } -} diff --git a/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java index 9272b46b283..da32ce81909 100644 --- a/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java @@ -131,7 +131,6 @@ public class DevelopmentSettingsDashboardFragmentTest { @Config(shadows = { ShadowPictureColorModePreferenceController.class, ShadowAdbPreferenceController.class, - ShadowBluetoothInbandRingingPreferenceController.class, ShadowClearAdbKeysPreferenceController.class }) public void searchIndex_pageEnabled_shouldNotAddKeysToNonIndexable() { @@ -282,15 +281,6 @@ public class DevelopmentSettingsDashboardFragmentTest { } } - @Implements(BluetoothInbandRingingPreferenceController.class) - public static class ShadowBluetoothInbandRingingPreferenceController { - - @Implementation - public boolean isAvailable() { - return true; - } - } - @Implements(ClearAdbKeysPreferenceController.class) public static class ShadowClearAdbKeysPreferenceController { diff --git a/tests/robotests/src/com/android/settings/deviceinfo/DeviceModelPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/DeviceModelPreferenceControllerTest.java index c91683e00d4..dbffc3182e1 100644 --- a/tests/robotests/src/com/android/settings/deviceinfo/DeviceModelPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/deviceinfo/DeviceModelPreferenceControllerTest.java @@ -15,40 +15,44 @@ */ package com.android.settings.deviceinfo; -import static com.android.settings.deviceinfo.DeviceModelPreferenceController.getDeviceModel; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Fragment; +import android.app.FragmentManager; +import android.app.FragmentTransaction; import android.content.Context; -import androidx.preference.Preference; -import androidx.preference.PreferenceScreen; +import android.os.Build; import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Answers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import androidx.preference.Preference; +import androidx.preference.PreferenceManager; +import androidx.preference.PreferenceScreen; + @RunWith(SettingsRobolectricTestRunner.class) public class DeviceModelPreferenceControllerTest { - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private Fragment mFragment; - @Mock - private Preference mPreference; - @Mock - private PreferenceScreen mPreferenceScreen; + private final String KEY = "device_model_key"; + @Mock + private Fragment mFragment; + private Preference mPreference; + private PreferenceScreen mPreferenceScreen; private Context mContext; private DeviceModelPreferenceController mController; @@ -56,35 +60,49 @@ public class DeviceModelPreferenceControllerTest { public void setUp() { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; - mController = new DeviceModelPreferenceController(mContext, mFragment); - when(mPreferenceScreen.findPreference(mController.getPreferenceKey())) - .thenReturn(mPreference); - when(mPreference.getKey()).thenReturn(mController.getPreferenceKey()); + mController = new DeviceModelPreferenceController(mContext, KEY); + mController.setHost(mFragment); + mPreference = new Preference(mContext); + mPreference.setKey(KEY); + final PreferenceManager preferenceManager = new PreferenceManager(mContext); + mPreferenceScreen = preferenceManager.createPreferenceScreen(mContext); + mPreferenceScreen.addPreference(mPreference); } @Test public void isAvailable_returnTrueIfVisible() { - assertThat(mController.isAvailable()).isTrue(); + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.AVAILABLE); } @Test @Config(qualifiers = "mcc999") public void isAvailable_returnFalseIfNotVisible() { - assertThat(mController.isAvailable()).isFalse(); + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.UNSUPPORTED_ON_DEVICE); } @Test - public void displayPref_shouldSetSummary() { - mController.displayPreference(mPreferenceScreen); + public void updatePreference_summaryShouldContainBuildModel() { + mController.updateState(mPreference); - verify(mPreference).setSummary(mContext.getString(R.string.model_summary, getDeviceModel())); + assertThat(containBuildModel(mPreference.getSummary())).isTrue(); } @Test public void clickPreference_shouldLaunchHardwareInfoDialog() { + FragmentManager fragmentManager = mock(FragmentManager.class); + when(mFragment.getFragmentManager()).thenReturn(fragmentManager); + when(fragmentManager.beginTransaction()).thenReturn(mock(FragmentTransaction.class)); + assertThat(mController.handlePreferenceTreeClick(mPreference)).isTrue(); - verify(mFragment).getFragmentManager(); - verify(mFragment.getFragmentManager().beginTransaction()) + verify(fragmentManager.beginTransaction()) .add(any(HardwareInfoDialogFragment.class), eq(HardwareInfoDialogFragment.TAG)); } + + private boolean containBuildModel(CharSequence result) { + final String oracle = mContext.getResources().getString(R.string.model_summary, + Build.MODEL); + return result.toString().contains(oracle); + } } diff --git a/tests/robotests/src/com/android/settings/network/PrivateDnsModeDialogPreferenceTest.java b/tests/robotests/src/com/android/settings/network/PrivateDnsModeDialogPreferenceTest.java index e9ffa8ac4c6..dfea6fba515 100644 --- a/tests/robotests/src/com/android/settings/network/PrivateDnsModeDialogPreferenceTest.java +++ b/tests/robotests/src/com/android/settings/network/PrivateDnsModeDialogPreferenceTest.java @@ -28,7 +28,10 @@ import static org.mockito.Mockito.when; import android.app.AlertDialog; import android.app.Fragment; +import android.content.ContentResolver; import android.content.Context; +import android.content.DialogInterface; +import android.net.ConnectivityManager; import android.provider.Settings; import android.view.LayoutInflater; import android.view.View; @@ -150,4 +153,36 @@ public class PrivateDnsModeDialogPreferenceTest { assertThat(mSaveButton.isEnabled()).named("provider: " + invalid).isFalse(); } } + + @Test + public void testOnClick_positiveButtonClicked_saveData() { + // Set the default settings to OFF + final ContentResolver contentResolver = mContext.getContentResolver(); + Settings.Global.putString(contentResolver, Settings.Global.PRIVATE_DNS_MODE, + ConnectivityManager.PRIVATE_DNS_MODE_OFF); + + mPreference.mMode = ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; + mPreference.onClick(null, DialogInterface.BUTTON_POSITIVE); + + // Change to OPPORTUNISTIC + assertThat(Settings.Global.getString(contentResolver, + Settings.Global.PRIVATE_DNS_MODE)).isEqualTo( + ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC); + } + + @Test + public void testOnClick_negativeButtonClicked_doNothing() { + // Set the default settings to OFF + final ContentResolver contentResolver = mContext.getContentResolver(); + Settings.Global.putString(contentResolver, Settings.Global.PRIVATE_DNS_MODE, + ConnectivityManager.PRIVATE_DNS_MODE_OFF); + + mPreference.mMode = ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; + mPreference.onClick(null, DialogInterface.BUTTON_NEGATIVE); + + // Still equal to OFF + assertThat(Settings.Global.getString(contentResolver, + Settings.Global.PRIVATE_DNS_MODE)).isEqualTo( + ConnectivityManager.PRIVATE_DNS_MODE_OFF); + } } diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeSettingsTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeSettingsTest.java index 5b2782fb6b7..39e9271fa2f 100644 --- a/tests/robotests/src/com/android/settings/notification/ZenModeSettingsTest.java +++ b/tests/robotests/src/com/android/settings/notification/ZenModeSettingsTest.java @@ -134,13 +134,13 @@ public class ZenModeSettingsTest { public void testGetCallsSettingSummary_contacts() { Policy policy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS | Policy.PRIORITY_CATEGORY_CALLS, Policy.PRIORITY_SENDERS_CONTACTS, 0, 0); - assertThat(mBuilder.getCallsSettingSummary(policy)).isEqualTo("From contacts only"); + assertThat(mBuilder.getCallsSettingSummary(policy)).isEqualTo("From contacts"); } @Test public void testGetCallsSettingSummary_repeatCallers() { Policy policy = new Policy(Policy.PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0); - assertThat(mBuilder.getCallsSettingSummary(policy)).isEqualTo("From repeat callers only"); + assertThat(mBuilder.getCallsSettingSummary(policy)).isEqualTo("From repeat callers"); } @Test diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceControllerTest.java index f3d92d17d21..45446229930 100644 --- a/tests/robotests/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/ZenModeVisEffectsCustomPreferenceControllerTest.java @@ -88,23 +88,14 @@ public class ZenModeVisEffectsCustomPreferenceControllerTest { } @Test - public void isAvailable_menuOff_noVisEffects() { + public void isAvailable_noVisEffects() { mBackend.mPolicy = new NotificationManager.Policy(0, 0, 0, 0); - mController.mShowMenuSelected = false; - assertThat(mController.isAvailable()).isFalse(); - } - - @Test - public void isAvailable_menuOn_noVisEffects() { - mBackend.mPolicy = new NotificationManager.Policy(0, 0, 0, 0); - mController.mShowMenuSelected = true; assertThat(mController.isAvailable()).isTrue(); } @Test - public void isAvailable_menuOn_visEffects() { + public void isAvailable_visEffects() { mBackend.mPolicy = new NotificationManager.Policy(0, 0, 0, 1); - mController.mShowMenuSelected = false; assertThat(mController.isAvailable()).isTrue(); } diff --git a/tests/robotests/src/com/android/settings/notification/ZenOnboardingActivityTest.java b/tests/robotests/src/com/android/settings/notification/ZenOnboardingActivityTest.java index 7bc93713977..7fb02a535cc 100644 --- a/tests/robotests/src/com/android/settings/notification/ZenOnboardingActivityTest.java +++ b/tests/robotests/src/com/android/settings/notification/ZenOnboardingActivityTest.java @@ -29,6 +29,11 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OF import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; +import static com.android.settings.notification.ZenOnboardingActivity.ALWAYS_SHOW_THRESHOLD; +import static com.android.settings.notification.ZenOnboardingActivity + .PREF_KEY_SUGGESTION_FIRST_DISPLAY_TIME; +import static com.android.settings.notification.ZenOnboardingActivity.isSuggestionComplete; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -39,20 +44,22 @@ import static org.mockito.Mockito.when; import android.app.NotificationManager; import android.app.NotificationManager.Policy; import android.content.Context; +import android.content.SharedPreferences; +import android.provider.Settings; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.settings.R; +import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; +import org.robolectric.RuntimeEnvironment; @RunWith(SettingsRobolectricTestRunner.class) public class ZenOnboardingActivityTest { @@ -64,6 +71,9 @@ public class ZenOnboardingActivityTest { ZenOnboardingActivity mActivity; + private Context mContext; + private FakeFeatureFactory mFeatureFactory; + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -75,6 +85,11 @@ public class ZenOnboardingActivityTest { mActivity.setMetricsLogger(mMetricsLogger); mActivity.setupUI(); + + mContext = RuntimeEnvironment.application; + mFeatureFactory = FakeFeatureFactory.setupForTest(); + when(mFeatureFactory.suggestionsFeatureProvider.getSharedPrefs(any(Context.class))) + .thenReturn(getSharedPreferences()); } @Test @@ -124,4 +139,73 @@ public class ZenOnboardingActivityTest { verify(mNm, never()).setNotificationPolicy(any()); } + + @Test + public void isSuggestionComplete_zenUpdated() { + setZenUpdated(true); + setShowSettingsSuggestion(false); + setWithinTimeThreshold(true); + assertThat(isSuggestionComplete(mContext)).isTrue(); + } + + @Test + public void isSuggestionComplete_withinTimeThreshold() { + setZenUpdated(false); + setShowSettingsSuggestion(false); + setWithinTimeThreshold(true); + assertThat(isSuggestionComplete(mContext)).isFalse(); + } + + @Test + public void isSuggestionComplete_showSettingsSuggestionTrue() { + setZenUpdated(false); + setShowSettingsSuggestion(true); + setWithinTimeThreshold(false); + assertThat(isSuggestionComplete(mContext)).isFalse(); + } + + @Test + public void isSuggestionComplete_showSettingsSuggestionFalse_notWithinTimeThreshold() { + setZenUpdated(false); + setShowSettingsSuggestion(false); + setWithinTimeThreshold(false); + assertThat(isSuggestionComplete(mContext)).isTrue(); + } + + private void setZenUpdated(boolean updated) { + int zenUpdated = 0; + if (updated) { + zenUpdated = 1; + } + + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.ZEN_SETTINGS_UPDATED, zenUpdated); + } + + private void setWithinTimeThreshold(boolean withinTime) { + long firstTime = System.currentTimeMillis(); + + if (withinTime) { + firstTime -= ALWAYS_SHOW_THRESHOLD / 2; + } else { + firstTime -= ALWAYS_SHOW_THRESHOLD * 2; + } + + getSharedPreferences().edit().putLong(PREF_KEY_SUGGESTION_FIRST_DISPLAY_TIME, + firstTime).commit(); + } + + private void setShowSettingsSuggestion(boolean show) { + int showZenSuggestion = 0; + if (show) { + showZenSuggestion = 1; + } + + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.SHOW_ZEN_SETTINGS_SUGGESTION, showZenSuggestion); + } + + private SharedPreferences getSharedPreferences() { + return mContext.getSharedPreferences("test_zen_sugg", Context.MODE_PRIVATE); + } } diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java index 75b6bb4b14a..7ee9ea51c45 100644 --- a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java +++ b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java @@ -67,7 +67,6 @@ public class ChooseLockPasswordTest { Intent intent = new IntentBuilder(application) .setPassword("password") .setPasswordQuality(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) - .setPasswordLengthRange(123, 456) .setUserId(123) .build(); @@ -77,12 +76,6 @@ public class ChooseLockPasswordTest { assertThat(intent.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD)) .named("EXTRA_KEY_PASSWORD") .isEqualTo("password"); - assertThat(intent.getIntExtra(ChooseLockPassword.PASSWORD_MIN_KEY, 0)) - .named("PASSWORD_MIN_KEY") - .isEqualTo(123); - assertThat(intent.getIntExtra(ChooseLockPassword.PASSWORD_MAX_KEY, 0)) - .named("PASSWORD_MAX_KEY") - .isEqualTo(456); assertThat(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, 0)) .named("PASSWORD_TYPE_KEY") .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC); @@ -96,7 +89,6 @@ public class ChooseLockPasswordTest { Intent intent = new IntentBuilder(application) .setChallenge(12345L) .setPasswordQuality(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC) - .setPasswordLengthRange(123, 456) .setUserId(123) .build(); @@ -106,12 +98,6 @@ public class ChooseLockPasswordTest { assertThat(intent.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0L)) .named("EXTRA_KEY_CHALLENGE") .isEqualTo(12345L); - assertThat(intent.getIntExtra(ChooseLockPassword.PASSWORD_MIN_KEY, 0)) - .named("PASSWORD_MIN_KEY") - .isEqualTo(123); - assertThat(intent.getIntExtra(ChooseLockPassword.PASSWORD_MAX_KEY, 0)) - .named("PASSWORD_MAX_KEY") - .isEqualTo(456); assertThat(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, 0)) .named("PASSWORD_TYPE_KEY") .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC); diff --git a/tests/robotests/src/com/android/settings/suggestions/SettingsSuggestionsTest.java b/tests/robotests/src/com/android/settings/suggestions/SettingsSuggestionsTest.java index bb14667fce8..c6583c7eb03 100644 --- a/tests/robotests/src/com/android/settings/suggestions/SettingsSuggestionsTest.java +++ b/tests/robotests/src/com/android/settings/suggestions/SettingsSuggestionsTest.java @@ -30,6 +30,8 @@ import com.android.settings.R; import com.android.settings.Settings; import com.android.settings.fingerprint.FingerprintEnrollSuggestionActivity; import com.android.settings.fingerprint.FingerprintSuggestionActivity; +import com.android.settings.notification.ZenOnboardingActivity; +import com.android.settings.notification.ZenSuggestionActivity; import com.android.settings.support.NewDeviceIntroSuggestionActivity; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.wallpaper.WallpaperSuggestionActivity; @@ -82,6 +84,14 @@ public class SettingsSuggestionsTest { R.string.night_display_suggestion_summary); } + @Test + public void zenSuggestion_isValid() { + assertSuggestionEquals( + ZenSuggestionActivity.class.getName(), + R.string.zen_suggestion_title, + R.string.zen_suggestion_summary); + } + @Test public void newDeviceIntroSuggestion_isValid() { assertSuggestionEquals( diff --git a/tests/robotests/src/com/android/settings/support/NewDeviceIntroSuggestionActivityTest.java b/tests/robotests/src/com/android/settings/support/NewDeviceIntroSuggestionActivityTest.java index 15975689c7e..286676dd0d0 100644 --- a/tests/robotests/src/com/android/settings/support/NewDeviceIntroSuggestionActivityTest.java +++ b/tests/robotests/src/com/android/settings/support/NewDeviceIntroSuggestionActivityTest.java @@ -78,7 +78,7 @@ public class NewDeviceIntroSuggestionActivityTest { final long currentTime = System.currentTimeMillis(); getSharedPreferences().edit().putLong(PREF_KEY_SUGGGESTION_FIRST_DISPLAY_TIME, - currentTime - 2 * PERMANENT_DISMISS_THRESHOLD); + currentTime - 2 * PERMANENT_DISMISS_THRESHOLD).commit(); assertThat(isSuggestionComplete(mContext)).isTrue(); } diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java new file mode 100644 index 00000000000..ac3ff3ff635 --- /dev/null +++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java @@ -0,0 +1,316 @@ +/* + * 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.calling; + +import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; +import static android.app.slice.Slice.HINT_TITLE; +import static android.app.slice.SliceItem.FORMAT_TEXT; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +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.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.net.Uri; +import android.telephony.CarrierConfigManager; + +import androidx.slice.Slice; +import androidx.slice.SliceItem; +import androidx.slice.SliceMetadata; +import androidx.slice.SliceProvider; +import androidx.slice.core.SliceAction; +import androidx.slice.core.SliceQuery; +import androidx.slice.widget.SliceLiveData; + +import com.android.ims.ImsManager; +import com.android.settings.R; +import com.android.settings.slices.SettingsSliceProvider; +import com.android.settings.slices.SliceBroadcastReceiver; +import com.android.settings.slices.SliceBuilderUtils; +import com.android.settings.slices.SliceData; +import com.android.settings.slices.SlicesFeatureProvider; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; + +import java.util.List; + +@RunWith(SettingsRobolectricTestRunner.class) +public class WifiCallingSliceHelperTest { + + private Context mContext; + @Mock + private CarrierConfigManager mMockCarrierConfigManager; + + @Mock + private ImsManager mMockImsManager; + + private final Uri mWfcURI = Uri.parse("content://com.android.settings.slices/wifi_calling"); + + private FakeWifiCallingSliceHelper mWfcSliceHelper; + private SettingsSliceProvider mProvider; + private SliceBroadcastReceiver mReceiver; + private FakeFeatureFactory mFeatureFactory; + private SlicesFeatureProvider mSlicesFeatureProvider; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + + //setup for SettingsSliceProvider tests + mProvider = spy(new SettingsSliceProvider()); + doReturn(mContext).when(mProvider).getContext(); + + //setup for SliceBroadcastReceiver test + mReceiver = spy(new SliceBroadcastReceiver()); + + mFeatureFactory = FakeFeatureFactory.setupForTest(); + mSlicesFeatureProvider = mFeatureFactory.getSlicesFeatureProvider(); + + // Prevent crash in SliceMetadata. + Resources resources = spy(mContext.getResources()); + doReturn(60).when(resources).getDimensionPixelSize(anyInt()); + doReturn(resources).when(mContext).getResources(); + + mWfcSliceHelper = new FakeWifiCallingSliceHelper(mContext); + + // Set-up specs for SliceMetadata. + SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); + } + + @Test + public void test_CreateWifiCallingSlice_invalidSubId() { + mWfcSliceHelper.setDefaultVoiceSubId(-1); + + final Slice slice = mWfcSliceHelper.createWifiCallingSlice(mWfcURI); + + testWifiCallingSettingsUnavailableSlice(slice, null, + SliceBuilderUtils.getSettingsIntent(mContext)); + } + + @Test + public void test_CreateWifiCallingSlice_wfcNotSupported() { + doReturn(false).when(mMockImsManager).isWfcEnabledByPlatform(); + + final Slice slice = mWfcSliceHelper.createWifiCallingSlice(mWfcURI); + + assertThat(mWfcSliceHelper.getDefaultVoiceSubId()).isEqualTo(1); + testWifiCallingSettingsUnavailableSlice(slice, null, + SliceBuilderUtils.getSettingsIntent(mContext)); + } + + @Test + public void test_CreateWifiCallingSlice_needsActivation() { + /* In cases where activation is needed and the user action + would be turning on the wifi calling (i.e. if wifi calling is + turned off) we need to guide the user to wifi calling settings + activity so the user can perform the activation there.(PrimaryAction) + */ + doReturn(true).when(mMockImsManager).isWfcEnabledByPlatform(); + doReturn(true).when(mMockImsManager).isWfcProvisionedOnDevice(); + doReturn(false).when(mMockImsManager).isWfcEnabledByUser(); + doReturn(false).when(mMockImsManager).isNonTtyOrTtyOnVolteEnabled(); + doReturn(null).when(mMockCarrierConfigManager).getConfigForSubId(1); + mWfcSliceHelper.setActivationAppIntent(new Intent()); // dummy Intent + + final Slice slice = mWfcSliceHelper.createWifiCallingSlice(mWfcURI); + + assertThat(mWfcSliceHelper.getDefaultVoiceSubId()).isEqualTo(1); + testWifiCallingSettingsUnavailableSlice(slice, null, + getActivityIntent(WifiCallingSliceHelper.ACTION_WIFI_CALLING_SETTINGS_ACTIVITY)); + } + + @Test + public void test_CreateWifiCallingSlice_success() { + doReturn(true).when(mMockImsManager).isWfcEnabledByPlatform(); + doReturn(true).when(mMockImsManager).isWfcProvisionedOnDevice(); + doReturn(true).when(mMockImsManager).isWfcEnabledByUser(); + doReturn(true).when(mMockImsManager).isNonTtyOrTtyOnVolteEnabled(); + doReturn(null).when(mMockCarrierConfigManager).getConfigForSubId(1); + + final Slice slice = mWfcSliceHelper.createWifiCallingSlice(mWfcURI); + + assertThat(mWfcSliceHelper.getDefaultVoiceSubId()).isEqualTo(1); + testWifiCallingSettingsToggleSlice(slice, null); + } + + @Test + public void test_SettingSliceProvider_getsRightSliceWifiCalling() { + doReturn(true).when(mMockImsManager).isWfcEnabledByPlatform(); + doReturn(true).when(mMockImsManager).isWfcProvisionedOnDevice(); + doReturn(true).when(mMockImsManager).isWfcEnabledByUser(); + doReturn(true).when(mMockImsManager).isNonTtyOrTtyOnVolteEnabled(); + doReturn(null).when(mMockCarrierConfigManager).getConfigForSubId(1); + doReturn(mWfcSliceHelper).when(mSlicesFeatureProvider) + .getNewWifiCallingSliceHelper(mContext); + + final Slice slice = mProvider.onBindSlice(mWfcURI); + + assertThat(mWfcSliceHelper.getDefaultVoiceSubId()).isEqualTo(1); + testWifiCallingSettingsToggleSlice(slice, null); + } + + @Test + public void test_SliceBroadcastReceiver_toggleOffWifiCalling() { + doReturn(true).when(mMockImsManager).isWfcEnabledByPlatform(); + doReturn(true).when(mMockImsManager).isWfcProvisionedOnDevice(); + doReturn(false).when(mMockImsManager).isWfcEnabledByUser(); + doReturn(true).when(mMockImsManager).isNonTtyOrTtyOnVolteEnabled(); + doReturn(mWfcSliceHelper).when(mSlicesFeatureProvider) + .getNewWifiCallingSliceHelper(mContext); + mWfcSliceHelper.setActivationAppIntent(null); + + ArgumentCaptor mWfcSettingCaptor = ArgumentCaptor.forClass(Boolean.class); + + // turn on Wifi calling setting + Intent intent = new Intent(WifiCallingSliceHelper.ACTION_WIFI_CALLING_CHANGED); + intent.putExtra(EXTRA_TOGGLE_STATE, true); + + // change the setting + mReceiver.onReceive(mContext, intent); + + verify((mMockImsManager)).setWfcSetting(mWfcSettingCaptor.capture()); + + // assert the change + assertThat(mWfcSettingCaptor.getValue()).isTrue(); + } + + private void testWifiCallingSettingsUnavailableSlice(Slice slice, + SliceData sliceData, PendingIntent expectedPrimaryAction) { + final SliceMetadata metadata = SliceMetadata.from(mContext, slice); + + //Check there is no toggle action + final List toggles = metadata.getToggles(); + assertThat(toggles).isEmpty(); + + // Check whether the primary action is to open wifi calling settings activity + final PendingIntent primaryPendingIntent = + metadata.getPrimaryAction().getAction(); + assertThat(primaryPendingIntent).isEqualTo(expectedPrimaryAction); + + // Check the title + final List sliceItems = slice.getItems(); + assertTitle(sliceItems, mContext.getString(R.string.wifi_calling_settings_title)); + } + + private void testWifiCallingSettingsToggleSlice(Slice slice, + SliceData sliceData) { + final SliceMetadata metadata = SliceMetadata.from(mContext, slice); + + final List toggles = metadata.getToggles(); + assertThat(toggles).hasSize(1); + + final SliceAction mainToggleAction = toggles.get(0); + + // Check intent in Toggle Action + final PendingIntent togglePendingIntent = mainToggleAction.getAction(); + final PendingIntent expectedToggleIntent = getBroadcastIntent( + WifiCallingSliceHelper.ACTION_WIFI_CALLING_CHANGED); + assertThat(togglePendingIntent).isEqualTo(expectedToggleIntent); + + // Check primary intent + final PendingIntent primaryPendingIntent = metadata.getPrimaryAction().getAction(); + final PendingIntent expectedPendingIntent = + getActivityIntent(WifiCallingSliceHelper.ACTION_WIFI_CALLING_SETTINGS_ACTIVITY); + assertThat(primaryPendingIntent).isEqualTo(expectedPendingIntent); + + // Check the title + final List sliceItems = slice.getItems(); + assertTitle(sliceItems, mContext.getString(R.string.wifi_calling_settings_title)); + } + + private PendingIntent getBroadcastIntent(String action) { + final Intent intent = new Intent(action); + intent.setClass(mContext, SliceBroadcastReceiver.class); + return PendingIntent.getBroadcast(mContext, 0 /* requestCode */, intent, + PendingIntent.FLAG_CANCEL_CURRENT); + } + + private PendingIntent getActivityIntent(String action) { + final Intent intent = new Intent(action); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */); + } + + private void assertTitle(List sliceItems, String title) { + boolean hasTitle = false; + for (SliceItem item : sliceItems) { + List titleItems = SliceQuery.findAll(item, FORMAT_TEXT, HINT_TITLE, + null /* non-hints */); + if (titleItems == null) { + continue; + } + + hasTitle = true; + for (SliceItem subTitleItem : titleItems) { + assertThat(subTitleItem.getText()).isEqualTo(title); + } + } + assertThat(hasTitle).isTrue(); + } + private class FakeWifiCallingSliceHelper extends WifiCallingSliceHelper { + int mSubId = 1; + + private Intent mActivationAppIntent; + FakeWifiCallingSliceHelper(Context context) { + super(context); + mActivationAppIntent = null; + } + + @Override + protected CarrierConfigManager getCarrierConfigManager(Context mContext) { + return mMockCarrierConfigManager; + } + + @Override + protected ImsManager getImsManager(int subId) { + return mMockImsManager; + } + + protected int getDefaultVoiceSubId() { + return mSubId; + } + + protected void setDefaultVoiceSubId(int id) { + mSubId = id; + } + + @Override + protected Intent getWifiCallingCarrierActivityIntent(int subId) { + return mActivationAppIntent; + } + + public void setActivationAppIntent(Intent intent) { + mActivationAppIntent = intent; + } + } +} diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java new file mode 100644 index 00000000000..58bb1989d98 --- /dev/null +++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java @@ -0,0 +1,106 @@ +/* + * 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.tether; + +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.when; + +import android.content.Context; +import android.os.UserHandle; +import android.os.UserManager; +import android.net.ConnectivityManager; + +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +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; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(SettingsRobolectricTestRunner.class) +public class WifiTetherSettingsTest { + + private Context mContext; + + @Mock + private ConnectivityManager mConnectivityManager; + @Mock + private UserManager mUserManager; + + @Before + public void setUp() { + mContext = spy(RuntimeEnvironment.application); + + MockitoAnnotations.initMocks(this); + doReturn(mConnectivityManager) + .when(mContext).getSystemService(Context.CONNECTIVITY_SERVICE); + doReturn(mUserManager) + .when(mContext).getSystemService(Context.USER_SERVICE); + } + + @Test + public void testWifiTetherNonIndexableKeys_tetherAvailable_keysNotReturned() { + // To let TetherUtil.isTetherAvailable return true, select one of the combinations + setupIsTetherAvailable(true); + + final List niks = + WifiTetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); + + assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); + assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); + assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); + assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_AP_BAND); + } + + @Test + public void testWifiTetherNonIndexableKeys_tetherNotAvailable_keysReturned() { + // To let TetherUtil.isTetherAvailable return false, select one of the combinations + setupIsTetherAvailable(false); + + final List niks = + WifiTetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); + + assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); + assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); + assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); + assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_AP_BAND); + } + + private void setupIsTetherAvailable(boolean returnValue) { + when(mConnectivityManager.isTetheringSupported()).thenReturn(true); + + // For RestrictedLockUtils.checkIfRestrictionEnforced + final int userId = UserHandle.myUserId(); + List enforcingUsers = new ArrayList<>(); + when(mUserManager.getUserRestrictionSources( + UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.of(userId))) + .thenReturn(enforcingUsers); + + // For RestrictedLockUtils.hasBaseUserRestriction + when(mUserManager.hasBaseUserRestriction( + UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.of(userId))) + .thenReturn(!returnValue); + } +}