diff --git a/res/layout/advanced_bt_entity_header.xml b/res/layout/advanced_bt_entity_header.xml index 833f6bda19d..37ae843f514 100644 --- a/res/layout/advanced_bt_entity_header.xml +++ b/res/layout/advanced_bt_entity_header.xml @@ -17,6 +17,7 @@ - + android:gravity="center"> + + + + + + + + + + + + + + + + + diff --git a/res/layout/le_audio_bt_entity_header.xml b/res/layout/le_audio_bt_entity_header.xml index 460ae69edd2..81911e9c946 100644 --- a/res/layout/le_audio_bt_entity_header.xml +++ b/res/layout/le_audio_bt_entity_header.xml @@ -17,6 +17,7 @@ - + android:gravity="center"> + + + - 16sp 90dp 48dp - 96dp - 56dp - 32dp + 76dp + 52dp + 28dp 32dp 20dp diff --git a/res/xml/bluetooth_device_details_fragment.xml b/res/xml/bluetooth_device_details_fragment.xml index 91f73a70b6e..f2e9e73f1b8 100644 --- a/res/xml/bluetooth_device_details_fragment.xml +++ b/res/xml/bluetooth_device_details_fragment.xml @@ -26,6 +26,13 @@ settings:allowDividerBelow="true" settings:searchable="false"/> + + { + RemoteDeviceNameDialogFragment.newInstance(mCachedDevice).show( + mFragment.getFragmentManager(), RemoteDeviceNameDialogFragment.TAG); + }); + } } } diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java index 462f422a56c..3fbd445c8fc 100644 --- a/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java +++ b/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java @@ -26,6 +26,7 @@ import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceScreen; import com.android.settings.R; +import com.android.settings.flags.Flags; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; @@ -47,6 +48,9 @@ public class BluetoothDetailsHeaderController extends BluetoothDetailsController @Override public boolean isAvailable() { + if (Flags.enableBluetoothDeviceDetailsPolish()) { + return false; + } boolean hasLeAudio = mCachedDevice.getUiAccessibleProfiles() .stream() .anyMatch(profile -> profile.getProfileId() == BluetoothProfile.LE_AUDIO); diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java index 5f9957b9121..ccf38ed2835 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java @@ -50,6 +50,7 @@ import com.android.settings.R; import com.android.settings.connecteddevice.stylus.StylusDevicesController; import com.android.settings.core.SettingsUIDeviceConfig; import com.android.settings.dashboard.RestrictedDashboardFragment; +import com.android.settings.flags.Flags; import com.android.settings.inputmethod.KeyboardSettingsPreferenceController; import com.android.settings.overlay.FeatureFactory; import com.android.settings.slices.SlicePreferenceController; @@ -213,8 +214,8 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment finish(); return; } - use(AdvancedBluetoothDetailsHeaderController.class).init(mCachedDevice); - use(LeAudioBluetoothDetailsHeaderController.class).init(mCachedDevice, mManager); + use(AdvancedBluetoothDetailsHeaderController.class).init(mCachedDevice, this); + use(LeAudioBluetoothDetailsHeaderController.class).init(mCachedDevice, mManager, this); use(KeyboardSettingsPreferenceController.class).init(mCachedDevice); final BluetoothFeatureProvider featureProvider = @@ -338,7 +339,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - if (!mUserManager.isGuestUser()) { + if (!Flags.enableBluetoothDeviceDetailsPolish() && !mUserManager.isGuestUser()) { MenuItem item = menu.add(0, EDIT_DEVICE_NAME_ITEM_ID, 0, R.string.bluetooth_rename_button); item.setIcon(com.android.internal.R.drawable.ic_mode_edit); @@ -365,6 +366,9 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment Lifecycle lifecycle = getSettingsLifecycle(); controllers.add(new BluetoothDetailsHeaderController(context, this, mCachedDevice, lifecycle)); + controllers.add( + new GeneralBluetoothDetailsHeaderController( + context, this, mCachedDevice, lifecycle)); controllers.add(new BluetoothDetailsButtonsController(context, this, mCachedDevice, lifecycle)); controllers.add(new BluetoothDetailsCompanionAppsController(context, this, diff --git a/src/com/android/settings/bluetooth/GeneralBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/GeneralBluetoothDetailsHeaderController.java new file mode 100644 index 00000000000..57a10278190 --- /dev/null +++ b/src/com/android/settings/bluetooth/GeneralBluetoothDetailsHeaderController.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2024 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.bluetooth; + +import android.bluetooth.BluetoothProfile; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.util.Pair; +import android.view.View; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.flags.Flags; +import com.android.settingslib.bluetooth.BluetoothUtils; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.widget.LayoutPreference; + +/** This class adds a header with device name and status (connected/disconnected, etc.). */ +public class GeneralBluetoothDetailsHeaderController extends BluetoothDetailsController { + private static final String KEY_GENERAL_DEVICE_HEADER = "general_bluetooth_device_header"; + + @Nullable + private LayoutPreference mLayoutPreference; + + public GeneralBluetoothDetailsHeaderController( + Context context, + PreferenceFragmentCompat fragment, + CachedBluetoothDevice device, + Lifecycle lifecycle) { + super(context, fragment, device, lifecycle); + } + + @Override + public boolean isAvailable() { + if (!Flags.enableBluetoothDeviceDetailsPolish()) { + return false; + } + boolean hasLeAudio = + mCachedDevice.getUiAccessibleProfiles().stream() + .anyMatch(profile -> profile.getProfileId() == BluetoothProfile.LE_AUDIO); + return !BluetoothUtils.isAdvancedDetailsHeader(mCachedDevice.getDevice()) && !hasLeAudio; + } + + @Override + protected void init(PreferenceScreen screen) { + mLayoutPreference = screen.findPreference(KEY_GENERAL_DEVICE_HEADER); + } + + @Override + protected void refresh() { + if (!isAvailable() || mLayoutPreference == null) { + return; + } + ImageView imageView = mLayoutPreference.findViewById(R.id.bt_header_icon); + if (imageView != null) { + final Pair pair = + BluetoothUtils.getBtRainbowDrawableWithDescription(mContext, mCachedDevice); + imageView.setImageDrawable(pair.first); + imageView.setContentDescription(pair.second); + } + + TextView title = mLayoutPreference.findViewById(R.id.bt_header_device_name); + if (title != null) { + title.setText(mCachedDevice.getName()); + } + TextView summary = mLayoutPreference.findViewById(R.id.bt_header_connection_summary); + if (summary != null) { + summary.setText(mCachedDevice.getConnectionSummary()); + } + ImageButton renameButton = mLayoutPreference.findViewById(R.id.rename_button); + renameButton.setVisibility(View.VISIBLE); + renameButton.setOnClickListener( + view -> { + RemoteDeviceNameDialogFragment.newInstance(mCachedDevice) + .show( + mFragment.getFragmentManager(), + RemoteDeviceNameDialogFragment.TAG); + }); + } + + @Override + @NonNull + public String getPreferenceKey() { + return KEY_GENERAL_DEVICE_HEADER; + } +} diff --git a/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java index a5e9cde2d17..25248942be7 100644 --- a/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java +++ b/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java @@ -27,14 +27,17 @@ import android.os.Looper; import android.util.Log; import android.util.Pair; import android.view.View; +import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.VisibleForTesting; +import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; +import com.android.settings.flags.Flags; import com.android.settings.fuelgauge.BatteryMeterView; import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; @@ -86,6 +89,7 @@ public class LeAudioBluetoothDetailsHeaderController extends BasePreferenceContr @VisibleForTesting static final int INVALID_RESOURCE_ID = -1; + PreferenceFragmentCompat mFragment; @VisibleForTesting LayoutPreference mLayoutPreference; LocalBluetoothManager mManager; @@ -151,11 +155,12 @@ public class LeAudioBluetoothDetailsHeaderController extends BasePreferenceContr } public void init(CachedBluetoothDevice cachedBluetoothDevice, - LocalBluetoothManager bluetoothManager) { + LocalBluetoothManager bluetoothManager, PreferenceFragmentCompat fragment) { mCachedDevice = cachedBluetoothDevice; mManager = bluetoothManager; mProfileManager = bluetoothManager.getProfileManager(); mCachedDeviceGroup = Utils.findAllCachedBluetoothDevicesByGroupId(mManager, mCachedDevice); + mFragment = fragment; } @VisibleForTesting @@ -163,6 +168,14 @@ public class LeAudioBluetoothDetailsHeaderController extends BasePreferenceContr if (mLayoutPreference == null || mCachedDevice == null) { return; } + if (Flags.enableBluetoothDeviceDetailsPolish()) { + ImageButton renameButton = mLayoutPreference.findViewById(R.id.rename_button); + renameButton.setVisibility(View.VISIBLE); + renameButton.setOnClickListener(view -> { + RemoteDeviceNameDialogFragment.newInstance(mCachedDevice).show( + mFragment.getFragmentManager(), RemoteDeviceNameDialogFragment.TAG); + }); + } final ImageView imageView = mLayoutPreference.findViewById(R.id.entity_header_icon); if (imageView != null) { final Pair pair = diff --git a/src/com/android/settings/network/SubscriptionsPreferenceController.java b/src/com/android/settings/network/SubscriptionsPreferenceController.java index b7ed31eda56..cdbf4562684 100644 --- a/src/com/android/settings/network/SubscriptionsPreferenceController.java +++ b/src/com/android/settings/network/SubscriptionsPreferenceController.java @@ -241,6 +241,8 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl if (mSubsGearPref == null) { mPreferenceGroup.removeAll(); mSubsGearPref = new MutableGearPreference(mContext, null); + mSubsGearPref + .checkRestrictionAndSetDisabled(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS); mSubsGearPref.setOnPreferenceClickListener(preference -> { connectCarrierNetwork(); return true; diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java index ee1485ea6e9..a5cdb954664 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java +++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java @@ -202,7 +202,6 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme @Override public void onAttach(Context context) { super.onAttach(context); - if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { Log.d(LOG_TAG, "Invalid subId, get the default subscription to show."); SubscriptionInfo info = SubscriptionUtil.getSubscriptionOrDefault(context, mSubId); @@ -341,6 +340,11 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme setTelephonyAvailabilityStatus(getPreferenceControllersAsList()); super.onCreate(icicle); + if (isUiRestricted()) { + Log.d(LOG_TAG, "Mobile network page is disallowed."); + finish(); + return; + } final Context context = getContext(); mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); mTelephonyManager = context.getSystemService(TelephonyManager.class) diff --git a/src/com/android/settings/notification/modes/IconUtil.java b/src/com/android/settings/notification/modes/IconUtil.java index 55abccf6231..43161ce085d 100644 --- a/src/com/android/settings/notification/modes/IconUtil.java +++ b/src/com/android/settings/notification/modes/IconUtil.java @@ -82,8 +82,8 @@ class IconUtil { } /** - * Returns a variant of the supplied {@code icon} to be used as the header in the icon picker. - * The inner icon is 48x48dp and it's contained in a circle of diameter 90dp. + * Returns a variant of the supplied {@code icon} to be used as the header in the icon picker + * (large icon within large circle, with the "material secondary" color combination). */ static Drawable makeIconPickerHeader(@NonNull Context context, Drawable icon) { return composeIconCircle( @@ -99,9 +99,9 @@ class IconUtil { } /** - * Returns a variant of the supplied {@code icon} to be used as an option in the icon picker. - * The inner icon is 36x36dp and it's contained in a circle of diameter 54dp. It's also set up - * so that selection and pressed states are represented in the color. + * Returns a variant of the supplied {@code icon} to be used as an option in the icon picker + * (small icon in small circle, with "material secondary" colors for the normal state and + * "material primary" colors for the selected state). */ static Drawable makeIconPickerItem(@NonNull Context context, @DrawableRes int iconResId) { return composeIconCircle( diff --git a/src/com/android/settings/system/reset/ResetNetworkConfirm.kt b/src/com/android/settings/system/reset/ResetNetworkConfirm.kt index 34b9909e55e..e0403569872 100644 --- a/src/com/android/settings/system/reset/ResetNetworkConfirm.kt +++ b/src/com/android/settings/system/reset/ResetNetworkConfirm.kt @@ -18,6 +18,7 @@ package com.android.settings.system.reset import android.app.ProgressDialog import android.app.settings.SettingsEnums +import android.content.DialogInterface import android.os.Bundle import android.os.Looper import android.telephony.SubscriptionManager @@ -56,7 +57,8 @@ import kotlinx.coroutines.withContext * This is the confirmation screen. */ class ResetNetworkConfirm : InstrumentedFragment() { - @VisibleForTesting lateinit var resetNetworkRequest: ResetNetworkRequest + @VisibleForTesting + lateinit var resetNetworkRequest: ResetNetworkRequest private var progressDialog: ProgressDialog? = null private var alertDialog: AlertDialog? = null private var resetStarted = false @@ -87,10 +89,7 @@ class ResetNetworkConfirm : InstrumentedFragment() { /** Configure the UI for the final confirmation interaction */ private fun View.establishFinalConfirmationState() { requireViewById(R.id.execute_reset_network).setOnClickListener { - if (!Utils.isMonkeyRunning() && !resetStarted) { - resetStarted = true - viewLifecycleOwner.lifecycleScope.launch { onResetClicked() } - } + showResetInternetDialog(); } } @@ -118,10 +117,10 @@ class ResetNetworkConfirm : InstrumentedFragment() { private fun invalidSubIdFlow(): Flow { val subIdsInRequest = listOf( - resetNetworkRequest.resetTelephonyAndNetworkPolicyManager, - resetNetworkRequest.resetApnSubId, - resetNetworkRequest.resetImsSubId, - ) + resetNetworkRequest.resetTelephonyAndNetworkPolicyManager, + resetNetworkRequest.resetApnSubId, + resetNetworkRequest.resetImsSubId, + ) .distinct() .filter(SubscriptionManager::isUsableSubscriptionId) @@ -162,6 +161,24 @@ class ResetNetworkConfirm : InstrumentedFragment() { } } + private fun showResetInternetDialog() { + val builder = AlertDialog.Builder(requireContext()) + val resetInternetClickListener = + DialogInterface.OnClickListener { dialog, which -> + if (!Utils.isMonkeyRunning() && !resetStarted) { + resetStarted = true + viewLifecycleOwner.lifecycleScope.launch { onResetClicked() } + } + } + + builder.setTitle(R.string.reset_your_internet_title) + .setMessage(R.string.reset_internet_text) + .setPositiveButton(R.string.tts_reset, resetInternetClickListener) + .setNegativeButton(android.R.string.cancel, null) + .create() + .show() + } + /** * Do all reset task. * @@ -173,7 +190,8 @@ class ResetNetworkConfirm : InstrumentedFragment() { withContext(Dispatchers.Default) { val builder = resetNetworkRequest.toResetNetworkOperationBuilder( - requireContext(), Looper.getMainLooper()) + requireContext(), Looper.getMainLooper() + ) resetNetworkRequest.resetEsimPackageName?.let { resetEsimPackageName -> builder.resetEsim(resetEsimPackageName) builder.resetEsimResultCallback { resetEsimSuccess = it } @@ -199,8 +217,8 @@ class ResetNetworkConfirm : InstrumentedFragment() { } else { Toast.makeText(activity, R.string.reset_network_complete_toast, Toast.LENGTH_SHORT) .show() + activity.finish() } - activity.finish() } override fun onDestroy() { diff --git a/src/com/android/settings/users/AppRestrictionsFragment.java b/src/com/android/settings/users/AppRestrictionsFragment.java index 1532448718c..c42e2f57b1d 100644 --- a/src/com/android/settings/users/AppRestrictionsFragment.java +++ b/src/com/android/settings/users/AppRestrictionsFragment.java @@ -651,7 +651,7 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen int requestCode = generateCustomActivityRequestCode( RestrictionsResultReceiver.this.preference); AppRestrictionsFragment.this.startActivityForResult( - restrictionsIntent, requestCode); + new Intent(restrictionsIntent), requestCode); } } } diff --git a/src/com/android/settings/widget/MutableGearPreference.java b/src/com/android/settings/widget/MutableGearPreference.java index b0804ebc2db..73491f02236 100644 --- a/src/com/android/settings/widget/MutableGearPreference.java +++ b/src/com/android/settings/widget/MutableGearPreference.java @@ -41,11 +41,13 @@ public class MutableGearPreference extends GearPreference { @Override public void setGearEnabled(boolean enabled) { + boolean state = false; if (mGear != null) { - mGear.setEnabled(enabled); - mGear.setImageAlpha(enabled ? VALUE_ENABLED_ALPHA : mDisabledAlphaValue); + state = enabled && !(isDisabledByAdmin() || isDisabledByEcm()); + mGear.setEnabled(state); + mGear.setImageAlpha(state ? VALUE_ENABLED_ALPHA : mDisabledAlphaValue); } - mGearState = enabled; + mGearState = state; } @Override diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java index 74671b5f4af..980dee57753 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherSettings.java +++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java @@ -371,12 +371,20 @@ public class WifiTetherSettings extends RestrictedDashboardFragment || !mWifiRestriction.isHotspotAvailable(context)) { keys.add(KEY_WIFI_TETHER_NETWORK_NAME); keys.add(KEY_WIFI_TETHER_SECURITY); + keys.add(KEY_WIFI_HOTSPOT_SECURITY); keys.add(KEY_WIFI_TETHER_NETWORK_PASSWORD); keys.add(KEY_WIFI_TETHER_AUTO_OFF); keys.add(KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); + keys.add(KEY_WIFI_HOTSPOT_SPEED); keys.add(KEY_INSTANT_HOTSPOT); - } else if (!mIsInstantHotspotEnabled) { - keys.add(KEY_INSTANT_HOTSPOT); + } else { + if (!isSpeedFeatureAvailable()) { + keys.add(KEY_WIFI_HOTSPOT_SECURITY); + keys.add(KEY_WIFI_HOTSPOT_SPEED); + } + if (!mIsInstantHotspotEnabled) { + keys.add(KEY_INSTANT_HOTSPOT); + } } // Remove duplicate @@ -400,6 +408,12 @@ public class WifiTetherSettings extends RestrictedDashboardFragment public List createPreferenceControllers(Context context) { return buildPreferenceControllers(context, null /* listener */); } + + @VisibleForTesting + boolean isSpeedFeatureAvailable() { + return FeatureFactory.getFeatureFactory().getWifiFeatureProvider() + .getWifiHotspotRepository().isSpeedFeatureAvailable(); + } } @VisibleForTesting diff --git a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java index 8d96f213446..af4888b3588 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java @@ -28,15 +28,19 @@ import android.bluetooth.BluetoothDevice; import android.content.Context; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; +import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.DeviceConfig; import android.view.LayoutInflater; import android.view.View; +import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; +import androidx.preference.PreferenceFragmentCompat; + import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.core.BasePreferenceController; @@ -93,6 +97,8 @@ public class AdvancedBluetoothDetailsHeaderControllerTest { private CachedBluetoothDevice mCachedDevice; @Mock private BluetoothAdapter mBluetoothAdapter; + @Mock + private PreferenceFragmentCompat mFragment; private AdvancedBluetoothDetailsHeaderController mController; private LayoutPreference mLayoutPreference; @@ -103,7 +109,7 @@ public class AdvancedBluetoothDetailsHeaderControllerTest { mContext = Robolectric.buildActivity(SettingsActivity.class).get(); mController = new AdvancedBluetoothDetailsHeaderController(mContext, "pref_Key"); when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice); - mController.init(mCachedDevice); + mController.init(mCachedDevice, mFragment); mLayoutPreference = new LayoutPreference(mContext, LayoutInflater.from(mContext).inflate(R.layout.advanced_bt_entity_header, null)); mController.mLayoutPreference = mLayoutPreference; @@ -540,6 +546,22 @@ public class AdvancedBluetoothDetailsHeaderControllerTest { rightBatteryPrediction); } + @Test + @EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH) + public void enablePolishFlag_renameButtonShown() { + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI, + SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true); + when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) + .thenReturn("true".getBytes()); + Set cacheBluetoothDevices = new HashSet<>(); + when(mCachedDevice.getMemberDevice()).thenReturn(cacheBluetoothDevices); + + mController.onStart(); + + ImageButton button = mLayoutPreference.findViewById(R.id.rename_button); + assertThat(button.getVisibility()).isEqualTo(View.VISIBLE); + } + private void assertBatteryPredictionVisible(LinearLayout linearLayout, int visible) { final TextView textView = linearLayout.findViewById(R.id.bt_battery_prediction); assertThat(textView.getVisibility()).isEqualTo(visible); diff --git a/tests/robotests/src/com/android/settings/bluetooth/GeneralBluetoothDetailsHeaderControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/GeneralBluetoothDetailsHeaderControllerTest.java new file mode 100644 index 00000000000..d608f3fcf68 --- /dev/null +++ b/tests/robotests/src/com/android/settings/bluetooth/GeneralBluetoothDetailsHeaderControllerTest.java @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2024 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.bluetooth; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; +import android.widget.TextView; + +import com.android.settings.R; +import com.android.settings.core.SettingsUIDeviceConfig; +import com.android.settings.flags.Flags; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.shadow.ShadowDeviceConfig; +import com.android.settings.testutils.shadow.ShadowEntityHeaderController; +import com.android.settingslib.bluetooth.LeAudioProfile; +import com.android.settingslib.widget.LayoutPreference; + +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import java.util.List; + +@RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowDeviceConfig.class}) +public class GeneralBluetoothDetailsHeaderControllerTest + extends BluetoothDetailsControllerTestBase { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + + private GeneralBluetoothDetailsHeaderController mController; + private LayoutPreference mPreference; + + @Mock private BluetoothDevice mBluetoothDevice; + @Mock private LeAudioProfile mLeAudioProfile; + + @Override + public void setUp() { + super.setUp(); + FakeFeatureFactory.setupForTest(); + android.provider.DeviceConfig.setProperty( + android.provider.DeviceConfig.NAMESPACE_SETTINGS_UI, + SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, + "true", + true); + mController = + new GeneralBluetoothDetailsHeaderController( + mContext, mFragment, mCachedDevice, mLifecycle); + mPreference = new LayoutPreference(mContext, R.layout.general_bt_entity_header); + mPreference.setKey(mController.getPreferenceKey()); + mScreen.addPreference(mPreference); + setupDevice(mDeviceConfig); + when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice); + when(mLeAudioProfile.getProfileId()).thenReturn(BluetoothProfile.LE_AUDIO); + } + + @After + public void tearDown() { + ShadowEntityHeaderController.reset(); + } + + /** + * Test to verify the current test context object works so that we are not checking null against + * null + */ + @Test + public void testContextMock() { + assertThat(mContext.getString(com.android.settingslib.R.string.bluetooth_connected)) + .isNotNull(); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH) + public void header() { + when(mCachedDevice.getName()).thenReturn("device name"); + when(mCachedDevice.getConnectionSummary()).thenReturn("Active"); + + showScreen(mController); + + TextView deviceName = mPreference.findViewById(R.id.bt_header_device_name); + TextView summary = mPreference.findViewById(R.id.bt_header_connection_summary); + assertThat(deviceName.getText().toString()).isEqualTo("device name"); + assertThat(summary.getText().toString()).isEqualTo("Active"); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH) + public void connectionStatusChangesWhileScreenOpen() { + TextView summary = mPreference.findViewById(R.id.bt_header_connection_summary); + when(mCachedDevice.getConnectionSummary()) + .thenReturn( + mContext.getString(com.android.settingslib.R.string.bluetooth_connected)); + + showScreen(mController); + String summaryText1 = summary.getText().toString(); + when(mCachedDevice.getConnectionSummary()).thenReturn(null); + mController.onDeviceAttributesChanged(); + String summaryText2 = summary.getText().toString(); + when(mCachedDevice.getConnectionSummary()) + .thenReturn( + mContext.getString(com.android.settingslib.R.string.bluetooth_connecting)); + mController.onDeviceAttributesChanged(); + String summaryText3 = summary.getText().toString(); + + assertThat(summaryText1) + .isEqualTo( + mContext.getString(com.android.settingslib.R.string.bluetooth_connected)); + assertThat(summaryText2).isEqualTo(""); + assertThat(summaryText3) + .isEqualTo( + mContext.getString(com.android.settingslib.R.string.bluetooth_connecting)); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH) + public void isAvailable_untetheredHeadset_returnFalse() { + when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) + .thenReturn("true".getBytes()); + + assertThat(mController.isAvailable()).isFalse(); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH) + public void isAvailable_notUntetheredHeadset_returnTrue() { + when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) + .thenReturn("false".getBytes()); + + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH) + public void isAvailable_leAudioDevice_returnFalse() { + when(mCachedDevice.getUiAccessibleProfiles()) + .thenReturn(List.of(mLeAudioProfile)); + + assertThat(mController.isAvailable()).isFalse(); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH) + public void isAvailable_flagEnabled_returnTrue() { + when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) + .thenReturn("false".getBytes()); + + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + @DisableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH) + public void iaAvailable_flagDisabled_returnFalse() { + assertThat(mController.isAvailable()).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java index 299d545c49b..0bc0a32daa6 100644 --- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java +++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java @@ -263,91 +263,114 @@ public class WifiTetherSettingsTest { @Test public void getNonIndexableKeys_tetherAvailable_keysNotReturned() { - when(mWifiRestriction.isTetherAvailable(mContext)).thenReturn(true); - when(mWifiRestriction.isHotspotAvailable(mContext)).thenReturn(true); - WifiTetherSettings.SearchIndexProvider searchIndexProvider = - new WifiTetherSettings.SearchIndexProvider(XML_RES, mWifiRestriction, - true /* isInstantHotspotEnabled */); + WifiTetherSettings.SearchIndexProvider searchIndexProvider = createSearchIndexProvider( + true /* isTetherAvailable */, true /* isHotspotAvailable */, + true /* isInstantHotspotEnabled */, true /* isSpeedFeatureAvailable */); final List keys = searchIndexProvider.getNonIndexableKeys(mContext); + assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); + assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_SECURITY); + assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_HOTSPOT_SECURITY); + assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); + assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); + assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); + assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_HOTSPOT_SPEED); + assertThat(keys).doesNotContain(WifiTetherSettings.KEY_INSTANT_HOTSPOT); + } + + @Test + public void getNonIndexableKeys_tetherNotAvailable_keysReturned() { + WifiTetherSettings.SearchIndexProvider searchIndexProvider = createSearchIndexProvider( + false /* isTetherAvailable */, true /* isHotspotAvailable */, + true /* isInstantHotspotEnabled */, true /* isSpeedFeatureAvailable */); + + final List keys = searchIndexProvider.getNonIndexableKeys(mContext); + + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_SECURITY); + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_HOTSPOT_SECURITY); + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_HOTSPOT_SPEED); + assertThat(keys).contains(WifiTetherSettings.KEY_INSTANT_HOTSPOT); + } + + @Test + public void getNonIndexableKeys_hotspotNotAvailable_keysReturned() { + WifiTetherSettings.SearchIndexProvider searchIndexProvider = createSearchIndexProvider( + true /* isTetherAvailable */, false /* isHotspotAvailable */, + true /* isInstantHotspotEnabled */, true /* isSpeedFeatureAvailable */); + + final List keys = searchIndexProvider.getNonIndexableKeys(mContext); + + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_SECURITY); + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_HOTSPOT_SECURITY); + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_HOTSPOT_SPEED); + assertThat(keys).contains(WifiTetherSettings.KEY_INSTANT_HOTSPOT); + } + + @Test + public void getNonIndexableKeys_tetherAndHotspotNotAvailable_keysReturned() { + WifiTetherSettings.SearchIndexProvider searchIndexProvider = createSearchIndexProvider( + false /* isTetherAvailable */, false /* isHotspotAvailable */, + true /* isInstantHotspotEnabled */, true /* isSpeedFeatureAvailable */); + + final List keys = searchIndexProvider.getNonIndexableKeys(mContext); + + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_SECURITY); + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_HOTSPOT_SECURITY); + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_HOTSPOT_SPEED); + assertThat(keys).contains(WifiTetherSettings.KEY_INSTANT_HOTSPOT); + } + + @Test + public void getNonIndexableKeys_instantHotspotNotAvailableOnly_keysContainInstantHotspotOnly() { + WifiTetherSettings.SearchIndexProvider searchIndexProvider = createSearchIndexProvider( + true /* isTetherAvailable */, true /* isHotspotAvailable */, + false /* isInstantHotspotEnabled */, true /* isSpeedFeatureAvailable */); + + final List keys = searchIndexProvider.getNonIndexableKeys(mContext); + + // doesNotContain + assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); + assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_SECURITY); + assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_HOTSPOT_SECURITY); + assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); + assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); + assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); + assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_HOTSPOT_SPEED); + // contains + assertThat(keys).contains(WifiTetherSettings.KEY_INSTANT_HOTSPOT); + } + + @Test + public void getNonIndexableKeys_speedFeatureNotAvailableOnly_keysContainInstantHotspotOnly() { + WifiTetherSettings.SearchIndexProvider searchIndexProvider = createSearchIndexProvider( + true /* isTetherAvailable */, true /* isHotspotAvailable */, + true /* isInstantHotspotEnabled */, false /* isSpeedFeatureAvailable */); + + final List keys = searchIndexProvider.getNonIndexableKeys(mContext); + + // doesNotContain assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_SECURITY); assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); assertThat(keys).doesNotContain(WifiTetherSettings.KEY_INSTANT_HOTSPOT); - } - - @Test - public void getNonIndexableKeys_tetherNotAvailable_keysReturned() { - when(mWifiRestriction.isTetherAvailable(mContext)).thenReturn(false); - when(mWifiRestriction.isHotspotAvailable(mContext)).thenReturn(true); - WifiTetherSettings.SearchIndexProvider searchIndexProvider = - new WifiTetherSettings.SearchIndexProvider(XML_RES, mWifiRestriction, - true /* isInstantHotspotEnabled */); - - final List keys = searchIndexProvider.getNonIndexableKeys(mContext); - - assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); - assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_SECURITY); - assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); - assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); - assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); - assertThat(keys).contains(WifiTetherSettings.KEY_INSTANT_HOTSPOT); - } - - @Test - public void getNonIndexableKeys_hotspotNotAvailable_keysReturned() { - when(mWifiRestriction.isTetherAvailable(mContext)).thenReturn(true); - when(mWifiRestriction.isHotspotAvailable(mContext)).thenReturn(false); - WifiTetherSettings.SearchIndexProvider searchIndexProvider = - new WifiTetherSettings.SearchIndexProvider(XML_RES, mWifiRestriction, - true /* isInstantHotspotEnabled */); - - final List keys = searchIndexProvider.getNonIndexableKeys(mContext); - - assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); - assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_SECURITY); - assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); - assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); - assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); - assertThat(keys).contains(WifiTetherSettings.KEY_INSTANT_HOTSPOT); - } - - @Test - public void getNonIndexableKeys_tetherAndHotspotNotAvailable_keysReturned() { - when(mWifiRestriction.isTetherAvailable(mContext)).thenReturn(false); - when(mWifiRestriction.isHotspotAvailable(mContext)).thenReturn(false); - WifiTetherSettings.SearchIndexProvider searchIndexProvider = - new WifiTetherSettings.SearchIndexProvider(XML_RES, mWifiRestriction, - true /* isInstantHotspotEnabled */); - - final List keys = searchIndexProvider.getNonIndexableKeys(mContext); - - assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); - assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_SECURITY); - assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); - assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); - assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); - } - - @Test - public void getNonIndexableKeys_instantHotspotNotAvailableOnly_keysContainInstantHotspotOnly() { - when(mWifiRestriction.isTetherAvailable(mContext)).thenReturn(true); - when(mWifiRestriction.isHotspotAvailable(mContext)).thenReturn(true); - WifiTetherSettings.SearchIndexProvider searchIndexProvider = - new WifiTetherSettings.SearchIndexProvider(XML_RES, mWifiRestriction, - false /* isInstantHotspotEnabled */); - - final List keys = searchIndexProvider.getNonIndexableKeys(mContext); - - assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); - assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_SECURITY); - assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); - assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); - assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); - assertThat(keys).contains(WifiTetherSettings.KEY_INSTANT_HOTSPOT); + // contains + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_HOTSPOT_SECURITY); + assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_HOTSPOT_SPEED); } @Test @@ -506,6 +529,18 @@ public class WifiTetherSettingsTest { mSettings.onCreate(Bundle.EMPTY); } + private WifiTetherSettings.SearchIndexProvider createSearchIndexProvider( + boolean isTetherAvailable, boolean isHotspotAvailable, boolean isInstantHotspotEnabled, + boolean isSpeedFeatureAvailable) { + when(mWifiRestriction.isTetherAvailable(mContext)).thenReturn(isTetherAvailable); + when(mWifiRestriction.isHotspotAvailable(mContext)).thenReturn(isHotspotAvailable); + WifiTetherSettings.SearchIndexProvider provider = + spy(new WifiTetherSettings.SearchIndexProvider(XML_RES, mWifiRestriction, + isInstantHotspotEnabled)); + when(provider.isSpeedFeatureAvailable()).thenReturn(isSpeedFeatureAvailable); + return provider; + } + @Implements(RestrictedDashboardFragment.class) public static final class ShadowRestrictedDashboardFragment { diff --git a/tests/unit/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java b/tests/unit/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java index 4d723dc1846..34c86024739 100644 --- a/tests/unit/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java +++ b/tests/unit/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java @@ -20,15 +20,33 @@ import static android.os.UserManager.DISALLOW_ADD_WIFI_CONFIG; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; import android.content.Context; +import android.content.Intent; import android.os.UserManager; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; import androidx.test.annotation.UiThreadTest; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.android.settings.flags.Flags; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.wifi.factory.WifiFeatureProvider; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -44,18 +62,36 @@ public class WifiDppConfiguratorActivityTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Spy private final Context mContext = ApplicationProvider.getApplicationContext(); @Mock private UserManager mUserManager; + @Mock + private FragmentManager mFragmentManager; + // Mock, created by FakeFeatureFactory + private WifiFeatureProvider mWifiFeatureProviderMock; + + @Spy private WifiDppConfiguratorActivity mActivity; @Before public void setUp() { when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager); - mActivity = new WifiDppConfiguratorActivity(); + mActivity.mFragmentManager = mFragmentManager; + doReturn(mContext).when(mActivity).getApplicationContext(); + + FragmentTransaction mockTransaction = mock(FragmentTransaction.class); + when(mFragmentManager.beginTransaction()).thenReturn(mockTransaction); + when(mockTransaction.replace(anyInt(), any(Fragment.class), anyString())) + .thenReturn(mockTransaction); + + FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest(); + mWifiFeatureProviderMock = featureFactory.mWifiFeatureProvider; } @Test @@ -71,4 +107,37 @@ public class WifiDppConfiguratorActivityTest { assertThat(mActivity.isAddWifiConfigAllowed(mContext)).isFalse(); } + + + @Test + @EnableFlags(Flags.FLAG_ENABLE_WIFI_SHARING_RUNTIME_FRAGMENT) + public void showQrCodeGeneratorFragment_shouldUseFeatureFactory() { + when(mUserManager.isGuestUser()).thenReturn(false); + when(mWifiFeatureProviderMock.getWifiDppQrCodeGeneratorFragment()) + .thenReturn(new WifiDppQrCodeGeneratorFragment()); + + mActivity.handleIntent(createQrCodeGeneratorIntent()); + + verify(mWifiFeatureProviderMock).getWifiDppQrCodeGeneratorFragment(); + } + + @Test + @DisableFlags(Flags.FLAG_ENABLE_WIFI_SHARING_RUNTIME_FRAGMENT) + public void showQrCodeGeneratorFragment_shouldNotUseFeatureFactory() { + when(mUserManager.isGuestUser()).thenReturn(false); + + mActivity.handleIntent(createQrCodeGeneratorIntent()); + + verify(mWifiFeatureProviderMock, never()) + .getWifiDppQrCodeGeneratorFragment(); + } + + private static Intent createQrCodeGeneratorIntent() { + Intent intent = new Intent( + WifiDppConfiguratorActivity.ACTION_CONFIGURATOR_QR_CODE_GENERATOR); + intent.putExtra(WifiDppUtils.EXTRA_WIFI_SSID, "GoogleGuest"); + intent.putExtra(WifiDppUtils.EXTRA_WIFI_SECURITY, "WPA"); + intent.putExtra(WifiDppUtils.EXTRA_WIFI_PRE_SHARED_KEY, "\\012345678,"); + return intent; + } }