diff --git a/res/drawable/ic_signal_flashlight.xml b/res/drawable/ic_signal_flashlight.xml new file mode 100644 index 00000000000..e63595300d5 --- /dev/null +++ b/res/drawable/ic_signal_flashlight.xml @@ -0,0 +1,28 @@ + + + + + + diff --git a/res/values/arrays.xml b/res/values/arrays.xml index 37462f9ba7d..cbfc42814ab 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -268,17 +268,27 @@ - + + 0 + 1 + + + @string/wifi_ap_choose_2G @string/wifi_ap_choose_5G - - @string/wifi_ap_2G - @string/wifi_ap_5G + + 0 + -1 - + + @string/wifi_ap_choose_2G + @string/wifi_ap_prefer_5G + + + @string/wifi_ap_choose_auto @string/wifi_ap_choose_2G diff --git a/res/values/strings.xml b/res/values/strings.xml index e64bd87f8a5..773e17f8a4a 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1993,8 +1993,10 @@ Auto 2.4 GHz Band - + 5.0 GHz Band + + 5.0 GHz Band preferred 2.4 GHz diff --git a/res/xml/configure_notification_settings.xml b/res/xml/configure_notification_settings.xml index f563643aea4..fbb464ca938 100644 --- a/res/xml/configure_notification_settings.xml +++ b/res/xml/configure_notification_settings.xml @@ -56,6 +56,7 @@ android:title="@string/zen_mode_settings_title" settings:useAdminDisabledSummary="true" android:fragment="com.android.settings.notification.ZenModeSettings" + settings:controller="com.android.settings.notification.ZenModePreferenceController" settings:allowDividerAbove="false" /> diff --git a/res/xml/gestures.xml b/res/xml/gestures.xml index 650b1c44005..9f69102a3cb 100644 --- a/res/xml/gestures.xml +++ b/res/xml/gestures.xml @@ -67,6 +67,6 @@ android:key="gesture_prevent_ringing_summary" android:title="@string/gesture_prevent_ringing_screen_title" android:fragment="com.android.settings.gestures.PreventRingingGestureSettings" - settings:controller="com.android.settings.gestures.PreventRingingPreferenceController" /> + settings:controller="com.android.settings.gestures.PreventRingingParentPreferenceController" /> diff --git a/res/xml/sound_settings.xml b/res/xml/sound_settings.xml index 54a4190b8ef..d38a8e4dc7c 100644 --- a/res/xml/sound_settings.xml +++ b/res/xml/sound_settings.xml @@ -94,14 +94,15 @@ android:order="-120" settings:useAdminDisabledSummary="true" settings:keywords="@string/keywords_sounds_and_notifications_interruptions" - settings:allowDividerAbove="true"/> + settings:allowDividerAbove="true" + settings:controller="com.android.settings.notification.ZenModePreferenceController" /> + settings:controller="com.android.settings.gestures.PreventRingingParentPreferenceController" /> - + android:title="@string/wifi_hotspot_ap_band_title" /> diff --git a/src/com/android/settings/RadioInfo.java b/src/com/android/settings/RadioInfo.java index c04269c1696..557c39b1648 100644 --- a/src/com/android/settings/RadioInfo.java +++ b/src/com/android/settings/RadioInfo.java @@ -17,6 +17,7 @@ package com.android.settings; import static android.net.ConnectivityManager.NetworkCallback; +import static android.provider.Settings.Global.PREFERRED_NETWORK_MODE; import android.app.Activity; import android.app.AlertDialog; @@ -39,6 +40,7 @@ import android.os.AsyncResult; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.provider.Settings; import android.telephony.CarrierConfigManager; import android.telephony.CellInfo; import android.telephony.CellInfoCdma; @@ -1451,6 +1453,19 @@ public class RadioInfo extends Activity { if (mPreferredNetworkTypeResult != pos && pos >= 0 && pos <= mPreferredNetworkLabels.length - 2) { mPreferredNetworkTypeResult = pos; + + // TODO: Possibly migrate this to TelephonyManager.setPreferredNetworkType() + // which today still has some issues (mostly that the "set" is conditional + // on a successful modem call, which is not what we want). Instead we always + // want this setting to be set, so that if the radio hiccups and this setting + // is for some reason unsuccessful, future calls to the radio will reflect + // the users's preference which is set here. + final int subId = phone.getSubId(); + if (SubscriptionManager.isUsableSubIdValue(subId)) { + Settings.Global.putInt(phone.getContext().getContentResolver(), + PREFERRED_NETWORK_MODE + subId, mPreferredNetworkTypeResult); + } + log("Calling setPreferredNetworkType(" + mPreferredNetworkTypeResult + ")"); Message msg = mHandler.obtainMessage(EVENT_SET_PREFERRED_TYPE_DONE); phone.setPreferredNetworkType(mPreferredNetworkTypeResult, msg); } diff --git a/src/com/android/settings/accounts/AccountSyncSettings.java b/src/com/android/settings/accounts/AccountSyncSettings.java index 66f9179ed64..66e8d2205c0 100644 --- a/src/com/android/settings/accounts/AccountSyncSettings.java +++ b/src/com/android/settings/accounts/AccountSyncSettings.java @@ -247,10 +247,13 @@ public class AccountSyncSettings extends AccountPreferenceBase { } if (preference instanceof SyncStateSwitchPreference) { SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) preference; - String authority = syncPref.getAuthority(); - Account account = syncPref.getAccount(); + final String authority = syncPref.getAuthority(); + if (TextUtils.isEmpty(authority)) { + return false; + } + final Account account = syncPref.getAccount(); final int userId = mUserHandle.getIdentifier(); - String packageName = syncPref.getPackageName(); + final String packageName = syncPref.getPackageName(); boolean syncAutomatically = ContentResolver.getSyncAutomaticallyAsUser(account, authority, userId); diff --git a/src/com/android/settings/applications/defaultapps/DefaultBrowserPicker.java b/src/com/android/settings/applications/defaultapps/DefaultBrowserPicker.java index 8153be281b3..c243970bad0 100644 --- a/src/com/android/settings/applications/defaultapps/DefaultBrowserPicker.java +++ b/src/com/android/settings/applications/defaultapps/DefaultBrowserPicker.java @@ -17,9 +17,11 @@ package com.android.settings.applications.defaultapps; import android.content.Context; +import android.content.pm.ComponentInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.util.ArraySet; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; @@ -27,6 +29,7 @@ import com.android.settingslib.applications.DefaultAppInfo; import java.util.ArrayList; import java.util.List; +import java.util.Set; /** * Fragment for choosing default browser. @@ -62,14 +65,20 @@ public class DefaultBrowserPicker extends DefaultAppPickerFragment { DefaultBrowserPreferenceController.BROWSE_PROBE, PackageManager.MATCH_ALL, mUserId); final int count = list.size(); + final Set addedPackages = new ArraySet<>(); for (int i = 0; i < count; i++) { ResolveInfo info = list.get(i); if (info.activityInfo == null || !info.handleAllWebDataURI) { continue; } + final String packageName = info.activityInfo.packageName; + if (addedPackages.contains(packageName)) { + continue; + } try { candidates.add(new DefaultAppInfo(context, mPm, - mPm.getApplicationInfoAsUser(info.activityInfo.packageName, 0, mUserId))); + mPm.getApplicationInfoAsUser(packageName, 0, mUserId))); + addedPackages.add(packageName); } catch (PackageManager.NameNotFoundException e) { // Skip unknown packages. } diff --git a/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java b/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java index bb60df929e4..27b7676a2f2 100644 --- a/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java +++ b/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java @@ -21,11 +21,13 @@ import android.content.pm.PackageManager; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; + import com.android.settings.bluetooth.BluetoothDeviceUpdater; import com.android.settings.bluetooth.SavedBluetoothDeviceUpdater; +import com.android.settings.connecteddevice.dock.DockUpdater; import com.android.settings.core.BasePreferenceController; import com.android.settings.dashboard.DashboardFragment; - +import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; @@ -35,10 +37,14 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc private Preference mPreference; private BluetoothDeviceUpdater mBluetoothDeviceUpdater; + private DockUpdater mSavedDockUpdater; private int mPreferenceSize; public PreviouslyConnectedDevicePreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); + + mSavedDockUpdater = FeatureFactory.getFactory( + context).getDockUpdaterFeatureProvider().getSavedDockUpdater(context, this); } @Override @@ -60,12 +66,14 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc @Override public void onStart() { mBluetoothDeviceUpdater.registerCallback(); + mSavedDockUpdater.registerCallback(); updatePreferenceOnSizeChanged(); } @Override public void onStop() { mBluetoothDeviceUpdater.unregisterCallback(); + mSavedDockUpdater.unregisterCallback(); } public void init(DashboardFragment fragment) { @@ -90,6 +98,11 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc mBluetoothDeviceUpdater = bluetoothDeviceUpdater; } + @VisibleForTesting + void setSavedDockUpdater(DockUpdater savedDockUpdater) { + mSavedDockUpdater = savedDockUpdater; + } + @VisibleForTesting void setPreferenceSize(int size) { mPreferenceSize = size; @@ -105,4 +118,4 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc mPreference.setEnabled(mPreferenceSize != 0); } } -} \ No newline at end of file +} diff --git a/src/com/android/settings/fingerprint/FingerprintEnrollFindSensor.java b/src/com/android/settings/fingerprint/FingerprintEnrollFindSensor.java index 95a534cdd42..03bba42f5be 100644 --- a/src/com/android/settings/fingerprint/FingerprintEnrollFindSensor.java +++ b/src/com/android/settings/fingerprint/FingerprintEnrollFindSensor.java @@ -24,6 +24,7 @@ import androidx.annotation.Nullable; import android.view.View; import android.widget.Button; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settings.Utils; @@ -35,7 +36,8 @@ import com.android.settings.password.ChooseLockSettingsHelper; */ public class FingerprintEnrollFindSensor extends FingerprintEnrollBase { - private static final int CONFIRM_REQUEST = 1; + @VisibleForTesting + static final int CONFIRM_REQUEST = 1; private static final int ENROLLING = 2; public static final String EXTRA_KEY_LAUNCHED_CONFIRM = "launched_confirm_lock"; @@ -170,7 +172,7 @@ public class FingerprintEnrollFindSensor extends FingerprintEnrollBase { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == CONFIRM_REQUEST) { - if (resultCode == RESULT_OK) { + if (resultCode == RESULT_OK && data != null) { mToken = data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN); overridePendingTransition(R.anim.suw_slide_next_in, R.anim.suw_slide_next_out); getIntent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken); diff --git a/src/com/android/settings/fingerprint/FingerprintSettings.java b/src/com/android/settings/fingerprint/FingerprintSettings.java index 0fd3f8be338..4aaf997dd85 100644 --- a/src/com/android/settings/fingerprint/FingerprintSettings.java +++ b/src/com/android/settings/fingerprint/FingerprintSettings.java @@ -785,6 +785,7 @@ public class FingerprintSettings extends SubSettings { if (mDeleteInProgress) { mAlertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(false); } + mDialogTextField.requestFocus(); } }); if (mTextHadFocus == null || mTextHadFocus) { diff --git a/src/com/android/settings/flashlight/FlashlightSliceBuilder.java b/src/com/android/settings/flashlight/FlashlightSliceBuilder.java new file mode 100644 index 00000000000..6317ce760f4 --- /dev/null +++ b/src/com/android/settings/flashlight/FlashlightSliceBuilder.java @@ -0,0 +1,156 @@ +/* + * 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.flashlight; + +import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; +import static androidx.slice.builders.ListBuilder.ICON_IMAGE; + +import android.annotation.ColorInt; +import android.app.PendingIntent; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraManager; +import android.net.Uri; +import android.provider.Settings; +import android.provider.Settings.Secure; +import android.provider.SettingsSlicesContract; +import android.util.Log; + +import androidx.core.graphics.drawable.IconCompat; +import com.android.settings.R; +import com.android.settings.Utils; +import com.android.settings.slices.SettingsSliceProvider; +import com.android.settings.slices.SliceBroadcastReceiver; + +import androidx.slice.Slice; +import androidx.slice.builders.ListBuilder; +import androidx.slice.builders.SliceAction; + +import android.app.StatusBarManager; + + +/** + * Utility class to build a Flashlight Slice, and handle all associated actions. + */ +public class FlashlightSliceBuilder { + + private static final String TAG = "FlashlightSliceBuilder"; + + public static final String KEY_FLASHLIGHT = "flashlight"; + + /** + * Backing Uri for the Flashlight Slice. + */ + public static final Uri FLASHLIGHT_URI = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(SettingsSliceProvider.SLICE_AUTHORITY) + .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION) + .appendPath(KEY_FLASHLIGHT) + .build(); + + /** + * Action notifying a change on the Flashlight Slice. + */ + public static final String ACTION_FLASHLIGHT_SLICE_CHANGED = + "com.android.settings.flashlight.action.FLASHLIGHT_SLICE_CHANGED"; + + /** + * Action broadcasting a change on whether flashlight is on or off. + */ + public static final String ACTION_FLASHLIGHT_CHANGED = + "com.android.settings.flashlight.action.FLASHLIGHT_CHANGED"; + + public static final IntentFilter INTENT_FILTER = new IntentFilter(ACTION_FLASHLIGHT_CHANGED); + + private FlashlightSliceBuilder() {} + + /** + * Return a Flashlight Slice bound to {@link #FLASHLIGHT_URI}. + */ + public static Slice getSlice(Context context) { + if (!isFlashlightAvailable(context)) { + return null; + } + final PendingIntent toggleAction = getBroadcastIntent(context); + @ColorInt final int color = Utils.getColorAccentDefaultColor(context); + final IconCompat icon = + IconCompat.createWithResource(context, R.drawable.ic_signal_flashlight); + return new ListBuilder(context, FLASHLIGHT_URI, ListBuilder.INFINITY) + .setAccentColor(color) + .addRow(b -> b + .setTitle(context.getText(R.string.power_flashlight)) + .setTitleItem(icon, ICON_IMAGE) + .setPrimaryAction( + new SliceAction(toggleAction, null, isFlashlightEnabled(context)))) + .build(); + } + + /** + * Update the current flashlight status to the boolean value keyed by + * {@link android.app.slice.Slice#EXTRA_TOGGLE_STATE} on {@param intent}. + */ + public static void handleUriChange(Context context, Intent intent) { + try { + final String cameraId = getCameraId(context); + if (cameraId != null) { + final boolean state = intent.getBooleanExtra( + EXTRA_TOGGLE_STATE, isFlashlightEnabled(context)); + final CameraManager cameraManager = context.getSystemService(CameraManager.class); + cameraManager.setTorchMode(cameraId, state); + } + } catch (CameraAccessException e) { + Log.e(TAG, "Camera couldn't set torch mode.", e); + } + context.getContentResolver().notifyChange(FLASHLIGHT_URI, null); + } + + private static String getCameraId(Context context) throws CameraAccessException { + final CameraManager cameraManager = context.getSystemService(CameraManager.class); + final String[] ids = cameraManager.getCameraIdList(); + for (String id : ids) { + CameraCharacteristics c = cameraManager.getCameraCharacteristics(id); + Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); + Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING); + if (flashAvailable != null && flashAvailable + && lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) { + return id; + } + } + return null; + } + + private static PendingIntent getBroadcastIntent(Context context) { + final Intent intent = new Intent(ACTION_FLASHLIGHT_SLICE_CHANGED); + intent.setClass(context, SliceBroadcastReceiver.class); + return PendingIntent.getBroadcast(context, 0 /* requestCode */, intent, + PendingIntent.FLAG_CANCEL_CURRENT); + } + + private static boolean isFlashlightAvailable(Context context) { + return Settings.Secure.getInt( + context.getContentResolver(), Secure.FLASHLIGHT_AVAILABLE, 0) == 1; + } + + private static boolean isFlashlightEnabled(Context context) { + return Settings.Secure.getInt( + context.getContentResolver(), Secure.FLASHLIGHT_ENABLED, 0) == 1; + } +} diff --git a/src/com/android/settings/fuelgauge/batterytip/StatsManagerConfig.java b/src/com/android/settings/fuelgauge/batterytip/StatsManagerConfig.java index a0d4e9baf9d..7d81c4273d8 100644 --- a/src/com/android/settings/fuelgauge/batterytip/StatsManagerConfig.java +++ b/src/com/android/settings/fuelgauge/batterytip/StatsManagerConfig.java @@ -46,15 +46,181 @@ public class StatsManagerConfig { AnomalyType.EXCESSIVE_WAKEUPS_IN_BACKGROUND, AnomalyType.EXCESSIVE_UNOPTIMIZED_BLE_SCAN, AnomalyType.EXCESSIVE_BACKGROUND_SERVICE, - AnomalyType.EXCESSIVE_WIFI_SCAN}) + AnomalyType.EXCESSIVE_WIFI_SCAN, + AnomalyType.EXCESSIVE_FLASH_WRITES, + AnomalyType.EXCESSIVE_MEMORY_IN_BACKGROUND, + AnomalyType.EXCESSIVE_DAVEY_RATE, + AnomalyType.EXCESSIVE_JANKY_FRAMES, + AnomalyType.SLOW_COLD_START_TIME, + AnomalyType.SLOW_HOT_START_TIME, + AnomalyType.SLOW_WARM_START_TIME, + AnomalyType.EXCESSIVE_BACKGROUND_SYNCS, + AnomalyType.EXCESSIVE_GPS_SCANS_IN_BACKGROUND, + AnomalyType.EXCESSIVE_JOB_SCHEDULING, + AnomalyType.EXCESSIVE_MOBILE_NETWORK_IN_BACKGROUND, + AnomalyType.EXCESSIVE_WIFI_LOCK_TIME, + AnomalyType.JOB_TIMED_OUT, + AnomalyType.LONG_UNOPTIMIZED_BLE_SCAN, + AnomalyType.BACKGROUND_ANR, + AnomalyType.BACKGROUND_CRASH_RATE, + AnomalyType.EXCESSIVE_ANR_LOOPING, + AnomalyType.EXCESSIVE_ANRS, + AnomalyType.EXCESSIVE_CRASH_RATE, + AnomalyType.EXCESSIVE_CRASH_LOOPING, + AnomalyType.NUMBER_OF_OPEN_FILES, + }) public @interface AnomalyType { + /** + * This represents an error condition in the anomaly detection. + */ int NULL = -1; + + /** + * The anomaly type does not match any other defined type. + */ int UNKNOWN_REASON = 0; + + /** + * The application held a partial (screen off) wake lock for a period of time that + * exceeded the threshold with the screen off when not charging. + */ int EXCESSIVE_WAKELOCK_ALL_SCREEN_OFF = 1; + + /** + * The application exceeded the maximum number of wakeups while in the background + * when not charging. + */ int EXCESSIVE_WAKEUPS_IN_BACKGROUND = 2; + + /** + * The application did unoptimized Bluetooth scans too frequently when not charging. + */ int EXCESSIVE_UNOPTIMIZED_BLE_SCAN = 3; + + /** + * The application ran in the background for a period of time that exceeded the + * threshold. + */ int EXCESSIVE_BACKGROUND_SERVICE = 4; + + /** + * The application exceeded the maximum number of wifi scans when not charging. + */ int EXCESSIVE_WIFI_SCAN = 5; + + /** + * The application exceed the maximum number of flash writes + */ + int EXCESSIVE_FLASH_WRITES = 6; + + /** + * The application used more than the maximum memory, while not spending any time + * in the foreground. + */ + int EXCESSIVE_MEMORY_IN_BACKGROUND = 7; + + /** + * The application exceeded the maximum percentage of frames with a render rate of + * greater than 700ms. + */ + int EXCESSIVE_DAVEY_RATE = 8; + + /** + * The application exceeded the maximum percentage of frames with a render rate + * greater than 16ms. + */ + int EXCESSIVE_JANKY_FRAMES = 9; + + /** + * The application exceeded the maximum cold start time - the app has not been + * launched since last system start, died or was killed. + */ + int SLOW_COLD_START_TIME = 10; + + /** + * The application exceeded the maximum hot start time - the app and activity are + * already in memory. + */ + int SLOW_HOT_START_TIME = 11; + + /** + * The application exceeded the maximum warm start time - the app was already in + * memory but the activity wasn’t created yet or was removed from memory. + */ + int SLOW_WARM_START_TIME = 12; + + /** + * The application exceeded the maximum number of syncs while in the background. + */ + int EXCESSIVE_BACKGROUND_SYNCS = 13; + + /** + * The application exceeded the maximum number of gps scans while in the background. + */ + int EXCESSIVE_GPS_SCANS_IN_BACKGROUND = 14; + + /** + * The application scheduled more than the maximum number of jobs while not charging. + */ + int EXCESSIVE_JOB_SCHEDULING = 15; + + /** + * The application exceeded the maximum amount of mobile network traffic while in + * the background. + */ + int EXCESSIVE_MOBILE_NETWORK_IN_BACKGROUND = 16; + + /** + * The application held the WiFi lock for more than the maximum amount of time while + * not charging. + */ + int EXCESSIVE_WIFI_LOCK_TIME = 17; + + /** + * The application scheduled a job that ran longer than the maximum amount of time. + */ + int JOB_TIMED_OUT = 18; + + /** + * The application did an unoptimized Bluetooth scan that exceeded the maximum + * time while in the background. + */ + int LONG_UNOPTIMIZED_BLE_SCAN = 19; + + /** + * The application exceeded the maximum ANR rate while in the background. + */ + int BACKGROUND_ANR = 20; + + /** + * The application exceeded the maximum crash rate while in the background. + */ + int BACKGROUND_CRASH_RATE = 21; + + /** + * The application exceeded the maximum ANR-looping rate. + */ + int EXCESSIVE_ANR_LOOPING = 22; + + /** + * The application exceeded the maximum ANR rate. + */ + int EXCESSIVE_ANRS = 23; + + /** + * The application exceeded the maximum crash rate. + */ + int EXCESSIVE_CRASH_RATE = 24; + + /** + * The application exceeded the maximum crash-looping rate. + */ + int EXCESSIVE_CRASH_LOOPING = 25; + + /** + * The application crashed because no more file descriptors were available. + */ + int NUMBER_OF_OPEN_FILES = 26; } } diff --git a/src/com/android/settings/gestures/PreventRingingParentPreferenceController.java b/src/com/android/settings/gestures/PreventRingingParentPreferenceController.java new file mode 100644 index 00000000000..88147657b27 --- /dev/null +++ b/src/com/android/settings/gestures/PreventRingingParentPreferenceController.java @@ -0,0 +1,36 @@ +/* + * 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.gestures; + +import android.content.Context; + +import com.android.settings.core.BasePreferenceController; + +public class PreventRingingParentPreferenceController extends BasePreferenceController { + + public PreventRingingParentPreferenceController(Context context, String preferenceKey) { + super(context, preferenceKey); + } + + @Override + public int getAvailabilityStatus() { + return mContext.getResources().getBoolean( + com.android.internal.R.bool.config_volumeHushGestureEnabled) + ? AVAILABLE_UNSEARCHABLE : UNSUPPORTED_ON_DEVICE; + } + +} diff --git a/src/com/android/settings/gestures/PreventRingingPreferenceController.java b/src/com/android/settings/gestures/PreventRingingPreferenceController.java index c6bc3aa8f26..be55151f91e 100644 --- a/src/com/android/settings/gestures/PreventRingingPreferenceController.java +++ b/src/com/android/settings/gestures/PreventRingingPreferenceController.java @@ -24,14 +24,8 @@ import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE; import android.content.Context; import android.os.Bundle; import android.provider.Settings; -import androidx.annotation.VisibleForTesting; -import androidx.preference.ListPreference; -import androidx.preference.Preference; -import androidx.preference.PreferenceScreen; import com.android.settings.R; -import com.android.settings.core.BasePreferenceController; -import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.widget.VideoPreference; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnCreate; @@ -39,8 +33,13 @@ import com.android.settingslib.core.lifecycle.events.OnPause; import com.android.settingslib.core.lifecycle.events.OnResume; import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState; -public class PreventRingingPreferenceController extends BasePreferenceController - implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener, +import androidx.annotation.VisibleForTesting; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +public class PreventRingingPreferenceController extends PreventRingingParentPreferenceController + implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause, OnCreate, OnSaveInstanceState { private static final String PREF_KEY_VIDEO = "gesture_prevent_ringing_video"; @@ -59,9 +58,11 @@ public class PreventRingingPreferenceController extends BasePreferenceController @Override public int getAvailabilityStatus() { - return mContext.getResources().getBoolean( - com.android.internal.R.bool.config_volumeHushGestureEnabled) - ? AVAILABLE : UNSUPPORTED_ON_DEVICE; + final int status = super.getAvailabilityStatus(); + if (status == AVAILABLE_UNSEARCHABLE) { + return AVAILABLE; + } + return status; } @Override diff --git a/src/com/android/settings/notification/ConfigureNotificationSettings.java b/src/com/android/settings/notification/ConfigureNotificationSettings.java index cdc02c042d8..96a4eb62a6c 100644 --- a/src/com/android/settings/notification/ConfigureNotificationSettings.java +++ b/src/com/android/settings/notification/ConfigureNotificationSettings.java @@ -24,8 +24,6 @@ import android.content.Intent; import android.os.Bundle; import android.os.UserHandle; import android.provider.SearchIndexableResource; -import androidx.annotation.VisibleForTesting; -import androidx.preference.Preference; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; @@ -42,6 +40,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; + @SearchIndexable public class ConfigureNotificationSettings extends DashboardFragment { private static final String TAG = "ConfigNotiSettings"; @@ -57,7 +58,6 @@ public class ConfigureNotificationSettings extends DashboardFragment { static final String KEY_SWIPE_DOWN = "gesture_swipe_down_fingerprint_notifications"; private static final String KEY_NOTI_DEFAULT_RINGTONE = "notification_default_ringtone"; - private static final String KEY_ZEN_MODE = "zen_mode_notifications"; private RingtonePreference mRequestPreference; private static final int REQUEST_CODE = 200; @@ -111,7 +111,6 @@ public class ConfigureNotificationSettings extends DashboardFragment { } }); - controllers.add(new ZenModePreferenceController(context, lifecycle, KEY_ZEN_MODE)); return controllers; } @@ -220,7 +219,6 @@ public class ConfigureNotificationSettings extends DashboardFragment { keys.add(KEY_LOCKSCREEN); keys.add(KEY_LOCKSCREEN_WORK_PROFILE); keys.add(KEY_LOCKSCREEN_WORK_PROFILE_HEADER); - keys.add(KEY_ZEN_MODE); return keys; } }; diff --git a/src/com/android/settings/notification/SoundSettings.java b/src/com/android/settings/notification/SoundSettings.java index 7ad22a93bd0..dbac1c264c6 100644 --- a/src/com/android/settings/notification/SoundSettings.java +++ b/src/com/android/settings/notification/SoundSettings.java @@ -27,10 +27,6 @@ import android.preference.SeekBarVolumizer; import android.provider.SearchIndexableResource; import android.text.TextUtils; -import androidx.annotation.VisibleForTesting; -import androidx.preference.ListPreference; -import androidx.preference.Preference; - import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; @@ -50,13 +46,16 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import androidx.annotation.VisibleForTesting; +import androidx.preference.ListPreference; +import androidx.preference.Preference; + @SearchIndexable public class SoundSettings extends DashboardFragment { private static final String TAG = "SoundSettings"; private static final String SELECTED_PREFERENCE_KEY = "selected_preference"; private static final int REQUEST_CODE = 200; - private static final String KEY_ZEN_MODE = "zen_mode"; private static final int SAMPLE_CUTOFF = 2000; // manually cap sample playback at 2 seconds @VisibleForTesting @@ -190,7 +189,7 @@ public class SoundSettings extends DashboardFragment { onPreferenceDataChanged(listPreference)); mMediaOutputControllerKey = use(MediaOutputPreferenceController.class).getPreferenceKey(); use(HandsFreeProfileOutputPreferenceController.class).setCallback(listPreference -> - onPreferenceDataChanged(listPreference)); + onPreferenceDataChanged(listPreference)); mHfpOutputControllerKey = use(HandsFreeProfileOutputPreferenceController.class).getPreferenceKey(); @@ -235,7 +234,6 @@ public class SoundSettings extends DashboardFragment { private static List buildPreferenceControllers(Context context, SoundSettings fragment, Lifecycle lifecycle) { final List controllers = new ArrayList<>(); - controllers.add(new ZenModePreferenceController(context, lifecycle, KEY_ZEN_MODE)); // Volumes are added via xml @@ -309,15 +307,6 @@ public class SoundSettings extends DashboardFragment { return buildPreferenceControllers(context, null /* fragment */, null /* lifecycle */); } - - @Override - public List getNonIndexableKeys(Context context) { - List keys = super.getNonIndexableKeys(context); - // Duplicate results - keys.add((new ZenModePreferenceController(context, null, KEY_ZEN_MODE)) - .getPreferenceKey()); - return keys; - } }; // === Work Sound Settings === diff --git a/src/com/android/settings/notification/ZenModePreferenceController.java b/src/com/android/settings/notification/ZenModePreferenceController.java index 0d94029f47c..d70ffe28b8b 100644 --- a/src/com/android/settings/notification/ZenModePreferenceController.java +++ b/src/com/android/settings/notification/ZenModePreferenceController.java @@ -23,38 +23,30 @@ import android.net.Uri; import android.os.Handler; import android.os.UserHandle; import android.provider.Settings; -import androidx.preference.Preference; -import androidx.preference.PreferenceScreen; -import android.util.Slog; -import com.android.settings.core.PreferenceControllerMixin; -import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settings.core.BasePreferenceController; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnPause; import com.android.settingslib.core.lifecycle.events.OnResume; -public class ZenModePreferenceController extends AbstractPreferenceController - implements LifecycleObserver, OnResume, OnPause, PreferenceControllerMixin { +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +public class ZenModePreferenceController extends BasePreferenceController + implements LifecycleObserver, OnResume, OnPause { - private final String mKey; private SettingObserver mSettingObserver; private ZenModeSettings.SummaryBuilder mSummaryBuilder; - public ZenModePreferenceController(Context context, Lifecycle lifecycle, String key) { - super(context); + public ZenModePreferenceController(Context context, String key) { + super(context, key); mSummaryBuilder = new ZenModeSettings.SummaryBuilder(context); - - if (lifecycle != null) { - lifecycle.addObserver(this); - } - mKey = key; } @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); - mSettingObserver = new SettingObserver(screen.findPreference(mKey)); + mSettingObserver = new SettingObserver(screen.findPreference(getPreferenceKey())); } @Override @@ -72,13 +64,8 @@ public class ZenModePreferenceController extends AbstractPreferenceController } @Override - public String getPreferenceKey() { - return mKey; - } - - @Override - public boolean isAvailable() { - return true; + public int getAvailabilityStatus() { + return AVAILABLE_UNSEARCHABLE; } @Override diff --git a/src/com/android/settings/notification/ZenModeStarredContactsPreferenceController.java b/src/com/android/settings/notification/ZenModeStarredContactsPreferenceController.java index 28475b6b5e3..10a7b561353 100644 --- a/src/com/android/settings/notification/ZenModeStarredContactsPreferenceController.java +++ b/src/com/android/settings/notification/ZenModeStarredContactsPreferenceController.java @@ -68,7 +68,10 @@ public class ZenModeStarredContactsPreferenceController extends public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); mPreference = screen.findPreference(KEY); - mPreference.setOnPreferenceClickListener(this); + + if (mPreference != null) { + mPreference.setOnPreferenceClickListener(this); + } } @Override diff --git a/src/com/android/settings/notification/ZenOnboardingActivity.java b/src/com/android/settings/notification/ZenOnboardingActivity.java index 7b50dde157c..99bc1723c1e 100644 --- a/src/com/android/settings/notification/ZenOnboardingActivity.java +++ b/src/com/android/settings/notification/ZenOnboardingActivity.java @@ -157,6 +157,12 @@ public class ZenOnboardingActivity extends Activity { // ZEN_SETTINGS_UPDATED is true for: // - fresh P+ device // - if zen visual effects values were changed by the user in Settings + NotificationManager nm = context.getSystemService(NotificationManager.class); + if (NotificationManager.Policy.areAllVisualEffectsSuppressed( + nm.getNotificationPolicy().suppressedVisualEffects)) { + Settings.Global.putInt(context.getContentResolver(), + Settings.Global.ZEN_SETTINGS_UPDATED, 1); + } return Settings.Global.getInt(context.getContentResolver(), Settings.Global.ZEN_SETTINGS_UPDATED, 0) != 0; } diff --git a/src/com/android/settings/print/PrintSettingsFragment.java b/src/com/android/settings/print/PrintSettingsFragment.java index dca1f580314..bad1e035dcc 100644 --- a/src/com/android/settings/print/PrintSettingsFragment.java +++ b/src/com/android/settings/print/PrintSettingsFragment.java @@ -56,6 +56,7 @@ import com.android.settings.R; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.Indexable; import com.android.settings.utils.ProfileSettingsPreferenceFragment; +import com.android.settings.widget.AppPreference; import com.android.settingslib.search.SearchIndexable; import java.text.DateFormat; @@ -193,7 +194,7 @@ public class PrintSettingsFragment extends ProfileSettingsPreferenceFragment } for (PrintServiceInfo service : services) { - Preference preference = new Preference(context); + AppPreference preference = new AppPreference(context); String title = service.getResolveInfo().loadLabel(pm).toString(); preference.setTitle(title); diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java index db09a378e1b..7eda82b902f 100644 --- a/src/com/android/settings/slices/SettingsSliceProvider.java +++ b/src/com/android/settings/slices/SettingsSliceProvider.java @@ -34,6 +34,7 @@ import android.util.Pair; import com.android.settings.bluetooth.BluetoothSliceBuilder; import com.android.settings.core.BasePreferenceController; +import com.android.settings.flashlight.FlashlightSliceBuilder; import com.android.settings.location.LocationSliceBuilder; import com.android.settings.notification.ZenModeSliceBuilder; import com.android.settings.mobilenetwork.Enhanced4gLteSliceHelper; @@ -160,6 +161,10 @@ public class SettingsSliceProvider extends SliceProvider { } else if (BluetoothSliceBuilder.BLUETOOTH_URI.equals(sliceUri)) { registerIntentToUri(BluetoothSliceBuilder.INTENT_FILTER, sliceUri); return; + } else if (FlashlightSliceBuilder.FLASHLIGHT_URI.equals(sliceUri)) { + registerIntentToUri(FlashlightSliceBuilder.INTENT_FILTER , sliceUri); + mRegisteredUris.add(sliceUri); + return; } // Start warming the slice, we expect someone will want it soon. @@ -191,32 +196,35 @@ public class SettingsSliceProvider extends SliceProvider { Log.e(TAG, "Requested blocked slice with Uri: " + sliceUri); return null; } - // If adding a new Slice, do not directly match Slice URIs. - // Use {@link SlicesDatabaseAccessor}. - if (WifiCallingSliceHelper.WIFI_CALLING_URI.equals(sliceUri)) { - return FeatureFactory.getFactory(getContext()) - .getSlicesFeatureProvider() - .getNewWifiCallingSliceHelper(getContext()) - .createWifiCallingSlice(sliceUri); - } else if (WifiSliceBuilder.WIFI_URI.equals(sliceUri)) { - return WifiSliceBuilder.getSlice(getContext()); - } else if (ZenModeSliceBuilder.ZEN_MODE_URI.equals(sliceUri)) { - return ZenModeSliceBuilder.getSlice(getContext()); - } else if (BluetoothSliceBuilder.BLUETOOTH_URI.equals(sliceUri)) { - return BluetoothSliceBuilder.getSlice(getContext()); - } else if (LocationSliceBuilder.LOCATION_URI.equals(sliceUri)) { - return LocationSliceBuilder.getSlice(getContext()); - } else if (Enhanced4gLteSliceHelper.SLICE_URI.equals(sliceUri)) { - return FeatureFactory.getFactory(getContext()) - .getSlicesFeatureProvider() - .getNewEnhanced4gLteSliceHelper(getContext()) - .createEnhanced4gLteSlice(sliceUri); - } else if (WifiCallingSliceHelper.WIFI_CALLING_PREFERENCE_URI.equals(sliceUri)) { - return FeatureFactory.getFactory(getContext()) - .getSlicesFeatureProvider() - .getNewWifiCallingSliceHelper(getContext()) - .createWifiCallingPreferenceSlice(sliceUri); - } + + // If adding a new Slice, do not directly match Slice URIs. + // Use {@link SlicesDatabaseAccessor}. + if (WifiCallingSliceHelper.WIFI_CALLING_URI.equals(sliceUri)) { + return FeatureFactory.getFactory(getContext()) + .getSlicesFeatureProvider() + .getNewWifiCallingSliceHelper(getContext()) + .createWifiCallingSlice(sliceUri); + } else if (WifiSliceBuilder.WIFI_URI.equals(sliceUri)) { + return WifiSliceBuilder.getSlice(getContext()); + } else if (ZenModeSliceBuilder.ZEN_MODE_URI.equals(sliceUri)) { + return ZenModeSliceBuilder.getSlice(getContext()); + } else if (BluetoothSliceBuilder.BLUETOOTH_URI.equals(sliceUri)) { + return BluetoothSliceBuilder.getSlice(getContext()); + } else if (LocationSliceBuilder.LOCATION_URI.equals(sliceUri)) { + return LocationSliceBuilder.getSlice(getContext()); + } else if (Enhanced4gLteSliceHelper.SLICE_URI.equals(sliceUri)) { + return FeatureFactory.getFactory(getContext()) + .getSlicesFeatureProvider() + .getNewEnhanced4gLteSliceHelper(getContext()) + .createEnhanced4gLteSlice(sliceUri); + } else if (WifiCallingSliceHelper.WIFI_CALLING_PREFERENCE_URI.equals(sliceUri)) { + return FeatureFactory.getFactory(getContext()) + .getSlicesFeatureProvider() + .getNewWifiCallingSliceHelper(getContext()) + .createWifiCallingPreferenceSlice(sliceUri); + } else if (FlashlightSliceBuilder.FLASHLIGHT_URI.equals(sliceUri)) { + return FlashlightSliceBuilder.getSlice(getContext()); + } SliceData cachedSliceData = mSliceWeakDataCache.get(sliceUri); if (cachedSliceData == null) { @@ -381,7 +389,8 @@ public class SettingsSliceProvider extends SliceProvider { private List getSpecialCaseOemUris() { return Arrays.asList( - ZenModeSliceBuilder.ZEN_MODE_URI + ZenModeSliceBuilder.ZEN_MODE_URI, + FlashlightSliceBuilder.FLASHLIGHT_URI ); } diff --git a/src/com/android/settings/slices/SliceBroadcastReceiver.java b/src/com/android/settings/slices/SliceBroadcastReceiver.java index 9f1ef769d7d..95f053844e5 100644 --- a/src/com/android/settings/slices/SliceBroadcastReceiver.java +++ b/src/com/android/settings/slices/SliceBroadcastReceiver.java @@ -17,6 +17,7 @@ package com.android.settings.slices; import static com.android.settings.bluetooth.BluetoothSliceBuilder.ACTION_BLUETOOTH_SLICE_CHANGED; +import static com.android.settings.flashlight.FlashlightSliceBuilder.ACTION_FLASHLIGHT_SLICE_CHANGED; import static com.android.settings.notification.ZenModeSliceBuilder.ACTION_ZEN_MODE_SLICE_CHANGED; import static com.android.settings.slices.SettingsSliceProvider.ACTION_SLIDER_CHANGED; import static com.android.settings.slices.SettingsSliceProvider.ACTION_TOGGLE_CHANGED; @@ -45,6 +46,7 @@ import com.android.settings.bluetooth.BluetoothSliceBuilder; import com.android.settings.core.BasePreferenceController; import com.android.settings.core.SliderPreferenceController; import com.android.settings.core.TogglePreferenceController; +import com.android.settings.flashlight.FlashlightSliceBuilder; import com.android.settings.notification.ZenModeSliceBuilder; import com.android.settings.overlay.FeatureFactory; import com.android.settings.wifi.WifiSliceBuilder; @@ -102,6 +104,9 @@ public class SliceBroadcastReceiver extends BroadcastReceiver { .getNewWifiCallingSliceHelper(context) .handleWifiCallingPreferenceChanged(intent); break; + case ACTION_FLASHLIGHT_SLICE_CHANGED: + FlashlightSliceBuilder.handleUriChange(context, intent); + break; default: final String uriString = intent.getStringExtra(SliceBroadcastRelay.EXTRA_URI); if (!TextUtils.isEmpty(uriString)) { diff --git a/src/com/android/settings/sound/AudioSwitchPreferenceController.java b/src/com/android/settings/sound/AudioSwitchPreferenceController.java index f35fd60ad54..119fadbd4ba 100644 --- a/src/com/android/settings/sound/AudioSwitchPreferenceController.java +++ b/src/com/android/settings/sound/AudioSwitchPreferenceController.java @@ -118,7 +118,6 @@ public abstract class AudioSwitchPreferenceController extends BasePreferenceCont Log.e(TAG, "Bluetooth is not supported on this device"); return; } - mLocalBluetoothManager.setForegroundActivity(mContext); mProfileManager = mLocalBluetoothManager.getProfileManager(); } @@ -171,11 +170,13 @@ public abstract class AudioSwitchPreferenceController extends BasePreferenceCont @Override public void onStart() { + mLocalBluetoothManager.setForegroundActivity(mContext); register(); } @Override public void onStop() { + mLocalBluetoothManager.setForegroundActivity(null); unregister(); } diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java index 52a36928c3a..a33561b3bbe 100644 --- a/src/com/android/settings/users/UserSettings.java +++ b/src/com/android/settings/users/UserSettings.java @@ -42,13 +42,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.ContactsContract; import android.provider.SearchIndexableResource; -import android.provider.Settings.Global; -import androidx.annotation.VisibleForTesting; -import androidx.annotation.WorkerThread; -import androidx.preference.Preference; -import androidx.preference.Preference.OnPreferenceClickListener; -import androidx.preference.PreferenceGroup; -import androidx.preference.PreferenceScreen; import android.util.Log; import android.util.SparseArray; import android.view.Menu; @@ -82,6 +75,13 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; +import androidx.annotation.VisibleForTesting; +import androidx.annotation.WorkerThread; +import androidx.preference.Preference; +import androidx.preference.Preference.OnPreferenceClickListener; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceScreen; + /** * Screen that manages the list of users on the device. * Guest user is an always visible entry, even if the guest is not currently @@ -109,6 +109,8 @@ public class UserSettings extends SettingsPreferenceFragment private static final int MENU_REMOVE_USER = Menu.FIRST; + private static final IntentFilter USER_REMOVED_INTENT_FILTER; + private static final int DIALOG_CONFIRM_REMOVE = 1; private static final int DIALOG_ADD_USER = 2; private static final int DIALOG_SETUP_USER = 3; @@ -134,6 +136,11 @@ public class UserSettings extends SettingsPreferenceFragment private static final String KEY_TITLE = "title"; private static final String KEY_SUMMARY = "summary"; + static { + USER_REMOVED_INTENT_FILTER = new IntentFilter(Intent.ACTION_USER_REMOVED); + USER_REMOVED_INTENT_FILTER.addAction(Intent.ACTION_USER_INFO_CHANGED); + } + private PreferenceGroup mUserListCategory; private UserPreference mMePreference; private RestrictedPreference mAddUser; @@ -195,13 +202,14 @@ public class UserSettings extends SettingsPreferenceFragment public void onCreate(Bundle icicle) { super.onCreate(icicle); addPreferencesFromResource(R.xml.user_settings); - if (Global.getInt(getContext().getContentResolver(), Global.DEVICE_PROVISIONED, 0) == 0) { - getActivity().finish(); + final Activity activity = getActivity(); + if (!Utils.isDeviceProvisioned(getActivity())) { + activity.finish(); return; } - final Context context = getActivity(); + mAddUserWhenLockedPreferenceController = new AddUserWhenLockedPreferenceController( - context, KEY_ADD_USER_WHEN_LOCKED, getLifecycle()); + activity, KEY_ADD_USER_WHEN_LOCKED, getLifecycle()); final PreferenceScreen screen = getPreferenceScreen(); mAddUserWhenLockedPreferenceController.displayPreference(screen); @@ -218,8 +226,8 @@ public class UserSettings extends SettingsPreferenceFragment mEditUserInfoController.onRestoreInstanceState(icicle); } - mUserCaps = UserCapabilities.create(context); - mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); + mUserCaps = UserCapabilities.create(activity); + mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE); if (!mUserCaps.mEnabled) { return; } @@ -248,9 +256,10 @@ public class UserSettings extends SettingsPreferenceFragment } else { mAddUser.setVisible(false); } - final IntentFilter filter = new IntentFilter(Intent.ACTION_USER_REMOVED); - filter.addAction(Intent.ACTION_USER_INFO_CHANGED); - context.registerReceiverAsUser(mUserChangeReceiver, UserHandle.ALL, filter, null, mHandler); + + activity.registerReceiverAsUser( + mUserChangeReceiver, UserHandle.ALL, USER_REMOVED_INTENT_FILTER, null, mHandler); + loadProfile(); updateUserList(); mShouldUpdateUserList = false; @@ -762,9 +771,11 @@ public class UserSettings extends SettingsPreferenceFragment } private void updateUserList() { - if (getActivity() == null) return; - List users = mUserManager.getUsers(true); final Context context = getActivity(); + if (context == null) { + return; + } + final List users = mUserManager.getUsers(true); final boolean voiceCapable = Utils.isVoiceCapable(context); final ArrayList missingIcons = new ArrayList<>(); diff --git a/src/com/android/settings/widget/HotspotApBandSelectionPreference.java b/src/com/android/settings/widget/HotspotApBandSelectionPreference.java deleted file mode 100644 index b249b993856..00000000000 --- a/src/com/android/settings/widget/HotspotApBandSelectionPreference.java +++ /dev/null @@ -1,258 +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.widget; - -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.net.wifi.WifiConfiguration; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; -import androidx.annotation.VisibleForTesting; -import android.util.AttributeSet; -import android.view.View; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.LinearLayout; - -import com.android.settings.R; -import com.android.settingslib.CustomDialogPreference; - -import java.util.ArrayList; - -public class HotspotApBandSelectionPreference extends CustomDialogPreference implements - CompoundButton.OnCheckedChangeListener, DialogInterface.OnShowListener { - private static final int UNSET = Integer.MIN_VALUE; - - @VisibleForTesting - static final String KEY_CHECKED_BANDS = "checked_bands"; - @VisibleForTesting - static final String KEY_HOTSPOT_SUPER_STATE = "hotspot_super_state"; - - @VisibleForTesting - CheckBox mBox2G; - @VisibleForTesting - CheckBox mBox5G; - @VisibleForTesting - ArrayList mRestoredBands; - @VisibleForTesting - boolean mShouldRestore; - - private String[] mBandEntries; - private int mExistingConfigValue = UNSET; - - public HotspotApBandSelectionPreference(Context context) { - super(context); - } - - public HotspotApBandSelectionPreference(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public HotspotApBandSelectionPreference(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public HotspotApBandSelectionPreference(Context context, AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - @Override - protected void onRestoreInstanceState(Parcelable state) { - SavedState myState = (SavedState) state; - - super.onRestoreInstanceState(myState.getSuperState()); - - mShouldRestore = myState.shouldRestore; - if (mShouldRestore) { - mRestoredBands = new ArrayList<>(); - if (myState.enabled2G) { - mRestoredBands.add(WifiConfiguration.AP_BAND_2GHZ); - } - if (myState.enabled5G) { - mRestoredBands.add(WifiConfiguration.AP_BAND_5GHZ); - } - } else { - mRestoredBands = null; - } - updatePositiveButton(); - } - - @Override - protected void onBindDialogView(View view) { - super.onBindDialogView(view); - final Context context = getContext(); - - // Register so we can adjust the buttons if needed once the dialog is available. - setOnShowListener(this); - - mBandEntries = context.getResources().getStringArray(R.array.wifi_ap_band_config_full); - // add a checkbox for every band entry. - addApBandViews((LinearLayout) view); - // try to update the button just in case we already missed the onShow call. - updatePositiveButton(); - // clear any saved state so it doesn't leak across multiple rotations/dialog closings - mRestoredBands = null; - mShouldRestore = false; - } - - @Override - protected Parcelable onSaveInstanceState() { - final Parcelable superState = super.onSaveInstanceState(); - - SavedState myState = new SavedState(superState); - myState.shouldRestore = getDialog() != null; - myState.enabled2G = mBox2G != null && mBox2G.isChecked(); - myState.enabled5G = mBox5G != null && mBox5G.isChecked(); - return myState; - } - - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - if (!(buttonView instanceof CheckBox)) { - return; - } - updatePositiveButton(); - } - - @Override - protected void onClick(DialogInterface dialog, int which) { - // we only want to persist our enabled bands if apply is clicked - if (which == DialogInterface.BUTTON_POSITIVE) { - if (mBox2G.isChecked() || mBox5G.isChecked()) { - int wifiBand = getWifiBand(); - mExistingConfigValue = wifiBand; - callChangeListener(wifiBand); - } - } - } - - /** - * Used to set the band selection for the preference if one already exists - * @param band the band to set it to from {@link WifiConfiguration} - */ - public void setExistingConfigValue(int band) { - mExistingConfigValue = band; - } - - private void addApBandViews(LinearLayout view) { - mBox2G = view.findViewById(R.id.box_2g); - mBox2G.setText(mBandEntries[WifiConfiguration.AP_BAND_2GHZ]); - mBox2G.setChecked(restoreBandIfNeeded(WifiConfiguration.AP_BAND_2GHZ)); - mBox2G.setOnCheckedChangeListener(this); - - mBox5G = view.findViewById(R.id.box_5g); - mBox5G.setText(mBandEntries[WifiConfiguration.AP_BAND_5GHZ]); - mBox5G.setChecked(restoreBandIfNeeded(WifiConfiguration.AP_BAND_5GHZ)); - mBox5G.setOnCheckedChangeListener(this); - } - - private boolean restoreBandIfNeeded(int band) { - // Only use the provided config if we aren't restoring, restore if state available - return (isBandPreviouslySelected(band) && !mShouldRestore) - || (mShouldRestore && mRestoredBands.contains(band)); - } - - private void updatePositiveButton() { - AlertDialog dialog = (AlertDialog) getDialog(); - Button button = dialog == null ? null : dialog.getButton(DialogInterface.BUTTON_POSITIVE); - if (button != null && mBox5G != null && mBox2G != null) { - button.setEnabled(mBox2G.isChecked() || mBox5G.isChecked()); - } - } - - @VisibleForTesting - int getWifiBand() { - final boolean checked_2g = mBox2G.isChecked(); - final boolean checked_5g = mBox5G.isChecked(); - if (checked_2g && checked_5g) { - return WifiConfiguration.AP_BAND_ANY; - } else if (checked_2g && !checked_5g) { - return WifiConfiguration.AP_BAND_2GHZ; - } else if (checked_5g && !checked_2g) { - return WifiConfiguration.AP_BAND_5GHZ; - } else { - throw new IllegalStateException("Wifi Config only supports selecting one or all bands"); - } - } - - private boolean isBandPreviouslySelected(int bandIndex) { - switch(mExistingConfigValue) { - case WifiConfiguration.AP_BAND_ANY: - return true; - case WifiConfiguration.AP_BAND_2GHZ: - return bandIndex == 0; - case WifiConfiguration.AP_BAND_5GHZ: - return bandIndex == 1; - case UNSET: - default: - return false; - } - } - - @Override - public void onShow(DialogInterface dialog) { - updatePositiveButton(); - } - - private static class SavedState extends BaseSavedState { - boolean shouldRestore; - boolean enabled2G; - boolean enabled5G; - - public SavedState(Parcelable source) { - super(source); - } - - private SavedState(Parcel in) { - super(in); - shouldRestore = in.readByte() == 1; - enabled2G = in.readByte() == 1; - enabled5G = in.readByte() == 1; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeByte((byte) (shouldRestore ? 1 : 0)); - dest.writeByte((byte) (enabled2G ? 1: 0)); - dest.writeByte((byte) (enabled5G ? 1 : 0)); - } - - @Override - public String toString() { - return "HotspotApBandSelectionPreference.SavedState{" - + Integer.toHexString(System.identityHashCode(this)) - + " shouldRestore=" + shouldRestore - + " enabled2G=" + enabled2G - + " enabled5G=" + enabled5G + "}"; - } - - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { - public SavedState createFromParcel(Parcel in) { - return new SavedState(in); - } - - public SavedState[] newArray(int size) { - return new SavedState[size]; - } - }; - } -} diff --git a/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceController.java index 8dde24bdb47..b1f171be3d5 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceController.java +++ b/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceController.java @@ -16,36 +16,32 @@ package com.android.settings.wifi.tether; -import static android.net.wifi.WifiConfiguration.AP_BAND_2GHZ; -import static android.net.wifi.WifiConfiguration.AP_BAND_5GHZ; - import android.content.Context; import android.content.res.Resources; -import android.icu.text.ListFormatter; import android.net.wifi.WifiConfiguration; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.ListPreference; import androidx.preference.Preference; import android.util.Log; import com.android.settings.R; -import com.android.settings.widget.HotspotApBandSelectionPreference; public class WifiTetherApBandPreferenceController extends WifiTetherBasePreferenceController { private static final String TAG = "WifiTetherApBandPref"; private static final String PREF_KEY = "wifi_tether_network_ap_band"; - public static final String[] BAND_VALUES = - {String.valueOf(AP_BAND_2GHZ), String.valueOf(AP_BAND_5GHZ)}; - private final String[] mBandEntries; - private final String[] mBandSummaries; + private String[] mBandEntries; + private String[] mBandSummaries; private int mBandIndex; + private boolean isDualMode; public WifiTetherApBandPreferenceController(Context context, OnTetherConfigUpdateListener listener) { super(context, listener); - Resources res = mContext.getResources(); - mBandEntries = res.getStringArray(R.array.wifi_ap_band_config_full); - mBandSummaries = res.getStringArray(R.array.wifi_ap_band_summary_full); + isDualMode = mWifiManager.isDualModeSupported(); + updatePreferenceEntries(); } @Override @@ -55,7 +51,7 @@ public class WifiTetherApBandPreferenceController extends WifiTetherBasePreferen mBandIndex = 0; Log.d(TAG, "Updating band index to 0 because no config"); } else if (is5GhzBandSupported()) { - mBandIndex = config.apBand; + mBandIndex = validateSelection(config.apBand); Log.d(TAG, "Updating band index to " + mBandIndex); } else { config.apBand = 0; @@ -63,21 +59,23 @@ public class WifiTetherApBandPreferenceController extends WifiTetherBasePreferen mBandIndex = config.apBand; Log.d(TAG, "5Ghz not supported, updating band index to " + mBandIndex); } - HotspotApBandSelectionPreference preference = - (HotspotApBandSelectionPreference) mPreference; + ListPreference preference = + (ListPreference) mPreference; + preference.setEntries(mBandSummaries); + preference.setEntryValues(mBandEntries); if (!is5GhzBandSupported()) { preference.setEnabled(false); preference.setSummary(R.string.wifi_ap_choose_2G); } else { - preference.setExistingConfigValue(config.apBand); + preference.setValue(Integer.toString(config.apBand)); preference.setSummary(getConfigSummary()); } } String getConfigSummary() { if (mBandIndex == WifiConfiguration.AP_BAND_ANY) { - return ListFormatter.getInstance().format((Object[]) mBandSummaries); + return mContext.getString(R.string.wifi_ap_prefer_5G); } return mBandSummaries[mBandIndex]; } @@ -89,13 +87,46 @@ public class WifiTetherApBandPreferenceController extends WifiTetherBasePreferen @Override public boolean onPreferenceChange(Preference preference, Object newValue) { - mBandIndex = (Integer) newValue; + mBandIndex = validateSelection(Integer.parseInt((String) newValue)); Log.d(TAG, "Band preference changed, updating band index to " + mBandIndex); preference.setSummary(getConfigSummary()); mListener.onTetherConfigUpdated(); return true; } + private int validateSelection(int band) { + // Reset the band to 2.4 GHz if we get a weird config back to avoid a crash. + final boolean isDualMode = mWifiManager.isDualModeSupported(); + + // unsupported states: + // 1: no dual mode means we can't have AP_BAND_ANY - default to 5GHZ + // 2: no 5 GHZ support means we can't have AP_BAND_5GHZ - default to 2GHZ + // 3: With Dual mode support we can't have AP_BAND_5GHZ - default to ANY + if (!isDualMode && WifiConfiguration.AP_BAND_ANY == band) { + return WifiConfiguration.AP_BAND_5GHZ; + } else if (!mWifiManager.is5GHzBandSupported() && WifiConfiguration.AP_BAND_5GHZ == band) { + return WifiConfiguration.AP_BAND_2GHZ; + } else if (isDualMode && WifiConfiguration.AP_BAND_5GHZ == band) { + return WifiConfiguration.AP_BAND_ANY; + } + + return band; + } + + @VisibleForTesting + void updatePreferenceEntries() { + Resources res = mContext.getResources(); + int entriesRes = R.array.wifi_ap_band_config_full; + int summariesRes = R.array.wifi_ap_band_summary_full; + // change the list options if this is a dual mode device + if (isDualMode) { + entriesRes = R.array.wifi_ap_band_dual_mode; + summariesRes = R.array.wifi_ap_band_dual_mode_summary; + } + mBandEntries = res.getStringArray(entriesRes); + mBandSummaries = res.getStringArray(summariesRes); + } + private boolean is5GhzBandSupported() { final String countryCode = mWifiManager.getCountryCode(); if (!mWifiManager.isDualBandSupported() || countryCode == null) { diff --git a/tests/robotests/src/com/android/settings/accounts/AccountSyncSettingsTest.java b/tests/robotests/src/com/android/settings/accounts/AccountSyncSettingsTest.java new file mode 100644 index 00000000000..da361852ab1 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accounts/AccountSyncSettingsTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.accounts; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.accounts.Account; +import android.app.Activity; +import android.content.Context; +import android.os.UserHandle; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.shadow.ShadowContentResolver; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.util.ReflectionHelpers; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(shadows = {ShadowContentResolver.class}) +public class AccountSyncSettingsTest { + + @After + public void tearDown() { + ShadowContentResolver.reset(); + } + + @Test + public void onPreferenceTreeClick_nullAuthority_shouldNotCrash() { + final Context context = RuntimeEnvironment.application; + final AccountSyncSettings settings = spy(new AccountSyncSettings()); + when(settings.getActivity()).thenReturn(mock(Activity.class)); + final SyncStateSwitchPreference preference = new SyncStateSwitchPreference(context, + new Account("acct1", "type1"), "" /* authority */, "testPackage", 1 /* uid */); + preference.setOneTimeSyncMode(false); + ReflectionHelpers.setField(settings, "mUserHandle", UserHandle.CURRENT); + + settings.onPreferenceTreeClick(preference); + // no crash + } +} diff --git a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultBrowserPickerTest.java b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultBrowserPickerTest.java index f7a21d8ed7c..bdf249d1f5e 100644 --- a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultBrowserPickerTest.java +++ b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultBrowserPickerTest.java @@ -16,6 +16,8 @@ package com.android.settings.applications.defaultapps; +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.verify; @@ -23,10 +25,20 @@ import static org.mockito.Mockito.when; import android.app.Activity; import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; import android.os.UserManager; +import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.applications.DefaultAppInfo; + +import java.util.ArrayList; +import java.util.List; import org.junit.Before; import org.junit.Test; @@ -53,6 +65,7 @@ public class DefaultBrowserPickerTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); + FakeFeatureFactory.setupForTest(); when(mActivity.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); mPicker = new DefaultBrowserPicker(); @@ -72,4 +85,39 @@ public class DefaultBrowserPickerTest { mPicker.getDefaultKey(); verify(mPackageManager).getDefaultBrowserPackageNameAsUser(anyInt()); } + + @Test + public void getCandidates_shouldNotIncludeDuplicatePackageName() throws NameNotFoundException { + final List resolveInfos = new ArrayList<>(); + final String PACKAGE_ONE = "com.first.package"; + final String PACKAGE_TWO = "com.second.package"; + resolveInfos.add(createResolveInfo(PACKAGE_ONE)); + resolveInfos.add(createResolveInfo(PACKAGE_TWO)); + resolveInfos.add(createResolveInfo(PACKAGE_ONE)); + resolveInfos.add(createResolveInfo(PACKAGE_TWO)); + when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt())) + .thenReturn(resolveInfos); + when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_ONE), anyInt(), anyInt())) + .thenReturn(createApplicationInfo(PACKAGE_ONE)); + when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_TWO), anyInt(), anyInt())) + .thenReturn(createApplicationInfo(PACKAGE_TWO)); + + final List defaultBrowserInfo = mPicker.getCandidates(); + + assertThat(defaultBrowserInfo.size()).isEqualTo(2); + } + + private ResolveInfo createResolveInfo(String packageName) { + final ResolveInfo info = new ResolveInfo(); + info.handleAllWebDataURI = true; + info.activityInfo = new ActivityInfo(); + info.activityInfo.packageName = packageName; + return info; + } + + private ApplicationInfo createApplicationInfo(String packageName) { + final ApplicationInfo info = new ApplicationInfo(); + info.packageName = packageName; + return info; + } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java index fd2ff0d8975..6e69e67ba0c 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java @@ -20,6 +20,7 @@ import android.content.pm.PackageManager; import androidx.preference.Preference; import com.android.settings.bluetooth.BluetoothDeviceUpdater; +import com.android.settings.connecteddevice.dock.DockUpdater; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.testutils.SettingsRobolectricTestRunner; @@ -45,6 +46,8 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { @Mock private BluetoothDeviceUpdater mBluetoothDeviceUpdater; @Mock + private DockUpdater mDockUpdater; + @Mock private PackageManager mPackageManager; private Context mContext; @@ -60,6 +63,7 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { mPreConnectedDeviceController = new PreviouslyConnectedDevicePreferenceController(mContext, KEY); mPreConnectedDeviceController.setBluetoothDeviceUpdater(mBluetoothDeviceUpdater); + mPreConnectedDeviceController.setSavedDockUpdater(mDockUpdater); mPreference = new Preference(mContext); mPreConnectedDeviceController.setPreference(mPreference); @@ -70,10 +74,12 @@ public class PreviouslyConnectedDevicePreferenceControllerTest { // register the callback in onStart() mPreConnectedDeviceController.onStart(); verify(mBluetoothDeviceUpdater).registerCallback(); + verify(mDockUpdater).registerCallback(); // unregister the callback in onStop() mPreConnectedDeviceController.onStop(); verify(mBluetoothDeviceUpdater).unregisterCallback(); + verify(mDockUpdater).unregisterCallback(); } @Test diff --git a/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollFindSensorTest.java b/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollFindSensorTest.java index e83e93d9bc9..1fe09310e76 100644 --- a/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollFindSensorTest.java +++ b/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollFindSensorTest.java @@ -22,6 +22,7 @@ import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.verify; import static org.robolectric.RuntimeEnvironment.application; +import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.hardware.fingerprint.FingerprintManager; @@ -151,4 +152,12 @@ public class FingerprintEnrollFindSensorTest { return callbackCaptor.getValue(); } + + @Test + public void onActivityResult_withNullIntentShouldNotCrash() { + // this should not crash + mActivity.onActivityResult(FingerprintEnrollFindSensor.CONFIRM_REQUEST, Activity.RESULT_OK, + null); + assertThat(Shadows.shadowOf(mActivity).getResultCode()).isEqualTo(Activity.RESULT_CANCELED); + } } diff --git a/tests/robotests/src/com/android/settings/flashlight/FlashlightSliceBuilderTest.java b/tests/robotests/src/com/android/settings/flashlight/FlashlightSliceBuilderTest.java new file mode 100644 index 00000000000..5862ab3d286 --- /dev/null +++ b/tests/robotests/src/com/android/settings/flashlight/FlashlightSliceBuilderTest.java @@ -0,0 +1,81 @@ +/* + * 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.flashlight; + +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 android.content.Context; + +import com.android.settings.R; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.SliceTester; + +import android.content.res.Resources; +import android.provider.Settings; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; + +import java.util.List; + +import androidx.slice.Slice; +import androidx.slice.SliceItem; +import androidx.slice.SliceMetadata; +import androidx.slice.SliceProvider; +import androidx.slice.core.SliceAction; +import androidx.slice.widget.SliceLiveData; + + +@RunWith(SettingsRobolectricTestRunner.class) +public class FlashlightSliceBuilderTest { + + private Context mContext; + + @Before + public void setUp() { + mContext = spy(RuntimeEnvironment.application); + + // Prevent crash in SliceMetadata. + Resources resources = spy(mContext.getResources()); + doReturn(60).when(resources).getDimensionPixelSize(anyInt()); + doReturn(resources).when(mContext).getResources(); + + // Set-up specs for SliceMetadata. + SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); + } + + @Test + public void getFlashlightSlice_correctData() { + Settings.Secure.putInt( + mContext.getContentResolver(), Settings.Secure.FLASHLIGHT_AVAILABLE, 1); + final Slice slice = FlashlightSliceBuilder.getSlice(mContext); + final SliceMetadata metadata = SliceMetadata.from(mContext, slice); + + final List toggles = metadata.getToggles(); + assertThat(toggles).hasSize(1); + + final List sliceItems = slice.getItems(); + SliceTester.assertTitle(sliceItems, mContext.getString(R.string.power_flashlight)); + } +} diff --git a/tests/robotests/src/com/android/settings/gestures/PreventRingingParentPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/PreventRingingParentPreferenceControllerTest.java new file mode 100644 index 00000000000..33a00fac303 --- /dev/null +++ b/tests/robotests/src/com/android/settings/gestures/PreventRingingParentPreferenceControllerTest.java @@ -0,0 +1,69 @@ +/* + * 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.gestures; + +import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE; +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.content.res.Resources; + +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 PreventRingingParentPreferenceControllerTest { + + @Mock + private Resources mResources; + + private Context mContext; + private PreventRingingParentPreferenceController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + when(mContext.getResources()).thenReturn(mResources); + + mController = new PreventRingingParentPreferenceController(mContext, "test_key"); + } + + @Test + public void testIsAvailable_configIsTrue_shouldAvailableUnSearchable() { + when(mResources.getBoolean( + com.android.internal.R.bool.config_volumeHushGestureEnabled)).thenReturn(true); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE); + } + + @Test + public void testIsAvailable_configIsFalse_shouldReturnFalse() { + when(mResources.getBoolean( + com.android.internal.R.bool.config_volumeHushGestureEnabled)).thenReturn(false); + + assertThat(mController.isAvailable()).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/notification/ZenModePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModePreferenceControllerTest.java index 3ea4471fadc..ece25e2661b 100644 --- a/tests/robotests/src/com/android/settings/notification/ZenModePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/ZenModePreferenceControllerTest.java @@ -16,6 +16,7 @@ package com.android.settings.notification; +import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doReturn; @@ -27,7 +28,6 @@ import static org.mockito.Mockito.when; import android.app.NotificationManager; import android.app.NotificationManager.Policy; import android.content.Context; -import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.testutils.SettingsRobolectricTestRunner; @@ -40,6 +40,8 @@ import org.mockito.MockitoAnnotations; import org.robolectric.shadows.ShadowApplication; import org.robolectric.util.ReflectionHelpers; +import androidx.preference.Preference; + @RunWith(SettingsRobolectricTestRunner.class) public class ZenModePreferenceControllerTest { @@ -61,7 +63,7 @@ public class ZenModePreferenceControllerTest { ShadowApplication shadowApplication = ShadowApplication.getInstance(); shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager); mContext = shadowApplication.getApplicationContext(); - mController = new ZenModePreferenceController(mContext, null, KEY_ZEN_MODE); + mController = new ZenModePreferenceController(mContext, KEY_ZEN_MODE); when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy); mSummaryBuilder = spy(new ZenModeSettings.SummaryBuilder(mContext)); ReflectionHelpers.setField(mController, "mSummaryBuilder", mSummaryBuilder); @@ -69,8 +71,8 @@ public class ZenModePreferenceControllerTest { } @Test - public void isAlwaysAvailable() { - assertThat(mController.isAvailable()).isTrue(); + public void isAvailable_unsearchable() { + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE); } @Test diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeStarredContactsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeStarredContactsPreferenceControllerTest.java index 064c0911319..ad8e0de7193 100644 --- a/tests/robotests/src/com/android/settings/notification/ZenModeStarredContactsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/ZenModeStarredContactsPreferenceControllerTest.java @@ -190,4 +190,13 @@ public class ZenModeStarredContactsPreferenceControllerTest { assertThat(contacts.get(i)).isNotNull(); } } + + @Test + public void nullPreference_displayPreference() { + when(mPreferenceScreen.findPreference(mMessagesController.getPreferenceKey())) + .thenReturn(null); + + // should not throw a null pointer + mMessagesController.displayPreference(mPreferenceScreen); + } } diff --git a/tests/robotests/src/com/android/settings/notification/ZenOnboardingActivityTest.java b/tests/robotests/src/com/android/settings/notification/ZenOnboardingActivityTest.java index e09dc0d2b8c..3ca9fdac779 100644 --- a/tests/robotests/src/com/android/settings/notification/ZenOnboardingActivityTest.java +++ b/tests/robotests/src/com/android/settings/notification/ZenOnboardingActivityTest.java @@ -51,6 +51,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; import org.robolectric.RuntimeEnvironment; +import org.robolectric.shadows.ShadowApplication; @RunWith(SettingsRobolectricTestRunner.class) public class ZenOnboardingActivityTest { @@ -68,6 +69,8 @@ public class ZenOnboardingActivityTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); + ShadowApplication shadowApplication = ShadowApplication.getInstance(); + shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm); mActivity = Robolectric.buildActivity(ZenOnboardingActivity.class) .create() @@ -124,6 +127,9 @@ public class ZenOnboardingActivityTest { @Test public void isSuggestionComplete_zenUpdated() { + Policy policy = new Policy(0, 0, 0, 0); + when(mNm.getNotificationPolicy()).thenReturn(policy); + setZenUpdated(true); setShowSettingsSuggestion(false); setWithinTimeThreshold(true); @@ -132,6 +138,9 @@ public class ZenOnboardingActivityTest { @Test public void isSuggestionComplete_withinTimeThreshold() { + Policy policy = new Policy(0, 0, 0, 0); + when(mNm.getNotificationPolicy()).thenReturn(policy); + setZenUpdated(false); setShowSettingsSuggestion(false); setWithinTimeThreshold(true); @@ -140,6 +149,9 @@ public class ZenOnboardingActivityTest { @Test public void isSuggestionComplete_showSettingsSuggestionTrue() { + Policy policy = new Policy(0, 0, 0, 0); + when(mNm.getNotificationPolicy()).thenReturn(policy); + setZenUpdated(false); setShowSettingsSuggestion(true); setWithinTimeThreshold(false); @@ -148,17 +160,33 @@ public class ZenOnboardingActivityTest { @Test public void isSuggestionComplete_showSettingsSuggestionFalse_notWithinTimeThreshold() { + Policy policy = new Policy(0, 0, 0, 0); + when(mNm.getNotificationPolicy()).thenReturn(policy); + setZenUpdated(false); setShowSettingsSuggestion(false); setWithinTimeThreshold(false); assertThat(isSuggestionComplete(mContext)).isTrue(); } + + @Test + public void isSuggestionComplete_visualEffectsUpdated() { + // all values suppressed + Policy policy = new Policy(0, 0, 0, 511); + when(mNm.getNotificationPolicy()).thenReturn(policy); + + setZenUpdated(false); + setShowSettingsSuggestion(true); + setWithinTimeThreshold(true); + assertThat(isSuggestionComplete(mContext)).isTrue(); + assertThat(Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.ZEN_SETTINGS_UPDATED, -1)).isEqualTo(1); + } + + private void setZenUpdated(boolean updated) { - int zenUpdated = 0; - if (updated) { - zenUpdated = 1; - } + int zenUpdated = updated ? 1 : 0; Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.ZEN_SETTINGS_UPDATED, zenUpdated); diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java index 6b1262a1e2c..c72e9f6c542 100644 --- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java +++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java @@ -33,10 +33,12 @@ import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import android.os.StrictMode; +import android.provider.Settings; import android.provider.SettingsSlicesContract; import android.util.ArraySet; import com.android.settings.bluetooth.BluetoothSliceBuilder; +import com.android.settings.flashlight.FlashlightSliceBuilder; import com.android.settings.location.LocationSliceBuilder; import com.android.settings.notification.ZenModeSliceBuilder; import com.android.settings.testutils.DatabaseTestUtils; @@ -95,7 +97,8 @@ public class SettingsSliceProviderTest { ); private static final List SPECIAL_CASE_OEM_URIS = Arrays.asList( - ZenModeSliceBuilder.ZEN_MODE_URI + ZenModeSliceBuilder.ZEN_MODE_URI, + FlashlightSliceBuilder.FLASHLIGHT_URI ); @Before @@ -444,6 +447,16 @@ public class SettingsSliceProviderTest { assertThat(wifiSlice.getUri()).isEqualTo(WifiSliceBuilder.WIFI_URI); } + @Test + public void bindSlice_flashlightSlice_returnsFlashlightSlice() { + Settings.Secure.putInt( + mContext.getContentResolver(), Settings.Secure.FLASHLIGHT_AVAILABLE, 1); + + final Slice flashlightSlice = mProvider.onBindSlice(FlashlightSliceBuilder.FLASHLIGHT_URI); + + assertThat(flashlightSlice.getUri()).isEqualTo(FlashlightSliceBuilder.FLASHLIGHT_URI); + } + @Test public void onSlicePinned_noIntentRegistered_specialCaseUri_doesNotCrash() { final Uri uri = new Uri.Builder() diff --git a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java index 56a5aa709fe..748aa0ca302 100644 --- a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java @@ -230,6 +230,7 @@ public class AudioOutputSwitchPreferenceControllerTest { verify(mLocalBluetoothManager.getEventManager()).registerCallback( any(BluetoothCallback.class)); verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class)); + verify(mLocalBluetoothManager).setForegroundActivity(mContext); } @Test @@ -240,6 +241,7 @@ public class AudioOutputSwitchPreferenceControllerTest { verify(mLocalBluetoothManager.getEventManager()).unregisterCallback( any(BluetoothCallback.class)); verify(mContext).unregisterReceiver(any(BroadcastReceiver.class)); + verify(mLocalBluetoothManager).setForegroundActivity(null); } @Test diff --git a/tests/robotests/src/com/android/settings/testutils/SettingsRobolectricTestRunner.java b/tests/robotests/src/com/android/settings/testutils/SettingsRobolectricTestRunner.java index f071f173633..0b29af58bad 100644 --- a/tests/robotests/src/com/android/settings/testutils/SettingsRobolectricTestRunner.java +++ b/tests/robotests/src/com/android/settings/testutils/SettingsRobolectricTestRunner.java @@ -84,9 +84,9 @@ public class SettingsRobolectricTestRunner extends RobolectricTestRunner { paths.add(new ResourcePath(null, Fs.fromURL(new URL("file:frameworks/opt/setupwizard/library/recyclerview/res")), null)); paths.add(new ResourcePath(null, - Fs.fromURL(new URL("file:frameworks/support/v7/appcompat/res")), null)); + Fs.fromURL(new URL("file:frameworks/support/appcompat/res")), null)); paths.add(new ResourcePath(null, - Fs.fromURL(new URL("file:frameworks/support/v7/cardview/res")), null)); + Fs.fromURL(new URL("file:frameworks/support/cardview/res")), null)); } catch (MalformedURLException e) { throw new RuntimeException("SettingsRobolectricTestRunner failure", e); } diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowContentResolver.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowContentResolver.java index 50c0330828a..928779b5c05 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowContentResolver.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowContentResolver.java @@ -24,6 +24,7 @@ import android.database.Cursor; import android.database.MatrixCursor; import android.net.Uri; import android.provider.SearchIndexablesContract; +import android.text.TextUtils; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.annotation.Resetter; @@ -66,6 +67,14 @@ public class ShadowContentResolver { return sSyncAutomatically.containsKey(authority) ? sSyncAutomatically.get(authority) : true; } + @Implementation + public static void setSyncAutomaticallyAsUser(Account account, String authority, boolean sync, + int userId) { + if (TextUtils.isEmpty(authority)) { + throw new IllegalArgumentException("Authority must be non-empty"); + } + } + @Implementation public static boolean getMasterSyncAutomaticallyAsUser(int userId) { return sMasterSyncAutomatically.containsKey(userId) diff --git a/tests/robotests/src/com/android/settings/widget/HotspotApBandSelectionPreferenceTest.java b/tests/robotests/src/com/android/settings/widget/HotspotApBandSelectionPreferenceTest.java deleted file mode 100644 index 0ffda3baa96..00000000000 --- a/tests/robotests/src/com/android/settings/widget/HotspotApBandSelectionPreferenceTest.java +++ /dev/null @@ -1,158 +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.widget; - -import static com.android.settingslib.CustomDialogPreference.CustomPreferenceDialogFragment; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.AlertDialog; -import android.content.Context; -import android.net.wifi.WifiConfiguration; -import android.os.Bundle; -import android.os.Parcelable; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.LinearLayout; - -import com.android.settings.R; -import com.android.settings.testutils.SettingsRobolectricTestRunner; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.util.ReflectionHelpers; - -import java.util.List; - -@RunWith(SettingsRobolectricTestRunner.class) -public class HotspotApBandSelectionPreferenceTest { - private HotspotApBandSelectionPreference mPreference; - private Context mContext; - private Button mSaveButton; - private View mLayout; - - @Before - public void setUp() { - mContext = RuntimeEnvironment.application; - mSaveButton = spy(new Button(mContext)); - - final CustomPreferenceDialogFragment fragment = mock(CustomPreferenceDialogFragment.class); - final AlertDialog dialog = mock(AlertDialog.class); - when(fragment.getDialog()).thenReturn(dialog); - when(dialog.getButton(anyInt())).thenReturn(mSaveButton); - - mPreference = new HotspotApBandSelectionPreference(mContext); - ReflectionHelpers.setField(mPreference, "mFragment", fragment); - - final LayoutInflater inflater = LayoutInflater.from(mContext); - mLayout = inflater.inflate(R.layout.hotspot_ap_band_selection_dialog, - new LinearLayout(mContext), false); - } - - @Test - public void getWifiBand_updatesBandPresetConfigProvided() { - mPreference.setExistingConfigValue(WifiConfiguration.AP_BAND_ANY); - mPreference.onBindDialogView(mLayout); - - // check that the boxes are set correctly when a pre-existing config is set - assertThat(mPreference.getWifiBand()).isEqualTo(WifiConfiguration.AP_BAND_ANY); - } - - @Test - public void getWifiBand_updatesBandWhenBoxesToggled() { - mPreference.setExistingConfigValue(WifiConfiguration.AP_BAND_ANY); - mPreference.onBindDialogView(mLayout); - - assertThat(mPreference.getWifiBand()).isEqualTo(WifiConfiguration.AP_BAND_ANY); - - // make sure we have the expected box then toggle it - mPreference.mBox2G.setChecked(false); - - // check that band is updated - assertThat(mPreference.getWifiBand()).isEqualTo(WifiConfiguration.AP_BAND_5GHZ); - } - - @Test - public void onSaveInstanceState_skipWhenDialogGone() { - mPreference.setExistingConfigValue(WifiConfiguration.AP_BAND_2GHZ); - mPreference.onBindDialogView(mLayout); - // remove the fragment to make the dialog unavailable - ReflectionHelpers.setField(mPreference, "mFragment", null); - - mPreference.setExistingConfigValue(WifiConfiguration.AP_BAND_ANY); - mPreference.onBindDialogView(mLayout); - - // state should only be saved when the dialog is available - Parcelable parcelable = mPreference.onSaveInstanceState(); - mPreference.onRestoreInstanceState(parcelable); - assertThat(mPreference.mShouldRestore).isFalse(); - } - - @Test - public void onSaveInstanceState_doesNotCrashWhenViewGone() { - mPreference.setExistingConfigValue(WifiConfiguration.AP_BAND_2GHZ); - mPreference.onBindDialogView(mLayout); - // When the device dozes the view and dialog can become null - mPreference.mBox5G = null; - mPreference.mBox2G = null; - ReflectionHelpers.setField(mPreference, "mFragment", null); - - // make sure it does not crash and state is not restored - Parcelable parcelable = mPreference.onSaveInstanceState(); - mPreference.onRestoreInstanceState(parcelable); - assertThat(mPreference.mShouldRestore).isFalse(); - } - - @Test - public void onSaveInstanceState_presentWhenDialogPresent() { - mPreference.setExistingConfigValue(WifiConfiguration.AP_BAND_2GHZ); - mPreference.onBindDialogView(mLayout); - - Parcelable parcelable = mPreference.onSaveInstanceState(); - mPreference.onRestoreInstanceState(parcelable); - assertThat(mPreference.mShouldRestore).isTrue(); - } - - @Test - public void positiveButton_updatedCorrectly() { - mPreference.setExistingConfigValue(WifiConfiguration.AP_BAND_ANY); - mPreference.onBindDialogView(mLayout); - - // button is enabled whole time so far since we have a pre-existing selection - verify(mSaveButton, never()).setEnabled(false); - - // clear all boxes and make sure it stays enabled until empty - mPreference.mBox2G.setChecked(false); - mPreference.mBox5G.setChecked(false); - - // button should be disabled now - verify(mSaveButton, times(1)).setEnabled(false); - } -} diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceControllerTest.java index 4b81b345001..4b2aa1c6418 100644 --- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherApBandPreferenceControllerTest.java @@ -18,6 +18,7 @@ package com.android.settings.wifi.tether; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -26,16 +27,16 @@ import android.content.Context; import android.net.ConnectivityManager; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; + +import androidx.preference.ListPreference; import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.testutils.SettingsRobolectricTestRunner; -import com.android.settings.widget.HotspotApBandSelectionPreference; 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; @@ -43,8 +44,9 @@ import org.robolectric.RuntimeEnvironment; @RunWith(SettingsRobolectricTestRunner.class) public class WifiTetherApBandPreferenceControllerTest { - private static final String ALL_BANDS = "2.4 GHz and 5.0 GHz"; - @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private static final String ALL_BANDS = "5.0 GHz Band preferred"; + private static final String TWO_GHZ_STRING = "2.4 GHz Band"; + private static final String FIVE_GHZ_STRING = "5.0 GHz Band"; private Context mContext; @Mock private ConnectivityManager mConnectivityManager; @@ -56,12 +58,13 @@ public class WifiTetherApBandPreferenceControllerTest { private PreferenceScreen mScreen; private WifiTetherApBandPreferenceController mController; - private HotspotApBandSelectionPreference mPreference; + private ListPreference mPreference; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mPreference = new HotspotApBandSelectionPreference(RuntimeEnvironment.application); + mContext = spy(RuntimeEnvironment.application); + mPreference = new ListPreference(RuntimeEnvironment.application); when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager); when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)) .thenReturn(mConnectivityManager); @@ -71,6 +74,7 @@ public class WifiTetherApBandPreferenceControllerTest { WifiConfiguration config = new WifiConfiguration(); config.apBand = WifiConfiguration.AP_BAND_ANY; when(mWifiManager.getWifiApConfiguration()).thenReturn(new WifiConfiguration()); + when(mWifiManager.isDualModeSupported()).thenReturn(false); mController = new WifiTetherApBandPreferenceController(mContext, mListener); } @@ -79,9 +83,10 @@ public class WifiTetherApBandPreferenceControllerTest { public void display_5GhzSupported_shouldDisplayFullList() { when(mWifiManager.getCountryCode()).thenReturn("US"); when(mWifiManager.isDualBandSupported()).thenReturn(true); + when(mWifiManager.isDualModeSupported()).thenReturn(true); mController.displayPreference(mScreen); - mController.onPreferenceChange(mPreference, -1); + mController.onPreferenceChange(mPreference, "-1"); assertThat(mPreference.getSummary()).isEqualTo(ALL_BANDS); } @@ -110,24 +115,54 @@ public class WifiTetherApBandPreferenceControllerTest { } @Test - public void changePreference_shouldUpdateValue() { + public void changePreference_noDualModeWith5G_shouldUpdateValue() { when(mWifiManager.is5GHzBandSupported()).thenReturn(true); mController.displayPreference(mScreen); + // -1 is WifiConfiguration.AP_BAND_ANY, for 'Auto' option. This should be prevented from + // being set since it is invalid for this configuration + mController.onPreferenceChange(mPreference, "-1"); + assertThat(mController.getBandIndex()).isEqualTo(1); + assertThat(mPreference.getSummary()).isEqualTo(FIVE_GHZ_STRING); + verify(mListener, times(1)).onTetherConfigUpdated(); + + // set to 5 Ghz + mController.onPreferenceChange(mPreference, "1"); + assertThat(mController.getBandIndex()).isEqualTo(1); + assertThat(mPreference.getSummary()).isEqualTo(FIVE_GHZ_STRING); + verify(mListener, times(2)).onTetherConfigUpdated(); + + // set to 2 Ghz + mController.onPreferenceChange(mPreference, "0"); + assertThat(mController.getBandIndex()).isEqualTo(0); + assertThat(mPreference.getSummary()).isEqualTo(TWO_GHZ_STRING); + verify(mListener, times(3)).onTetherConfigUpdated(); + } + + @Test + public void changePreference_dualModeWith5G_shouldUpdateValue() { + when(mWifiManager.is5GHzBandSupported()).thenReturn(true); + when(mWifiManager.isDualModeSupported()).thenReturn(true); + + mController.displayPreference(mScreen); + // -1 is WifiConfiguration.AP_BAND_ANY, for 'Auto' option. - mController.onPreferenceChange(mPreference, -1); + mController.onPreferenceChange(mPreference, "-1"); assertThat(mController.getBandIndex()).isEqualTo(-1); assertThat(mPreference.getSummary()).isEqualTo(ALL_BANDS); + verify(mListener, times(1)).onTetherConfigUpdated(); - mController.onPreferenceChange(mPreference, 1); - assertThat(mController.getBandIndex()).isEqualTo(1); - assertThat(mPreference.getSummary()).isEqualTo("5.0 GHz"); + // should revert to the default for 5 Ghz only since this is not supported with this config + mController.onPreferenceChange(mPreference, "1"); + assertThat(mController.getBandIndex()).isEqualTo(-1); + assertThat(mPreference.getSummary()).isEqualTo(ALL_BANDS); + verify(mListener, times(2)).onTetherConfigUpdated(); - mController.onPreferenceChange(mPreference, 0); + // set to 2 Ghz + mController.onPreferenceChange(mPreference, "0"); assertThat(mController.getBandIndex()).isEqualTo(0); - assertThat(mPreference.getSummary()).isEqualTo("2.4 GHz"); - + assertThat(mPreference.getSummary()).isEqualTo(TWO_GHZ_STRING); verify(mListener, times(3)).onTetherConfigUpdated(); } @@ -136,7 +171,7 @@ public class WifiTetherApBandPreferenceControllerTest { // Set controller band index to 1 and verify is set. when(mWifiManager.is5GHzBandSupported()).thenReturn(true); mController.displayPreference(mScreen); - mController.onPreferenceChange(mPreference, 1); + mController.onPreferenceChange(mPreference, "1"); assertThat(mController.getBandIndex()).isEqualTo(1); // Disable 5Ghz band