diff --git a/res/values/strings.xml b/res/values/strings.xml index d8c0d377b1e..cbb5e3be3ad 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9458,6 +9458,11 @@ Contact search Allow contact searches by your organization to identify callers and contacts + + Cross-profile calendar + + Show work events on personal calendar + @@ -10245,15 +10250,13 @@ Scan again - - + + %1$d device connected %1$d devices connected - - Bluetooth Devices - - No connected devices + + No Bluetooth devices Settings Panel diff --git a/res/xml/managed_profile_settings.xml b/res/xml/managed_profile_settings.xml index ee1e4fadec3..bd44cc1a307 100644 --- a/res/xml/managed_profile_settings.xml +++ b/res/xml/managed_profile_settings.xml @@ -32,4 +32,11 @@ settings:useAdditionalSummary="true" settings:controller="com.android.settings.accounts.ContactSearchPreferenceController"/> + + \ No newline at end of file diff --git a/src/com/android/settings/CryptKeeper.java b/src/com/android/settings/CryptKeeper.java index ca54b07dcd5..05054b4a55d 100644 --- a/src/com/android/settings/CryptKeeper.java +++ b/src/com/android/settings/CryptKeeper.java @@ -772,9 +772,10 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList if (imeSwitcher != null && hasMultipleEnabledIMEsOrSubtypes(imm, false)) { imeSwitcher.setVisibility(View.VISIBLE); imeSwitcher.setOnClickListener(new OnClickListener() { - @Override + @Override public void onClick(View v) { - imm.showInputMethodPicker(false /* showAuxiliarySubtypes */); + imm.showInputMethodPickerFromSystem(false /* showAuxiliarySubtypes */, + v.getDisplay().getDisplayId()); } }); } diff --git a/src/com/android/settings/accounts/CrossProfileCalendarPreferenceController.java b/src/com/android/settings/accounts/CrossProfileCalendarPreferenceController.java new file mode 100644 index 00000000000..38c95dcc7ad --- /dev/null +++ b/src/com/android/settings/accounts/CrossProfileCalendarPreferenceController.java @@ -0,0 +1,79 @@ +/* + * 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 android.provider.Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED; + +import android.content.Context; +import android.os.UserHandle; +import android.provider.Settings; + +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.TogglePreferenceController; +import com.android.settings.slices.SliceData; +import com.android.settingslib.RestrictedLockUtils; +import com.android.settingslib.RestrictedLockUtilsInternal; +import com.android.settingslib.RestrictedSwitchPreference; + +import androidx.preference.Preference; + +public class CrossProfileCalendarPreferenceController extends TogglePreferenceController { + + private UserHandle mManagedUser; + + public CrossProfileCalendarPreferenceController(Context context, String key) { + super(context, key); + } + + public void setManagedUser(UserHandle managedUser) { + mManagedUser = managedUser; + } + + @Override + public int getAvailabilityStatus() { + return (mManagedUser != null) ? AVAILABLE : DISABLED_FOR_USER; + } + + @Override + public void updateState(Preference preference) { + super.updateState(preference); + if (preference instanceof RestrictedSwitchPreference && mManagedUser != null) { + final RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference; + final RestrictedLockUtils.EnforcedAdmin enforcedAdmin = + RestrictedLockUtilsInternal.getCrossProfileCalendarEnforcingAdmin( + mContext, mManagedUser.getIdentifier()); + pref.setDisabledByAdmin(enforcedAdmin); + } + } + + @Override + public boolean isChecked() { + if (mManagedUser == null) { + return false; + } + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + CROSS_PROFILE_CALENDAR_ENABLED, /* default= */ 0, + mManagedUser.getIdentifier()) == 1; + } + + @Override + public boolean setChecked(boolean isChecked) { + if (mManagedUser == null) { + return false; + } + final int value = isChecked ? 1 : 0; + return Settings.Secure.putIntForUser(mContext.getContentResolver(), + CROSS_PROFILE_CALENDAR_ENABLED, value, mManagedUser.getIdentifier()); + } +} \ No newline at end of file diff --git a/src/com/android/settings/accounts/ManagedProfileSettings.java b/src/com/android/settings/accounts/ManagedProfileSettings.java index 07e58458983..dccd7f67499 100644 --- a/src/com/android/settings/accounts/ManagedProfileSettings.java +++ b/src/com/android/settings/accounts/ManagedProfileSettings.java @@ -23,17 +23,25 @@ import android.content.IntentFilter; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; +import android.provider.SearchIndexableResource; import android.util.Log; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settings.search.Indexable; +import com.android.settingslib.search.SearchIndexable; + +import java.util.ArrayList; +import java.util.List; /** * Setting page for managed profile. * FIXME: It currently assumes there is only one managed profile. */ +@SearchIndexable public class ManagedProfileSettings extends DashboardFragment { private UserManager mUserManager; @@ -63,6 +71,7 @@ public class ManagedProfileSettings extends DashboardFragment { } use(WorkModePreferenceController.class).setManagedUser(mManagedUser); use(ContactSearchPreferenceController.class).setManagedUser(mManagedUser); + use(CrossProfileCalendarPreferenceController.class).setManagedUser(mManagedUser); } @Override @@ -99,6 +108,23 @@ public class ManagedProfileSettings extends DashboardFragment { return MetricsProto.MetricsEvent.ACCOUNTS_WORK_PROFILE_SETTINGS; } + public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider() { + @Override + public List getXmlResourcesToIndex(Context context, + boolean enabled) { + final ArrayList result = new ArrayList<>(); + final SearchIndexableResource sir = new SearchIndexableResource(context); + sir.xmlResId = R.xml.managed_profile_settings; + result.add(sir); + return result; + } + @Override + protected boolean isPageSearchEnabled(Context context) { + return false; + } + }; + private class ManagedProfileBroadcastReceiver extends BroadcastReceiver { @Override diff --git a/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java b/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java index de8902a8ffa..19339cd60ec 100644 --- a/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java +++ b/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java @@ -199,20 +199,7 @@ public class BluetoothPermissionActivity extends AlertActivity implements private void onNegative() { if (DEBUG) Log.d(TAG, "onNegative"); - - boolean always = true; - if (mRequestType == BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS) { - LocalBluetoothManager bluetoothManager = Utils.getLocalBtManager(this); - CachedBluetoothDeviceManager cachedDeviceManager = - bluetoothManager.getCachedDeviceManager(); - CachedBluetoothDevice cachedDevice = cachedDeviceManager.findDevice(mDevice); - if (cachedDevice == null) { - cachedDevice = cachedDeviceManager.addDevice(mDevice); - } - always = cachedDevice.checkAndIncreaseMessageRejectionCount(); - } - - sendReplyIntentToReceiver(false, always); + sendReplyIntentToReceiver(false, true); } private void sendReplyIntentToReceiver(final boolean allowed, final boolean always) { diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java index 6373519c770..e631d22e216 100644 --- a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java +++ b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java @@ -20,7 +20,7 @@ import static android.app.slice.Slice.HINT_ERROR; import static androidx.slice.widget.SliceLiveData.SUPPORTED_SPECS; -import static com.android.settings.slices.CustomSliceRegistry.CONNECTED_DEVICE_SLICE_URI; +import static com.android.settings.slices.CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI; import static com.android.settings.slices.CustomSliceRegistry.WIFI_SLICE_URI; import android.content.ContentProviderClient; @@ -196,7 +196,7 @@ public class ContextualCardLoader extends AsyncLoaderCompat private int getNumberOfLargeCard(List cards) { return (int) cards.stream() .filter(card -> card.getSliceUri().equals(WIFI_SLICE_URI) - || card.getSliceUri().equals(CONNECTED_DEVICE_SLICE_URI)) + || card.getSliceUri().equals(BLUETOOTH_DEVICES_SLICE_URI)) .count(); } diff --git a/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java b/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java index 376bb83606a..d5500fb37e3 100644 --- a/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java +++ b/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java @@ -40,8 +40,8 @@ public class SettingsContextualCardProvider extends ContextualCardProvider { .build(); final ContextualCard connectedDeviceCard = ContextualCard.newBuilder() - .setSliceUri(CustomSliceRegistry.CONNECTED_DEVICE_SLICE_URI.toString()) - .setCardName(CustomSliceRegistry.CONNECTED_DEVICE_SLICE_URI.toString()) + .setSliceUri(CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI.toString()) + .setCardName(CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI.toString()) .setCardCategory(ContextualCard.Category.IMPORTANT) .build(); final ContextualCard lowStorageCard = diff --git a/src/com/android/settings/homepage/contextualcards/slices/ConnectedDeviceSlice.java b/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java similarity index 82% rename from src/com/android/settings/homepage/contextualcards/slices/ConnectedDeviceSlice.java rename to src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java index b5d58b6176c..ad8d4a644aa 100644 --- a/src/com/android/settings/homepage/contextualcards/slices/ConnectedDeviceSlice.java +++ b/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java @@ -57,19 +57,12 @@ import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; -/** - * TODO(b/114807655): Contextual Home Page - Connected Device - * - * Show connected device info if one is currently connected. UI for connected device should - * match Connected Devices > Currently Connected Devices - * - * TODO This class will be refactor for Bluetooth connected devices only. - */ -public class ConnectedDeviceSlice implements CustomSliceable { +public class BluetoothDevicesSlice implements CustomSliceable { /** - * To sort the Bluetooth devices by {@link CachedBluetoothDevice}. - * Refer compareTo method from {@link com.android.settings.bluetooth.BluetoothDevicePreference}. + * TODO(b/114807655): Contextual Home Page - Connected Device + * Re-design sorting for new rule: + * Sorting rule: Audio Streaming > Last connected > Recently connected. */ private static final Comparator COMPARATOR = Comparator.naturalOrder(); @@ -80,11 +73,11 @@ public class ConnectedDeviceSlice implements CustomSliceable { */ private static final int DEFAULT_EXPANDED_ROW_COUNT = 3; - private static final String TAG = "ConnectedDeviceSlice"; + private static final String TAG = "BluetoothDevicesSlice"; private final Context mContext; - public ConnectedDeviceSlice(Context context) { + public BluetoothDevicesSlice(Context context) { mContext = context; } @@ -101,38 +94,38 @@ public class ConnectedDeviceSlice implements CustomSliceable { @Override public Uri getUri() { - return CustomSliceRegistry.CONNECTED_DEVICE_SLICE_URI; + return CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI; } @Override public Slice getSlice() { final IconCompat icon = IconCompat.createWithResource(mContext, - R.drawable.ic_homepage_connected_device); - final CharSequence title = mContext.getText(R.string.bluetooth_connected_devices); - final CharSequence titleNoConnectedDevices = mContext.getText( - R.string.no_connected_devices); + R.drawable.ic_settings_bluetooth); + final CharSequence title = mContext.getText(R.string.bluetooth_devices); + final CharSequence titleNoBluetoothDevices = mContext.getText( + R.string.no_bluetooth_devices); final PendingIntent primaryActionIntent = PendingIntent.getActivity(mContext, 0, getIntent(), 0); final SliceAction primarySliceAction = SliceAction.createDeeplink(primaryActionIntent, icon, ListBuilder.ICON_IMAGE, title); final ListBuilder listBuilder = - new ListBuilder(mContext, CustomSliceRegistry.CONNECTED_DEVICE_SLICE_URI, + new ListBuilder(mContext, CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI, ListBuilder.INFINITY) .setAccentColor(Utils.getColorAccentDefaultColor(mContext)); - // Get row builders by connected devices, e.g. Bluetooth. + // Get row builders by Bluetooth devices. final List rows = getBluetoothRowBuilder(primarySliceAction); - // Return a header with IsError flag, if no connected devices. + // Return a header with IsError flag, if no Bluetooth devices. if (rows.isEmpty()) { return listBuilder.setHeader(new ListBuilder.HeaderBuilder() - .setTitle(titleNoConnectedDevices) + .setTitle(titleNoBluetoothDevices) .setPrimaryAction(primarySliceAction)) .setIsError(true) .build(); } - // According the number of connected devices to set sub title of header. + // According the number of Bluetooth devices to set sub title of header. listBuilder.setHeader(new ListBuilder.HeaderBuilder() .setTitle(title) .setSubtitle(getSubTitle(rows.size())) @@ -161,7 +154,7 @@ public class ConnectedDeviceSlice implements CustomSliceable { screenTitle, MetricsProto.MetricsEvent.SLICE) .setClassName(mContext.getPackageName(), SubSettings.class.getName()) - .setData(CustomSliceRegistry.CONNECTED_DEVICE_SLICE_URI); + .setData(CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI); } @Override @@ -174,25 +167,30 @@ public class ConnectedDeviceSlice implements CustomSliceable { } @VisibleForTesting - List getBluetoothConnectedDevices() { - final List connectedBluetoothList = new ArrayList<>(); + List getBluetoothDevices() { + final List bluetoothDeviceList = new ArrayList<>(); // If Bluetooth is disable, skip to get the bluetooth devices. if (!BluetoothAdapter.getDefaultAdapter().isEnabled()) { - Log.i(TAG, "Cannot get Bluetooth connected devices, Bluetooth is disabled."); - return connectedBluetoothList; + Log.i(TAG, "Cannot get Bluetooth devices, Bluetooth is disabled."); + return bluetoothDeviceList; } // Get the Bluetooth devices from LocalBluetoothManager. final LocalBluetoothManager bluetoothManager = com.android.settings.bluetooth.Utils.getLocalBtManager(mContext); if (bluetoothManager == null) { - Log.i(TAG, "Cannot get Bluetooth connected devices, Bluetooth is unsupported."); - return connectedBluetoothList; + Log.i(TAG, "Cannot get Bluetooth devices, Bluetooth is unsupported."); + return bluetoothDeviceList; } final Collection cachedDevices = bluetoothManager.getCachedDeviceManager().getCachedDevicesCopy(); + /** + * TODO(b/114807655): Contextual Home Page - Connected Device + * Re-design to get all Bluetooth devices and sort them by new rule: + * Sorting rule: Audio Streaming > Last connected > Recently connected. + */ // Get connected Bluetooth devices and sort them. return cachedDevices.stream().filter(device -> device.isConnected()).sorted( COMPARATOR).collect(Collectors.toList()); @@ -217,25 +215,29 @@ public class ConnectedDeviceSlice implements CustomSliceable { } @VisibleForTesting - IconCompat getConnectedDeviceIcon(CachedBluetoothDevice device) { + IconCompat getBluetoothDeviceIcon(CachedBluetoothDevice device) { final Pair pair = BluetoothUtils .getBtClassDrawableWithDescription(mContext, device); if (pair.first != null) { return IconCompat.createWithBitmap(getBitmapFromVectorDrawable(pair.first)); } else { - return IconCompat.createWithResource(mContext, R.drawable.ic_homepage_connected_device); + return IconCompat.createWithResource(mContext, R.drawable.ic_settings_bluetooth); } } private List getBluetoothRowBuilder(SliceAction primarySliceAction) { final List bluetoothRows = new ArrayList<>(); - // According Bluetooth connected device to create row builders. - final List bluetoothDevices = getBluetoothConnectedDevices(); + /** + * TODO(b/114807655): Contextual Home Page - Connected Device + * Re-design to do action "activating" in primary action. + */ + // According Bluetooth device to create row builders. + final List bluetoothDevices = getBluetoothDevices(); for (CachedBluetoothDevice bluetoothDevice : bluetoothDevices) { bluetoothRows.add(new ListBuilder.RowBuilder() - .setTitleItem(getConnectedDeviceIcon(bluetoothDevice), ListBuilder.ICON_IMAGE) + .setTitleItem(getBluetoothDeviceIcon(bluetoothDevice), ListBuilder.ICON_IMAGE) .setTitle(bluetoothDevice.getName()) .setSubtitle(bluetoothDevice.getConnectionSummary()) .setPrimaryAction(primarySliceAction) @@ -254,7 +256,7 @@ public class ConnectedDeviceSlice implements CustomSliceable { } private CharSequence getSubTitle(int deviceCount) { - return mContext.getResources().getQuantityString(R.plurals.show_connected_devices, + return mContext.getResources().getQuantityString(R.plurals.show_bluetooth_devices, deviceCount, deviceCount); } diff --git a/src/com/android/settings/slices/CustomSliceManager.java b/src/com/android/settings/slices/CustomSliceManager.java index 8f8ee967fc4..4a9de15d841 100644 --- a/src/com/android/settings/slices/CustomSliceManager.java +++ b/src/com/android/settings/slices/CustomSliceManager.java @@ -29,7 +29,7 @@ import com.android.settings.homepage.contextualcards.deviceinfo.DeviceInfoSlice; import com.android.settings.homepage.contextualcards.deviceinfo.EmergencyInfoSlice; import com.android.settings.homepage.contextualcards.deviceinfo.StorageSlice; import com.android.settings.homepage.contextualcards.slices.BatteryFixSlice; -import com.android.settings.homepage.contextualcards.slices.ConnectedDeviceSlice; +import com.android.settings.homepage.contextualcards.slices.BluetoothDevicesSlice; import com.android.settings.homepage.contextualcards.slices.LowStorageSlice; import com.android.settings.location.LocationSlice; import com.android.settings.wifi.WifiSlice; @@ -106,7 +106,7 @@ public class CustomSliceManager { private void addSlices() { mUriMap.put(CustomSliceRegistry.BATTERY_FIX_SLICE_URI, BatteryFixSlice.class); mUriMap.put(CustomSliceRegistry.BATTERY_INFO_SLICE_URI, BatterySlice.class); - mUriMap.put(CustomSliceRegistry.CONNECTED_DEVICE_SLICE_URI, ConnectedDeviceSlice.class); + mUriMap.put(CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI, BluetoothDevicesSlice.class); mUriMap.put(CustomSliceRegistry.DATA_USAGE_SLICE_URI, DataUsageSlice.class); mUriMap.put(CustomSliceRegistry.DEVICE_INFO_SLICE_URI, DeviceInfoSlice.class); mUriMap.put(CustomSliceRegistry.EMERGENCY_INFO_SLICE_URI, EmergencyInfoSlice.class); diff --git a/src/com/android/settings/slices/CustomSliceRegistry.java b/src/com/android/settings/slices/CustomSliceRegistry.java index 80de3b2b9eb..1b8cffef509 100644 --- a/src/com/android/settings/slices/CustomSliceRegistry.java +++ b/src/com/android/settings/slices/CustomSliceRegistry.java @@ -72,13 +72,13 @@ public class CustomSliceRegistry { .build(); /** - * Backing Uri for Connected device Slice. + * Backing Uri for Bluetooth devices Slice. */ - public static final Uri CONNECTED_DEVICE_SLICE_URI = new Uri.Builder() + public static final Uri BLUETOOTH_DEVICES_SLICE_URI = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(SettingsSliceProvider.SLICE_AUTHORITY) .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION) - .appendPath("connected_device") + .appendPath("bluetooth_devices") .build(); /** * Backing Uri for the Data usage Slice. diff --git a/src/com/android/settings/wifi/WifiSlice.java b/src/com/android/settings/wifi/WifiSlice.java index 2382abb2873..64e3fc3fb16 100644 --- a/src/com/android/settings/wifi/WifiSlice.java +++ b/src/com/android/settings/wifi/WifiSlice.java @@ -37,7 +37,6 @@ import androidx.annotation.VisibleForTesting; import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; import androidx.slice.builders.ListBuilder; -import androidx.slice.builders.ListBuilder.RowBuilder; import androidx.slice.builders.SliceAction; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -64,9 +63,11 @@ public class WifiSlice implements CustomSliceable { static final int DEFAULT_EXPANDED_ROW_COUNT = 3; private final Context mContext; + private final WifiManager mWifiManager; public WifiSlice(Context context) { mContext = context; + mWifiManager = mContext.getSystemService(WifiManager.class); } @Override @@ -100,7 +101,7 @@ public class WifiSlice implements CustomSliceable { final ListBuilder listBuilder = new ListBuilder(mContext, WIFI_SLICE_URI, ListBuilder.INFINITY) .setAccentColor(color) - .addRow(new RowBuilder() + .addRow(new ListBuilder.RowBuilder() .setTitle(title) .setSubtitle(summary) .addEndItem(toggleSliceAction) @@ -110,18 +111,25 @@ public class WifiSlice implements CustomSliceable { return listBuilder.build(); } - List results = SliceBackgroundWorker.getInstance(mContext, this).getResults(); - if (results == null) { - results = new ArrayList<>(); - } - final int apCount = results.size(); + final List results = + SliceBackgroundWorker.getInstance(mContext, this).getResults(); + + // Need a loading text when results are not ready. + boolean needLoadingRow = results == null; + final int apCount = needLoadingRow ? 0 : results.size(); + // Add AP rows final CharSequence placeholder = mContext.getText(R.string.summary_placeholder); for (int i = 0; i < DEFAULT_EXPANDED_ROW_COUNT; i++) { if (i < apCount) { listBuilder.addRow(getAccessPointRow(results.get(i))); + } else if (needLoadingRow) { + listBuilder.addRow(new ListBuilder.RowBuilder() + .setTitle(mContext.getText(R.string.wifi_empty_list_wifi_on)) + .setSubtitle(placeholder)); + needLoadingRow = false; } else { - listBuilder.addRow(new RowBuilder() + listBuilder.addRow(new ListBuilder.RowBuilder() .setTitle(placeholder) .setSubtitle(placeholder)); } @@ -129,12 +137,12 @@ public class WifiSlice implements CustomSliceable { return listBuilder.build(); } - private RowBuilder getAccessPointRow(AccessPoint accessPoint) { + private ListBuilder.RowBuilder getAccessPointRow(AccessPoint accessPoint) { final String title = accessPoint.getConfigName(); final IconCompat levelIcon = IconCompat.createWithResource(mContext, com.android.settingslib.Utils.getWifiIconResource(accessPoint.getLevel())); final CharSequence apSummary = accessPoint.getSettingsSummary(); - final RowBuilder rowBuilder = new RowBuilder() + final ListBuilder.RowBuilder rowBuilder = new ListBuilder.RowBuilder() .setTitleItem(levelIcon, ListBuilder.ICON_IMAGE) .setTitle(title) .setSubtitle(!TextUtils.isEmpty(apSummary) @@ -188,10 +196,9 @@ public class WifiSlice implements CustomSliceable { */ @Override public void onNotifyChange(Intent intent) { - final WifiManager wifiManager = mContext.getSystemService(WifiManager.class); final boolean newState = intent.getBooleanExtra(EXTRA_TOGGLE_STATE, - wifiManager.isWifiEnabled()); - wifiManager.setWifiEnabled(newState); + mWifiManager.isWifiEnabled()); + mWifiManager.setWifiEnabled(newState); // Do not notifyChange on Uri. The service takes longer to update the current value than it // does for the Slice to check the current value again. Let {@link SliceBroadcastRelay} // handle it. @@ -211,26 +218,19 @@ public class WifiSlice implements CustomSliceable { } private boolean isWifiEnabled() { - final WifiManager wifiManager = mContext.getSystemService(WifiManager.class); - - switch (wifiManager.getWifiState()) { + switch (mWifiManager.getWifiState()) { case WifiManager.WIFI_STATE_ENABLED: case WifiManager.WIFI_STATE_ENABLING: return true; - case WifiManager.WIFI_STATE_DISABLED: - case WifiManager.WIFI_STATE_DISABLING: - case WifiManager.WIFI_STATE_UNKNOWN: default: return false; } } private CharSequence getSummary() { - final WifiManager wifiManager = mContext.getSystemService(WifiManager.class); - - switch (wifiManager.getWifiState()) { + switch (mWifiManager.getWifiState()) { case WifiManager.WIFI_STATE_ENABLED: - final String ssid = WifiInfo.removeDoubleQuotes(wifiManager.getConnectionInfo() + final String ssid = WifiInfo.removeDoubleQuotes(mWifiManager.getConnectionInfo() .getSSID()); if (TextUtils.equals(ssid, WifiSsid.NONE)) { return mContext.getText(R.string.disconnected); diff --git a/tests/robotests/assets/grandfather_not_implementing_index_provider b/tests/robotests/assets/grandfather_not_implementing_index_provider index e105dfc898e..ab06f751c77 100644 --- a/tests/robotests/assets/grandfather_not_implementing_index_provider +++ b/tests/robotests/assets/grandfather_not_implementing_index_provider @@ -7,7 +7,6 @@ com.android.settings.accessibility.ToggleScreenReaderPreferenceFragmentForSetupW com.android.settings.accessibility.ToggleSelectToSpeakPreferenceFragmentForSetupWizard com.android.settings.accounts.AccountDetailDashboardFragment com.android.settings.accounts.AccountSyncSettings -com.android.settings.accounts.ManagedProfileSettings com.android.settings.applications.appinfo.AppInfoDashboardFragment com.android.settings.applications.appinfo.DrawOverlayDetails com.android.settings.applications.appinfo.ExternalSourcesDetails diff --git a/tests/robotests/src/com/android/settings/accounts/CrossProfileCalendarPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/CrossProfileCalendarPreferenceControllerTest.java new file mode 100644 index 00000000000..4469282a366 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accounts/CrossProfileCalendarPreferenceControllerTest.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.accounts; + +import static android.provider.Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.robolectric.RuntimeEnvironment.application; + +import android.app.admin.DevicePolicyManager; +import android.content.ComponentName; +import android.content.Context; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.ArraySet; +import android.util.Log; + +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.RestrictedSwitchPreference; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.Shadows; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowDevicePolicyManager; + +import java.util.Collections; +import java.util.Set; + +@RunWith(SettingsRobolectricTestRunner.class) +public class CrossProfileCalendarPreferenceControllerTest { + + private static final String PREF_KEY = "cross_profile_calendar"; + private static final int MANAGED_USER_ID = 10; + private static final String TEST_PACKAGE_NAME = "com.test"; + private static final ComponentName TEST_COMPONENT_NAME = new ComponentName("test", "test"); + + @Mock + private UserHandle mManagedUser; + + private RestrictedSwitchPreference mPreference; + private Context mContext; + private CrossProfileCalendarPreferenceController mController; + private ShadowDevicePolicyManager dpm; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + mController = new CrossProfileCalendarPreferenceController(mContext, PREF_KEY); + mController.setManagedUser(mManagedUser); + mPreference = spy(new RestrictedSwitchPreference(mContext)); + dpm = Shadows.shadowOf(application.getSystemService(DevicePolicyManager.class)); + + when(mManagedUser.getIdentifier()).thenReturn(MANAGED_USER_ID); + doReturn(mContext).when(mContext).createPackageContextAsUser( + any(String.class), anyInt(), any(UserHandle.class)); + } + + @Test + public void getAvailabilityStatus_noManagedUser_DISABLED() { + mController.setManagedUser(null); + + assertThat(mController.getAvailabilityStatus()) + .isNotEqualTo(CrossProfileCalendarPreferenceController.AVAILABLE); + } + + @Test + public void getAvailabilityStatus_hasManagedUser_AVAILABLE() { + mController.setManagedUser(mManagedUser); + assertThat(mController.getAvailabilityStatus()) + .isEqualTo(CrossProfileCalendarPreferenceController.AVAILABLE); + } + + @Test + public void updateStateToDisabled_isNotChecked() { + Settings.Secure.putIntForUser(mContext.getContentResolver(), + CROSS_PROFILE_CALENDAR_ENABLED, 0, mManagedUser.getIdentifier()); + + mController.updateState(mPreference); + assertThat(mPreference.isChecked()).isFalse(); + } + + @Test + public void updateStateToEnabled_isChecked() throws Exception { + // Put 0 first so we know the value is not originally 1. + Settings.Secure.putIntForUser(mContext.getContentResolver(), + CROSS_PROFILE_CALENDAR_ENABLED, 0, mManagedUser.getIdentifier()); + mController.updateState(mPreference); + Settings.Secure.putIntForUser(mContext.getContentResolver(), + CROSS_PROFILE_CALENDAR_ENABLED, 1, mManagedUser.getIdentifier()); + + mController.updateState(mPreference); + assertThat(mPreference.isChecked()).isTrue(); + } + + @Test + public void updateState_noPackageAllowed_preferenceShouldBeDisabled() throws Exception { + dpm.setProfileOwner(TEST_COMPONENT_NAME); + + mController.updateState(mPreference); + verify(mPreference).setDisabledByAdmin(any()); + } + + @Test + public void updateState_somePackagesAllowed_preferenceShouldNotBeDisabled() throws Exception { + dpm.setProfileOwner(TEST_COMPONENT_NAME); + dpm.addCrossProfileCalendarPackage(TEST_COMPONENT_NAME, TEST_PACKAGE_NAME); + + mController.updateState(mPreference); + verify(mPreference).setDisabledByAdmin(null); + } + + @Test + public void onPreferenceChangeToFalse_shouldUpdateProviderValue() { + mController.onPreferenceChange(mPreference, false); + assertThat(Settings.Secure.getIntForUser(mContext.getContentResolver(), + CROSS_PROFILE_CALENDAR_ENABLED, 1, mManagedUser.getIdentifier())) + .isEqualTo(0); + } + + @Test + public void onPreferenceChangeToTrue_shouldUpdateProviderValue() { + // Change to false first so we know the value is not originally 1. + mController.onPreferenceChange(mPreference, false); + + mController.onPreferenceChange(mPreference, true); + assertThat(Settings.Secure.getIntForUser(mContext.getContentResolver(), + CROSS_PROFILE_CALENDAR_ENABLED, 0, mManagedUser.getIdentifier())) + .isEqualTo(1); + } +} \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java index 4f501970416..98943a0e07c 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java @@ -162,7 +162,7 @@ public class ContextualCardLoaderTest { cards.add(new ContextualCard.Builder() .setName("test_connected") .setCardType(ContextualCard.CardType.SLICE) - .setSliceUri(CustomSliceRegistry.CONNECTED_DEVICE_SLICE_URI) + .setSliceUri(CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI) .build()); cards.add(new ContextualCard.Builder() .setName("test_gesture") diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/ConnectedDeviceSliceTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSliceTest.java similarity index 64% rename from tests/robotests/src/com/android/settings/homepage/contextualcards/slices/ConnectedDeviceSliceTest.java rename to tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSliceTest.java index 14a2c477668..ac6557e67d8 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/ConnectedDeviceSliceTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSliceTest.java @@ -52,7 +52,7 @@ import java.util.ArrayList; import java.util.List; @RunWith(SettingsRobolectricTestRunner.class) -public class ConnectedDeviceSliceTest { +public class BluetoothDevicesSliceTest { private static final String BLUETOOTH_MOCK_SUMMARY = "BluetoothSummary"; private static final String BLUETOOTH_MOCK_TITLE = "BluetoothTitle"; @@ -60,8 +60,8 @@ public class ConnectedDeviceSliceTest { @Mock private CachedBluetoothDevice mCachedBluetoothDevice; - private List mBluetoothConnectedDeviceList; - private ConnectedDeviceSlice mConnectedDeviceSlice; + private List mBluetoothDeviceList; + private BluetoothDevicesSlice mBluetoothDevicesSlice; private Context mContext; private IconCompat mIcon; private PendingIntent mDetailIntent; @@ -74,57 +74,53 @@ public class ConnectedDeviceSliceTest { // Set-up specs for SliceMetadata. SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); - mConnectedDeviceSlice = spy(new ConnectedDeviceSlice(mContext)); + mBluetoothDevicesSlice = spy(new BluetoothDevicesSlice(mContext)); // Mock the icon and detail intent of Bluetooth. - mIcon = IconCompat.createWithResource(mContext, R.drawable.ic_homepage_connected_device); + mIcon = IconCompat.createWithResource(mContext, R.drawable.ic_settings_bluetooth); mDetailIntent = PendingIntent.getActivity(mContext, 0, new Intent("test action"), 0); - doReturn(mIcon).when(mConnectedDeviceSlice).getConnectedDeviceIcon(any()); - doReturn(mDetailIntent).when(mConnectedDeviceSlice).getBluetoothDetailIntent(any()); + doReturn(mIcon).when(mBluetoothDevicesSlice).getBluetoothDeviceIcon(any()); + doReturn(mDetailIntent).when(mBluetoothDevicesSlice).getBluetoothDetailIntent(any()); - // Initial Bluetooth connected device list. - mBluetoothConnectedDeviceList = new ArrayList<>(); + // Initial Bluetooth device list. + mBluetoothDeviceList = new ArrayList<>(); } @After public void tearDown() { - if (!mBluetoothConnectedDeviceList.isEmpty()) { - mBluetoothConnectedDeviceList.clear(); + if (!mBluetoothDeviceList.isEmpty()) { + mBluetoothDeviceList.clear(); } } @Test - public void getSlice_hasConnectedDevices_shouldHaveConnectedDeviceTitle() { + public void getSlice_hasBluetoothDevices_shouldHaveBluetoothDevicesTitle() { mockBluetoothDeviceList(); - doReturn(mBluetoothConnectedDeviceList).when( - mConnectedDeviceSlice).getBluetoothConnectedDevices(); + doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getBluetoothDevices(); - final Slice slice = mConnectedDeviceSlice.getSlice(); + final Slice slice = mBluetoothDevicesSlice.getSlice(); final List sliceItems = slice.getItems(); - SliceTester.assertTitle(sliceItems, - mContext.getString(R.string.bluetooth_connected_devices)); + SliceTester.assertTitle(sliceItems, mContext.getString(R.string.bluetooth_devices)); } @Test - public void getSlice_hasConnectedDevices_shouldMatchBluetoothMockTitle() { + public void getSlice_hasBluetoothDevices_shouldMatchBluetoothMockTitle() { mockBluetoothDeviceList(); - doReturn(mBluetoothConnectedDeviceList).when( - mConnectedDeviceSlice).getBluetoothConnectedDevices(); + doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getBluetoothDevices(); - final Slice slice = mConnectedDeviceSlice.getSlice(); + final Slice slice = mBluetoothDevicesSlice.getSlice(); final List sliceItems = slice.getItems(); SliceTester.assertTitle(sliceItems, BLUETOOTH_MOCK_TITLE); } @Test - public void getSlice_hasConnectedDevices_shouldHavePairNewDevice() { + public void getSlice_hasBluetoothDevices_shouldHavePairNewDevice() { mockBluetoothDeviceList(); - doReturn(mBluetoothConnectedDeviceList).when( - mConnectedDeviceSlice).getBluetoothConnectedDevices(); + doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getBluetoothDevices(); - final Slice slice = mConnectedDeviceSlice.getSlice(); + final Slice slice = mBluetoothDevicesSlice.getSlice(); final List sliceItems = slice.getItems(); SliceTester.assertTitle(sliceItems, @@ -132,22 +128,20 @@ public class ConnectedDeviceSliceTest { } @Test - public void getSlice_noConnectedDevices_shouldHaveNoConnectedDeviceTitle() { - doReturn(mBluetoothConnectedDeviceList).when( - mConnectedDeviceSlice).getBluetoothConnectedDevices(); + public void getSlice_noBluetoothDevices_shouldHaveNoBluetoothDevicesTitle() { + doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getBluetoothDevices(); - final Slice slice = mConnectedDeviceSlice.getSlice(); + final Slice slice = mBluetoothDevicesSlice.getSlice(); final List sliceItems = slice.getItems(); - SliceTester.assertTitle(sliceItems, mContext.getString(R.string.no_connected_devices)); + SliceTester.assertTitle(sliceItems, mContext.getString(R.string.no_bluetooth_devices)); } @Test - public void getSlice_noConnectedDevices_shouldNotHavePairNewDevice() { - doReturn(mBluetoothConnectedDeviceList).when( - mConnectedDeviceSlice).getBluetoothConnectedDevices(); + public void getSlice_noBluetoothDevices_shouldNotHavePairNewDevice() { + doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getBluetoothDevices(); - final Slice slice = mConnectedDeviceSlice.getSlice(); + final Slice slice = mBluetoothDevicesSlice.getSlice(); final SliceMetadata metadata = SliceMetadata.from(mContext, slice); assertThat(hasTitle(metadata, @@ -157,7 +151,7 @@ public class ConnectedDeviceSliceTest { private void mockBluetoothDeviceList() { doReturn(BLUETOOTH_MOCK_TITLE).when(mCachedBluetoothDevice).getName(); doReturn(BLUETOOTH_MOCK_SUMMARY).when(mCachedBluetoothDevice).getConnectionSummary(); - mBluetoothConnectedDeviceList.add(mCachedBluetoothDevice); + mBluetoothDeviceList.add(mCachedBluetoothDevice); } private boolean hasTitle(SliceMetadata metadata, String title) { diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRuntimePermissionPresenter.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRuntimePermissionPresenter.java index f11d9e889d2..e0576b2e6db 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRuntimePermissionPresenter.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowRuntimePermissionPresenter.java @@ -17,7 +17,7 @@ package com.android.settings.testutils.shadow; import android.content.Context; -import android.content.pm.permission.RuntimePermissionPresenter; +import android.permission.RuntimePermissionPresenter; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; diff --git a/tests/robotests/src/com/android/settings/wifi/WifiSliceTest.java b/tests/robotests/src/com/android/settings/wifi/WifiSliceTest.java index 5ac25ed5cd5..cdd1664d47b 100644 --- a/tests/robotests/src/com/android/settings/wifi/WifiSliceTest.java +++ b/tests/robotests/src/com/android/settings/wifi/WifiSliceTest.java @@ -53,14 +53,17 @@ public class WifiSliceTest { private Context mContext; + private WifiManager mWifiManager; private WifiSlice mWifiSlice; @Before public void setUp() { mContext = RuntimeEnvironment.application; + mWifiManager = mContext.getSystemService(WifiManager.class); // Set-up specs for SliceMetadata. SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); + mWifiManager.setWifiEnabled(true); mWifiSlice = new WifiSlice(mContext); } @@ -83,13 +86,30 @@ public class WifiSliceTest { } @Test - public void getWifiSlice_noAp_shouldReturnPlaceholder() { + public void getWifiSlice_wifiOff_shouldReturnSingleRow() { + mWifiManager.setWifiEnabled(false); + final Slice wifiSlice = mWifiSlice.getSlice(); - int rows = SliceQuery.findAll(wifiSlice, FORMAT_SLICE, HINT_LIST_ITEM, + final int rows = SliceQuery.findAll(wifiSlice, FORMAT_SLICE, HINT_LIST_ITEM, null /* nonHints */).size(); + + // Title row + assertThat(rows).isEqualTo(1); + } + + @Test + public void getWifiSlice_noAp_shouldReturnLoadingRow() { + final Slice wifiSlice = mWifiSlice.getSlice(); + + final int rows = SliceQuery.findAll(wifiSlice, FORMAT_SLICE, HINT_LIST_ITEM, + null /* nonHints */).size(); + final List sliceItems = wifiSlice.getItems(); + // All AP rows + title row assertThat(rows).isEqualTo(DEFAULT_EXPANDED_ROW_COUNT + 1); + // Has scanning text + SliceTester.assertTitle(sliceItems, mContext.getString(R.string.wifi_empty_list_wifi_on)); } @Test