From 17631aeff7d0de700a31a0b5713e52b7a168317b Mon Sep 17 00:00:00 2001 From: Weng Su Date: Tue, 21 Mar 2023 04:31:03 +0800 Subject: [PATCH] Added "Speed and Compatibility" Settings page - Show each band option individually in single-band devices - Show "2.4 and 5GHz" combined option in dual-band devices - Disable 5 GHz option if the device is in the restricted country - Disable 6 GHz option if the device is in the restricted country - Hide 6 GHz option if the old device does not support 6 GHz band. Bug: 245258763 Test: manual test atest -c WifiHotspotSpeedSettingsTest atest -c WifiHotspotSpeedViewModelTest \ WifiHotspotRepositoryTest Change-Id: I358d4ff8d62df72fd5080e55f40d588c238d01fb --- res/values/strings.xml | 32 +- res/xml/wifi_hotspot_speed.xml | 61 ++++ res/xml/wifi_tether_settings.xml | 1 + .../wifi/factory/WifiFeatureProvider.java | 39 ++- .../wifi/factory/WifiVerboseLogging.java | 55 ++++ .../repository/WifiHotspotRepository.java | 193 +++++++++--- .../wifi/tether/WifiHotspotSpeedSettings.java | 132 ++++++++ .../tether/WifiHotspotSpeedViewModel.java | 160 ++++++++++ .../wifi/tether/WifiTetherViewModel.java | 9 +- .../tether/WifiHotspotSpeedSettingsTest.java | 238 +++++++++++++++ .../wifi/tether/WifiTetherSettingsTest.java | 2 +- .../repository/WifiHotspotRepositoryTest.java | 220 +++++++++++++- .../tether/WifiHotspotSpeedViewModelTest.java | 287 ++++++++++++++++++ 13 files changed, 1374 insertions(+), 55 deletions(-) create mode 100644 res/xml/wifi_hotspot_speed.xml create mode 100644 src/com/android/settings/wifi/factory/WifiVerboseLogging.java create mode 100644 src/com/android/settings/wifi/tether/WifiHotspotSpeedSettings.java create mode 100644 src/com/android/settings/wifi/tether/WifiHotspotSpeedViewModel.java create mode 100644 tests/robotests/src/com/android/settings/wifi/tether/WifiHotspotSpeedSettingsTest.java create mode 100644 tests/unit/src/com/android/settings/wifi/tether/WifiHotspotSpeedViewModelTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 197672db02f..b7fb0134f51 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -2018,13 +2018,37 @@ Speed & compatibility - 2.4 GHz / Any device can connect + 2.4 GHz / Any device can connect - 5 GHz / Most devices can connect + 5 GHz / Most devices can connect - 6 GHz / Few devices can connect + 6 GHz / Few devices can connect - 2.4 and 5 GHz / Any device can connect + 2.4 and 5 GHz / Any device can connect + + Choose a frequency for your hotspot. The frequency affects the connection speed and what types of devices can find your hotspot. + + Preferred frequency + + 2.4 GHz + + Slower speeds. Any device can connect. + + 5 GHz + + Fast speeds. Most devices can connect. + + 2.4 and 5 GHz + + Fast speeds. Any device can connect to this dual-band hotspot. + + 6 GHz + + Fastest speeds. Fewest devices can connect. + + Not available in your country or region + + If your preferred frequency isn\u0027t available, your hotspot may use a different one. Hotspot security settings may change if you change the frequency. Turning hotspot on\u2026 diff --git a/res/xml/wifi_hotspot_speed.xml b/res/xml/wifi_hotspot_speed.xml new file mode 100644 index 00000000000..b19a5b94483 --- /dev/null +++ b/res/xml/wifi_hotspot_speed.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/xml/wifi_tether_settings.xml b/res/xml/wifi_tether_settings.xml index 1cd5f48ca0d..3023a6e10bd 100644 --- a/res/xml/wifi_tether_settings.xml +++ b/res/xml/wifi_tether_settings.xml @@ -50,5 +50,6 @@ android:key="wifi_hotspot_speed" android:title="@string/wifi_hotspot_speed_title" android:summary="@string/summary_placeholder" + android:fragment="com.android.settings.wifi.tether.WifiHotspotSpeedSettings" settings:isPreferenceVisible="@bool/config_show_wifi_hotspot_speed"/> diff --git a/src/com/android/settings/wifi/factory/WifiFeatureProvider.java b/src/com/android/settings/wifi/factory/WifiFeatureProvider.java index 433ff0c07ad..ea15c4359e4 100644 --- a/src/com/android/settings/wifi/factory/WifiFeatureProvider.java +++ b/src/com/android/settings/wifi/factory/WifiFeatureProvider.java @@ -16,14 +16,17 @@ package com.android.settings.wifi.factory; +import android.annotation.Nullable; import android.content.Context; import android.net.wifi.WifiManager; +import android.util.Log; import androidx.annotation.NonNull; import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelStoreOwner; import com.android.settings.wifi.repository.WifiHotspotRepository; +import com.android.settings.wifi.tether.WifiHotspotSpeedViewModel; import com.android.settings.wifi.tether.WifiTetherViewModel; import org.jetbrains.annotations.NotNull; @@ -32,9 +35,11 @@ import org.jetbrains.annotations.NotNull; * Wi-Fi Feature Provider */ public class WifiFeatureProvider { + private static final String TAG = "WifiFeatureProvider"; private final Context mAppContext; private WifiManager mWifiManager; + private WifiVerboseLogging mWifiVerboseLogging; private WifiHotspotRepository mWifiHotspotRepository; public WifiFeatureProvider(@NonNull Context appContext) { @@ -52,11 +57,22 @@ public class WifiFeatureProvider { } /** - * Get WifiRepository + * Get WifiVerboseLogging + */ + public WifiVerboseLogging getWifiVerboseLogging() { + if (mWifiVerboseLogging == null) { + mWifiVerboseLogging = new WifiVerboseLogging(mAppContext, getWifiManager()); + } + return mWifiVerboseLogging; + } + + /** + * Get WifiHotspotRepository */ public WifiHotspotRepository getWifiHotspotRepository() { if (mWifiHotspotRepository == null) { mWifiHotspotRepository = new WifiHotspotRepository(mAppContext, getWifiManager()); + verboseLog(TAG, "getWifiHotspotRepository():" + mWifiHotspotRepository); } return mWifiHotspotRepository; } @@ -68,5 +84,26 @@ public class WifiFeatureProvider { return new ViewModelProvider(owner).get(WifiTetherViewModel.class); } + /** + * Get WifiHotspotSpeedViewModel + */ + public WifiHotspotSpeedViewModel getWifiHotspotSpeedViewModel( + @NotNull ViewModelStoreOwner owner) { + WifiHotspotSpeedViewModel viewModel = + new ViewModelProvider(owner).get(WifiHotspotSpeedViewModel.class); + verboseLog(TAG, "getWifiHotspotSpeedViewModel():" + viewModel); + return viewModel; + } + + /** + * Send a {@link Log#VERBOSE} log message. + * + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + */ + public void verboseLog(@Nullable String tag, @NonNull String msg) { + getWifiVerboseLogging().log(tag, msg); + } } diff --git a/src/com/android/settings/wifi/factory/WifiVerboseLogging.java b/src/com/android/settings/wifi/factory/WifiVerboseLogging.java new file mode 100644 index 00000000000..2935ed49e74 --- /dev/null +++ b/src/com/android/settings/wifi/factory/WifiVerboseLogging.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.wifi.factory; + +import android.annotation.Nullable; +import android.content.Context; +import android.net.wifi.WifiManager; +import android.util.Log; + +import androidx.annotation.NonNull; + +/** + * Wi-Fi Verbose Logging + */ +public class WifiVerboseLogging { + private static final String TAG = "WifiVerboseLogging"; + + protected final Context mAppContext; + protected final WifiManager mWifiManager; + protected final boolean mIsVerboseLoggingEnabled; + + public WifiVerboseLogging(@NonNull Context appContext, @NonNull WifiManager wifiManager) { + mAppContext = appContext; + mWifiManager = wifiManager; + mIsVerboseLoggingEnabled = wifiManager.isVerboseLoggingEnabled(); + Log.v(TAG, "isVerboseLoggingEnabled:" + mIsVerboseLoggingEnabled); + } + + /** + * Send a {@link Log#VERBOSE} log message. + * + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + */ + public void log(@Nullable String tag, @NonNull String msg) { + if (mIsVerboseLoggingEnabled) { + Log.v(tag, msg); + } + } +} diff --git a/src/com/android/settings/wifi/repository/WifiHotspotRepository.java b/src/com/android/settings/wifi/repository/WifiHotspotRepository.java index ee1ceea600e..ff6d8832ae1 100644 --- a/src/com/android/settings/wifi/repository/WifiHotspotRepository.java +++ b/src/com/android/settings/wifi/repository/WifiHotspotRepository.java @@ -32,7 +32,8 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; -import androidx.lifecycle.Transformations; + +import com.android.settings.overlay.FeatureFactory; import java.util.HashMap; import java.util.List; @@ -45,7 +46,6 @@ import java.util.function.Consumer; */ public class WifiHotspotRepository { private static final String TAG = "WifiHotspotRepository"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); /** Wi-Fi hotspot band unknown. */ public static final int BAND_UNKNOWN = 0; @@ -84,8 +84,12 @@ public class WifiHotspotRepository { protected MutableLiveData mSpeedType; protected Boolean mIsDualBand; + protected Boolean mIs5gBandSupported; protected Boolean mIs5gAvailable; + protected MutableLiveData m5gAvailable; + protected Boolean mIs6gBandSupported; protected Boolean mIs6gAvailable; + protected MutableLiveData m6gAvailable; protected String mCurrentCountryCode; protected ActiveCountryCodeChangedCallback mActiveCountryCodeChangedCallback; @@ -140,6 +144,8 @@ public class WifiHotspotRepository { * Refresh data from the SoftApConfiguration. */ public void refresh() { + update6gAvailable(); + update5gAvailable(); updateSpeedType(); } @@ -163,8 +169,9 @@ public class WifiHotspotRepository { if (mSpeedType == null) { mSpeedType = new MutableLiveData<>(); updateSpeedType(); + log("getSpeedType():" + mSpeedType.getValue()); } - return Transformations.distinctUntilChanged(mSpeedType); + return mSpeedType; } protected void updateSpeedType() { @@ -177,7 +184,7 @@ public class WifiHotspotRepository { return; } int keyBand = config.getBand(); - logd("updateSpeedType(), getBand():" + keyBand); + log("updateSpeedType(), getBand():" + keyBand); if (!is5gAvailable()) { keyBand &= ~BAND_5GHZ; } @@ -195,52 +202,172 @@ public class WifiHotspotRepository { } else { keyBand = 0; } - logd("updateSpeedType(), keyBand:" + keyBand); + log("updateSpeedType(), keyBand:" + keyBand); mSpeedType.setValue(sSpeedMap.get(keyBand)); } - protected boolean isDualBand() { + /** + * Sets SpeedType + * + * @param speedType the Wi-Fi hotspot speed type. + */ + public void setSpeedType(int speedType) { + log("setSpeedType():" + speedType); + if (mSpeedType == null) { + getSpeedType(); + } + if (speedType == mSpeedType.getValue()) { + Log.w(TAG, "setSpeedType() is no changed! mSpeedType:" + mSpeedType.getValue()); + return; + } + SoftApConfiguration config = mWifiManager.getSoftApConfiguration(); + if (config == null) { + mSpeedType.setValue(SPEED_UNKNOWN); + Log.e(TAG, "setSpeedType(), WifiManager#getSoftApConfiguration() return null!"); + return; + } + SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(config); + if (speedType == SPEED_6GHZ) { + log("setSpeedType(), setBand(BAND_2GHZ_5GHZ_6GHZ)"); + configBuilder.setBand(BAND_2GHZ_5GHZ_6GHZ); + } else if (speedType == SPEED_5GHZ) { + log("setSpeedType(), setBand(BAND_2GHZ_5GHZ)"); + configBuilder.setBand(BAND_2GHZ_5GHZ); + } else if (mIsDualBand) { + log("setSpeedType(), setBands(BAND_2GHZ + BAND_2GHZ_5GHZ)"); + int[] bands = {BAND_2GHZ, BAND_2GHZ_5GHZ}; + configBuilder.setBands(bands); + } else { + log("setSpeedType(), setBand(BAND_2GHZ)"); + configBuilder.setBand(BAND_2GHZ); + } + setSoftApConfiguration(configBuilder.build()); + } + + /** + * Return whether Wi-Fi Dual Band is supported or not. + * @return {@code true} if Wi-Fi Dual Band is supported + */ + public boolean isDualBand() { if (mIsDualBand == null) { mIsDualBand = mWifiManager.isBridgedApConcurrencySupported(); - logd("isDualBand():" + mIsDualBand); + log("isDualBand():" + mIsDualBand); } return mIsDualBand; } - protected boolean is5gAvailable() { + /** + * Return whether Wi-Fi 5 GHz band is supported or not. + * @return {@code true} if Wi-Fi 5 GHz Band is supported + */ + public boolean is5GHzBandSupported() { + if (mIs5gBandSupported == null) { + mIs5gBandSupported = mWifiManager.is5GHzBandSupported(); + log("is5GHzBandSupported():" + mIs5gBandSupported); + } + return mIs5gBandSupported; + } + + /** + * Return whether Wi-Fi Hotspot 5 GHz band is available or not. + * @return {@code true} if Wi-Fi Hotspot 5 GHz Band is available + */ + public boolean is5gAvailable() { if (mIs5gAvailable == null) { - // TODO(b/272450463): isBandAvailable(WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS) will - // cause crash in the old model device, use a simple check to workaround it first. - mIs5gAvailable = (mWifiManager.is5GHzBandSupported() && mCurrentCountryCode != null); - logd("is5gAvailable():" + mIs5gAvailable); + // If Settings is unable to get available 5GHz SAP information, Wi-Fi Framework's + // proposal is to assume that 5GHz is available. (See b/272450463#comment16) + mIs5gAvailable = is5GHzBandSupported() + && isChannelAvailable(WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS, + true /* defaultValue */); + log("is5gAvailable():" + mIs5gAvailable); } return mIs5gAvailable; } - protected boolean is6gAvailable() { + /** + * Gets is5gAvailable LiveData + */ + public LiveData get5gAvailable() { + if (m5gAvailable == null) { + m5gAvailable = new MutableLiveData<>(); + m5gAvailable.setValue(is5gAvailable()); + } + return m5gAvailable; + } + + protected void update5gAvailable() { + if (m5gAvailable != null) { + m5gAvailable.setValue(is5gAvailable()); + } + } + + /** + * Return whether Wi-Fi 6 GHz band is supported or not. + * @return {@code true} if Wi-Fi 6 GHz Band is supported + */ + public boolean is6GHzBandSupported() { + if (mIs6gBandSupported == null) { + mIs6gBandSupported = mWifiManager.is6GHzBandSupported(); + log("is6GHzBandSupported():" + mIs6gBandSupported); + } + return mIs6gBandSupported; + } + + /** + * Return whether Wi-Fi Hotspot 6 GHz band is available or not. + * @return {@code true} if Wi-Fi Hotspot 6 GHz Band is available + */ + public boolean is6gAvailable() { if (mIs6gAvailable == null) { - mIs6gAvailable = mWifiManager.is6GHzBandSupported() - && isBandAvailable(WifiScanner.WIFI_BAND_6_GHZ); - logd("is6gAvailable():" + mIs6gAvailable); + mIs6gAvailable = is6GHzBandSupported() + && isChannelAvailable(WifiScanner.WIFI_BAND_6_GHZ, false /* defaultValue */); + log("is6gAvailable():" + mIs6gAvailable); } return mIs6gAvailable; } /** - * Return whether the Hotspot band is available or not. - * - * @param band one of the following band constants defined in {@code WifiScanner#WIFI_BAND_*} - * constants. - * 1. {@code WifiScanner#WIFI_BAND_5_GHZ_WITH_DFS} - * 2. {@code WifiScanner#WIFI_BAND_6_GHZ} + * Gets is6gAvailable LiveData */ - protected boolean isBandAvailable(int band) { - List channels = mWifiManager.getUsableChannels(band, OP_MODE_SAP); - return (channels != null && channels.size() > 0); + public LiveData get6gAvailable() { + if (m6gAvailable == null) { + m6gAvailable = new MutableLiveData<>(); + m6gAvailable.setValue(is6gAvailable()); + } + return m6gAvailable; + } + + protected void update6gAvailable() { + if (m6gAvailable != null) { + m6gAvailable.setValue(is6gAvailable()); + } + } + + /** + * Return whether the Hotspot channel is available or not. + * + * @param band one of the following band constants defined in + * {@code WifiScanner#WIFI_BAND_*} constants. + * 1. {@code WifiScanner#WIFI_BAND_5_GHZ_WITH_DFS} + * 2. {@code WifiScanner#WIFI_BAND_6_GHZ} + * @param defaultValue returns the default value if WifiManager#getUsableChannels is + * unavailable to get the SAP information. + */ + protected boolean isChannelAvailable(int band, boolean defaultValue) { + try { + List channels = mWifiManager.getUsableChannels(band, OP_MODE_SAP); + log("isChannelAvailable(), band:" + band + ", channels:" + channels); + return (channels != null && channels.size() > 0); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Querying usable SAP channels failed, band:" + band); + } catch (UnsupportedOperationException e) { + // This is expected on some hardware. + Log.e(TAG, "Querying usable SAP channels is unsupported, band:" + band); + } + return defaultValue; } protected void purgeRefreshData() { - mIsDualBand = null; mIs5gAvailable = null; mIs6gAvailable = null; } @@ -249,7 +376,7 @@ public class WifiHotspotRepository { if (mActiveCountryCodeChangedCallback != null) { return; } - logd("startMonitorSoftApConfiguration()"); + log("startMonitorSoftApConfiguration()"); mActiveCountryCodeChangedCallback = new ActiveCountryCodeChangedCallback(); mWifiManager.registerActiveCountryCodeChangedCallback(mAppContext.getMainExecutor(), mActiveCountryCodeChangedCallback); @@ -259,7 +386,7 @@ public class WifiHotspotRepository { if (mActiveCountryCodeChangedCallback == null) { return; } - logd("stopMonitorSoftApConfiguration()"); + log("stopMonitorSoftApConfiguration()"); mWifiManager.unregisterActiveCountryCodeChangedCallback(mActiveCountryCodeChangedCallback); mActiveCountryCodeChangedCallback = null; } @@ -268,7 +395,7 @@ public class WifiHotspotRepository { WifiManager.ActiveCountryCodeChangedCallback { @Override public void onActiveCountryCodeChanged(String country) { - logd("onActiveCountryCodeChanged(), country:" + country); + log("onActiveCountryCodeChanged(), country:" + country); mCurrentCountryCode = country; purgeRefreshData(); refresh(); @@ -276,16 +403,14 @@ public class WifiHotspotRepository { @Override public void onCountryCodeInactive() { - logd("onCountryCodeInactive()"); + log("onCountryCodeInactive()"); mCurrentCountryCode = null; purgeRefreshData(); refresh(); } } - private static void logd(String msg) { - if (DEBUG) { - Log.d(TAG, msg); - } + private void log(String msg) { + FeatureFactory.getFactory(mAppContext).getWifiFeatureProvider().verboseLog(TAG, msg); } } diff --git a/src/com/android/settings/wifi/tether/WifiHotspotSpeedSettings.java b/src/com/android/settings/wifi/tether/WifiHotspotSpeedSettings.java new file mode 100644 index 00000000000..467d3944681 --- /dev/null +++ b/src/com/android/settings/wifi/tether/WifiHotspotSpeedSettings.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.wifi.tether; + +import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_2GHZ; +import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_2GHZ_5GHZ; +import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_5GHZ; +import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_6GHZ; + +import android.app.settings.SettingsEnums; +import android.os.Bundle; + +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.widget.SelectorWithWidgetPreference; + +import java.util.HashMap; +import java.util.Map; + +/** + * Wi-Fi Hotspot Speed & compatibility Settings + */ +public class WifiHotspotSpeedSettings extends DashboardFragment implements + SelectorWithWidgetPreference.OnClickListener { + + private static final String TAG = "WifiHotspotSpeedSettings"; + + protected static final String KEY_SPEED_2GHZ = "wifi_hotspot_speed_2g"; + protected static final String KEY_SPEED_5GHZ = "wifi_hotspot_speed_5g"; + protected static final String KEY_SPEED_2GHZ_5GHZ = "wifi_hotspot_speed_2g_5g"; + protected static final String KEY_SPEED_6GHZ = "wifi_hotspot_speed_6g"; + protected static Map sSpeedKeyMap = new HashMap<>(); + + static { + sSpeedKeyMap.put(KEY_SPEED_2GHZ, SPEED_2GHZ); + sSpeedKeyMap.put(KEY_SPEED_5GHZ, SPEED_5GHZ); + sSpeedKeyMap.put(KEY_SPEED_2GHZ_5GHZ, SPEED_2GHZ_5GHZ); + sSpeedKeyMap.put(KEY_SPEED_6GHZ, SPEED_6GHZ); + } + + protected WifiHotspotSpeedViewModel mWifiHotspotSpeedViewModel; + protected Map mSpeedPreferenceMap = new HashMap<>(); + + @Override + protected int getPreferenceScreenResId() { + return R.xml.wifi_hotspot_speed; + } + + @Override + protected String getLogTag() { + return TAG; + } + + @Override + public int getMetricsCategory() { + return SettingsEnums.WIFI_TETHER_SETTINGS; + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + loadPreferences(); + mWifiHotspotSpeedViewModel = FeatureFactory.getFactory(getContext()) + .getWifiFeatureProvider().getWifiHotspotSpeedViewModel(this); + onSpeedInfoMapDataChanged(mWifiHotspotSpeedViewModel.getSpeedInfoMapData().getValue()); + mWifiHotspotSpeedViewModel.getSpeedInfoMapData() + .observe(this, this::onSpeedInfoMapDataChanged); + } + + protected void loadPreferences() { + for (Map.Entry entry : sSpeedKeyMap.entrySet()) { + SelectorWithWidgetPreference preference = findPreference(entry.getKey()); + if (preference != null) { + preference.setOnClickListener(this); + mSpeedPreferenceMap.put(entry.getValue(), preference); + } + } + } + + protected void onSpeedInfoMapDataChanged( + Map speedInfoMap) { + log("onSpeedViewDataChanged(), speedInfoMap:" + speedInfoMap); + for (Map.Entry entry : + mSpeedPreferenceMap.entrySet()) { + WifiHotspotSpeedViewModel.SpeedInfo speedInfo = speedInfoMap.get(entry.getKey()); + if (speedInfo == null) { + continue; + } + SelectorWithWidgetPreference radioButton = entry.getValue(); + if (radioButton == null) { + continue; + } + if (radioButton.isChecked() != speedInfo.mIsChecked) { + radioButton.setChecked(speedInfo.mIsChecked); + } + if (radioButton.isEnabled() != speedInfo.mIsEnabled) { + radioButton.setEnabled(speedInfo.mIsEnabled); + } + if (radioButton.isVisible() != speedInfo.mIsVisible) { + radioButton.setVisible(speedInfo.mIsVisible); + } + } + } + + @Override + public void onRadioButtonClicked(SelectorWithWidgetPreference emiter) { + String key = emiter.getKey(); + log("onRadioButtonClicked(), key:" + key); + if (sSpeedKeyMap.containsKey(key)) { + mWifiHotspotSpeedViewModel.setSpeedType(sSpeedKeyMap.get(key)); + } + } + + private void log(String msg) { + FeatureFactory.getFactory(getContext()).getWifiFeatureProvider().verboseLog(TAG, msg); + } +} diff --git a/src/com/android/settings/wifi/tether/WifiHotspotSpeedViewModel.java b/src/com/android/settings/wifi/tether/WifiHotspotSpeedViewModel.java new file mode 100644 index 00000000000..c30174e7614 --- /dev/null +++ b/src/com/android/settings/wifi/tether/WifiHotspotSpeedViewModel.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.wifi.tether; + +import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_2GHZ; +import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_2GHZ_5GHZ; +import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_5GHZ; +import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_6GHZ; + +import android.app.Application; + +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.Observer; + +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.wifi.repository.WifiHotspotRepository; + +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +/** + * Wi-Fi Hotspot Speed View Model + */ +public class WifiHotspotSpeedViewModel extends AndroidViewModel { + private static final String TAG = "WifiHotspotSpeedViewModel"; + + protected final WifiHotspotRepository mWifiHotspotRepository; + protected Map mSpeedInfoMap = new HashMap<>(); + protected MutableLiveData> mSpeedInfoMapData; + protected SpeedInfo mSpeedInfo2g = new SpeedInfo(false, true, false); + protected SpeedInfo mSpeedInfo5g = new SpeedInfo(false, true, false); + protected SpeedInfo mSpeedInfo2g5g = new SpeedInfo(false, true, true); + protected SpeedInfo mSpeedInfo6g = new SpeedInfo(false, true, true); + + protected final Observer m6gAvailableObserver = a -> on6gAvailableChanged(a); + protected final Observer m5gAvailableObserver = a -> on5gAvailableChanged(a); + protected final Observer mSpeedTypeObserver = st -> onSpeedTypeChanged(st); + + public WifiHotspotSpeedViewModel(@NotNull Application application) { + super(application); + mWifiHotspotRepository = FeatureFactory.getFactory(application).getWifiFeatureProvider() + .getWifiHotspotRepository(); + mWifiHotspotRepository.get6gAvailable().observeForever(m6gAvailableObserver); + mWifiHotspotRepository.get5gAvailable().observeForever(m5gAvailableObserver); + mWifiHotspotRepository.getSpeedType().observeForever(mSpeedTypeObserver); + mWifiHotspotRepository.setAutoRefresh(true); + + // The visibility of the 6 GHz speed option will not change on a Pixel device. + mSpeedInfo6g.mIsVisible = mWifiHotspotRepository.is6GHzBandSupported(); + } + + @Override + protected void onCleared() { + mWifiHotspotRepository.get6gAvailable().removeObserver(m6gAvailableObserver); + mWifiHotspotRepository.get5gAvailable().removeObserver(m5gAvailableObserver); + mWifiHotspotRepository.getSpeedType().removeObserver(mSpeedTypeObserver); + } + + protected void on6gAvailableChanged(Boolean available) { + log("on6gAvailableChanged(), available:" + available); + mSpeedInfo6g.mIsEnabled = available; + updateSpeedInfoMapData(); + } + + protected void on5gAvailableChanged(Boolean available) { + log("on5gAvailableChanged(), available:" + available); + mSpeedInfo5g.mIsEnabled = available; + + boolean showDualBand = mWifiHotspotRepository.isDualBand() && available; + log("on5gAvailableChanged(), showDualBand:" + showDualBand); + mSpeedInfo2g5g.mIsVisible = showDualBand; + mSpeedInfo2g.mIsVisible = !showDualBand; + mSpeedInfo5g.mIsVisible = !showDualBand; + updateSpeedInfoMapData(); + } + + protected void onSpeedTypeChanged(Integer speedType) { + log("onSpeedTypeChanged(), speedType:" + speedType); + mSpeedInfo2g.mIsChecked = speedType.equals(SPEED_2GHZ); + mSpeedInfo5g.mIsChecked = speedType.equals(SPEED_5GHZ); + mSpeedInfo2g5g.mIsChecked = speedType.equals(SPEED_2GHZ_5GHZ); + mSpeedInfo6g.mIsChecked = speedType.equals(SPEED_6GHZ); + updateSpeedInfoMapData(); + } + + /** + * Sets SpeedType + */ + public void setSpeedType(Integer speedType) { + mWifiHotspotRepository.setSpeedType(speedType); + } + + /** + * Gets Speed Information LiveData + */ + public LiveData> getSpeedInfoMapData() { + if (mSpeedInfoMapData == null) { + mSpeedInfoMapData = new MutableLiveData<>(); + mSpeedInfoMapData.setValue(mSpeedInfoMap); + log("getSpeedViewData(), mSpeedInfoMap:" + mSpeedInfoMapData.getValue()); + } + return mSpeedInfoMapData; + } + + protected void updateSpeedInfoMapData() { + mSpeedInfoMap.put(SPEED_2GHZ, mSpeedInfo2g); + mSpeedInfoMap.put(SPEED_5GHZ, mSpeedInfo5g); + mSpeedInfoMap.put(SPEED_2GHZ_5GHZ, mSpeedInfo2g5g); + mSpeedInfoMap.put(SPEED_6GHZ, mSpeedInfo6g); + if (mSpeedInfoMapData != null) { + mSpeedInfoMapData.setValue(mSpeedInfoMap); + } + } + + /** + * Wi-Fi Hotspot Speed Information + */ + public static final class SpeedInfo { + Boolean mIsChecked; + boolean mIsEnabled; + boolean mIsVisible; + + public SpeedInfo(boolean isChecked, boolean isEnabled, boolean isVisible) { + this.mIsChecked = isChecked; + this.mIsEnabled = isEnabled; + this.mIsVisible = isVisible; + } + + @Override + public String toString() { + return new StringBuilder("SpeedInfo{") + .append("isChecked:").append(mIsChecked) + .append(",isEnabled:").append(mIsEnabled) + .append(",isVisible:").append(mIsVisible) + .append('}').toString(); + } + } + + private void log(String msg) { + FeatureFactory.getFactory(getApplication()).getWifiFeatureProvider().verboseLog(TAG, msg); + } +} diff --git a/src/com/android/settings/wifi/tether/WifiTetherViewModel.java b/src/com/android/settings/wifi/tether/WifiTetherViewModel.java index 4abca023606..8cb74c3ef74 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherViewModel.java +++ b/src/com/android/settings/wifi/tether/WifiTetherViewModel.java @@ -46,13 +46,12 @@ public class WifiTetherViewModel extends AndroidViewModel { private static final String TAG = "WifiTetherViewModel"; protected static Map sSpeedSummaryResMap = new HashMap<>(); - static { sSpeedSummaryResMap.put(SPEED_UNKNOWN, R.string.summary_placeholder); - sSpeedSummaryResMap.put(SPEED_2GHZ, R.string.wifi_hotspot_speed_2g_summary); - sSpeedSummaryResMap.put(SPEED_5GHZ, R.string.wifi_hotspot_speed_5g_summary); - sSpeedSummaryResMap.put(SPEED_6GHZ, R.string.wifi_hotspot_speed_6g_summary); - sSpeedSummaryResMap.put(SPEED_2GHZ_5GHZ, R.string.wifi_hotspot_speed_2g_and_5g_summary); + sSpeedSummaryResMap.put(SPEED_2GHZ, R.string.wifi_hotspot_speed_summary_2g); + sSpeedSummaryResMap.put(SPEED_5GHZ, R.string.wifi_hotspot_speed_summary_5g); + sSpeedSummaryResMap.put(SPEED_6GHZ, R.string.wifi_hotspot_speed_summary_6g); + sSpeedSummaryResMap.put(SPEED_2GHZ_5GHZ, R.string.wifi_hotspot_speed_summary_2g_and_5g); } protected final WifiHotspotRepository mWifiHotspotRepository; diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiHotspotSpeedSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiHotspotSpeedSettingsTest.java new file mode 100644 index 00000000000..ec8cfe8657c --- /dev/null +++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiHotspotSpeedSettingsTest.java @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.wifi.tether; + +import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_2GHZ; +import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_2GHZ_5GHZ; +import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_5GHZ; +import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_6GHZ; +import static com.android.settings.wifi.tether.WifiHotspotSpeedSettings.KEY_SPEED_2GHZ; +import static com.android.settings.wifi.tether.WifiHotspotSpeedSettings.KEY_SPEED_2GHZ_5GHZ; +import static com.android.settings.wifi.tether.WifiHotspotSpeedSettings.KEY_SPEED_5GHZ; +import static com.android.settings.wifi.tether.WifiHotspotSpeedSettings.KEY_SPEED_6GHZ; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; + +import androidx.lifecycle.ViewModelStoreOwner; +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.wifi.factory.WifiFeatureProvider; +import com.android.settingslib.widget.SelectorWithWidgetPreference; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; + +import java.util.HashMap; +import java.util.Map; + +@RunWith(RobolectricTestRunner.class) +public class WifiHotspotSpeedSettingsTest { + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Spy + Context mContext = ApplicationProvider.getApplicationContext(); + @Mock + ViewModelStoreOwner mViewModelStoreOwner; + @Mock + WifiHotspotSpeedViewModel mViewModel; + @Mock + SelectorWithWidgetPreference mRadioButton; + + Map mSpeedInfoMap = new HashMap<>(); + WifiHotspotSpeedViewModel.SpeedInfo mSpeedInfo2g = + new WifiHotspotSpeedViewModel.SpeedInfo(false, true, false); + WifiHotspotSpeedViewModel.SpeedInfo mSpeedInfo5g = + new WifiHotspotSpeedViewModel.SpeedInfo(false, true, false); + WifiHotspotSpeedViewModel.SpeedInfo mSpeedInfo2g5g = + new WifiHotspotSpeedViewModel.SpeedInfo(false, true, true); + WifiHotspotSpeedViewModel.SpeedInfo mSpeedInfo6g = + new WifiHotspotSpeedViewModel.SpeedInfo(false, true, true); + + WifiHotspotSpeedSettings mSettings; + + @Before + public void setUp() { + WifiFeatureProvider provider = FakeFeatureFactory.setupForTest().getWifiFeatureProvider(); + when(provider.getWifiHotspotSpeedViewModel(mViewModelStoreOwner)).thenReturn(mViewModel); + + mSettings = new WifiHotspotSpeedSettings(); + mSettings.mWifiHotspotSpeedViewModel = mViewModel; + } + + @Test + public void onSpeedInfoMapDataChanged_checkedSpeed2g_checkedToRadioButton2g() { + mSpeedInfo2g = new WifiHotspotSpeedViewModel.SpeedInfo(false, true, false); + updateSpeedInfoMap(); + mockRadioButton(true, false, true); + mSettings.mSpeedPreferenceMap.put(SPEED_2GHZ, mRadioButton); + + mSettings.onSpeedInfoMapDataChanged(mSpeedInfoMap); + + verifyRadioButton(false, true, false); + } + + @Test + public void onSpeedInfoMapDataChanged_uncheckedSpeed2g_uncheckedToRadioButton2g() { + mSpeedInfo2g = new WifiHotspotSpeedViewModel.SpeedInfo(true, false, true); + updateSpeedInfoMap(); + mockRadioButton(false, true, false); + mSettings.mSpeedPreferenceMap.put(SPEED_2GHZ, mRadioButton); + + mSettings.onSpeedInfoMapDataChanged(mSpeedInfoMap); + + verifyRadioButton(true, false, true); + } + + @Test + public void onSpeedInfoMapDataChanged_checkedSpeed5g_checkedToRadioButton5g() { + mSpeedInfo5g = new WifiHotspotSpeedViewModel.SpeedInfo(false, true, false); + updateSpeedInfoMap(); + mockRadioButton(true, false, true); + mSettings.mSpeedPreferenceMap.put(SPEED_5GHZ, mRadioButton); + + mSettings.onSpeedInfoMapDataChanged(mSpeedInfoMap); + + verifyRadioButton(false, true, false); + } + + @Test + public void onSpeedInfoMapDataChanged_uncheckedSpeed5g_uncheckedToRadioButton5g() { + mSpeedInfo5g = new WifiHotspotSpeedViewModel.SpeedInfo(true, false, true); + updateSpeedInfoMap(); + mockRadioButton(false, true, false); + mSettings.mSpeedPreferenceMap.put(SPEED_5GHZ, mRadioButton); + + mSettings.onSpeedInfoMapDataChanged(mSpeedInfoMap); + + verifyRadioButton(true, false, true); + } + + @Test + public void onSpeedInfoMapDataChanged_checkedSpeed2g5g_checkedToRadioButton2g5g() { + mSpeedInfo2g5g = new WifiHotspotSpeedViewModel.SpeedInfo(false, true, false); + updateSpeedInfoMap(); + mockRadioButton(true, false, true); + mSettings.mSpeedPreferenceMap.put(SPEED_2GHZ_5GHZ, mRadioButton); + + mSettings.onSpeedInfoMapDataChanged(mSpeedInfoMap); + + verifyRadioButton(false, true, false); + } + + @Test + public void onSpeedInfoMapDataChanged_uncheckedSpeed25g_uncheckedToRadioButton25g() { + mSpeedInfo2g5g = new WifiHotspotSpeedViewModel.SpeedInfo(true, false, true); + updateSpeedInfoMap(); + mockRadioButton(false, true, false); + mSettings.mSpeedPreferenceMap.put(SPEED_2GHZ_5GHZ, mRadioButton); + + mSettings.onSpeedInfoMapDataChanged(mSpeedInfoMap); + + verifyRadioButton(true, false, true); + } + + @Test + public void onSpeedInfoMapDataChanged_checkedSpeed6g_checkedToRadioButton6g() { + mSpeedInfo6g = new WifiHotspotSpeedViewModel.SpeedInfo(false, true, false); + updateSpeedInfoMap(); + mockRadioButton(true, false, true); + mSettings.mSpeedPreferenceMap.put(SPEED_6GHZ, mRadioButton); + + mSettings.onSpeedInfoMapDataChanged(mSpeedInfoMap); + + verifyRadioButton(false, true, false); + } + + @Test + public void onSpeedInfoMapDataChanged_uncheckedSpeed6g_uncheckedToRadioButton6g() { + mSpeedInfo6g = new WifiHotspotSpeedViewModel.SpeedInfo(true, false, true); + updateSpeedInfoMap(); + mockRadioButton(false, true, false); + mSettings.mSpeedPreferenceMap.put(SPEED_6GHZ, mRadioButton); + + mSettings.onSpeedInfoMapDataChanged(mSpeedInfoMap); + + verifyRadioButton(true, false, true); + } + + @Test + public void onRadioButtonClicked_toSpeed2g_setSpeedType2g() { + when(mRadioButton.getKey()).thenReturn(KEY_SPEED_2GHZ); + + mSettings.onRadioButtonClicked(mRadioButton); + + verify(mViewModel).setSpeedType(SPEED_2GHZ); + } + + @Test + public void onRadioButtonClicked_toSpeed5g_setSpeedType5g() { + when(mRadioButton.getKey()).thenReturn(KEY_SPEED_5GHZ); + + mSettings.onRadioButtonClicked(mRadioButton); + + verify(mViewModel).setSpeedType(SPEED_5GHZ); + } + + @Test + public void onRadioButtonClicked_toSpeed2g5g_setSpeedType2g5g() { + when(mRadioButton.getKey()).thenReturn(KEY_SPEED_2GHZ_5GHZ); + + mSettings.onRadioButtonClicked(mRadioButton); + + verify(mViewModel).setSpeedType(SPEED_2GHZ_5GHZ); + } + + @Test + public void onRadioButtonClicked_toSpeed6g_setSpeedType6g() { + when(mRadioButton.getKey()).thenReturn(KEY_SPEED_6GHZ); + + mSettings.onRadioButtonClicked(mRadioButton); + + verify(mViewModel).setSpeedType(SPEED_6GHZ); + } + + private void updateSpeedInfoMap() { + mSpeedInfoMap.put(SPEED_2GHZ, mSpeedInfo2g); + mSpeedInfoMap.put(SPEED_5GHZ, mSpeedInfo5g); + mSpeedInfoMap.put(SPEED_2GHZ_5GHZ, mSpeedInfo2g5g); + mSpeedInfoMap.put(SPEED_6GHZ, mSpeedInfo6g); + } + + private void mockRadioButton(boolean isChecked, boolean isEnabled, boolean isVisible) { + when(mRadioButton.isChecked()).thenReturn(isChecked); + when(mRadioButton.isEnabled()).thenReturn(isEnabled); + when(mRadioButton.isVisible()).thenReturn(isVisible); + } + + private void verifyRadioButton(boolean isChecked, boolean isEnabled, boolean isVisible) { + verify(mRadioButton).setChecked(isChecked); + verify(mRadioButton).setEnabled(isEnabled); + verify(mRadioButton).setVisible(isVisible); + } + +} 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 582f792b788..0c2bbf529c7 100644 --- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java +++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java @@ -157,7 +157,7 @@ public class WifiTetherSettingsTest { @Test public void onSpeedSummaryChanged_canNotShowWifiHotspot_returnFalse() { - int stringResId = R.string.wifi_hotspot_speed_6g_summary; + int stringResId = R.string.wifi_hotspot_speed_summary_6g; mWifiTetherSettings.mWifiHotspotSpeed = mock(Preference.class); mWifiTetherSettings.onSpeedSummaryChanged(stringResId); diff --git a/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java b/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java index 52f02f5f14a..b75f9fc6c7d 100644 --- a/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java +++ b/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java @@ -19,6 +19,7 @@ package com.android.settings.wifi.repository; import static android.net.wifi.SoftApConfiguration.BAND_2GHZ; import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_OPEN; import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA3_SAE; +import static android.net.wifi.WifiAvailableChannel.OP_MODE_SAP; import static com.android.settings.wifi.repository.WifiHotspotRepository.BAND_2GHZ_5GHZ; import static com.android.settings.wifi.repository.WifiHotspotRepository.BAND_2GHZ_5GHZ_6GHZ; @@ -31,13 +32,17 @@ 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.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.net.wifi.SoftApConfiguration; +import android.net.wifi.WifiAvailableChannel; import android.net.wifi.WifiManager; +import android.net.wifi.WifiScanner; +import android.util.SparseIntArray; import androidx.lifecycle.MutableLiveData; import androidx.test.annotation.UiThreadTest; @@ -48,11 +53,15 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import java.util.Arrays; +import java.util.List; + @RunWith(AndroidJUnit4.class) public class WifiHotspotRepositoryTest { static final String WIFI_SSID = "wifi_ssid"; @@ -61,6 +70,9 @@ public class WifiHotspotRepositoryTest { static final int WIFI_5GHZ_BAND_PREFERRED = BAND_2GHZ_5GHZ; static final int WIFI_6GHZ_BAND_PREFERRED = BAND_2GHZ_5GHZ_6GHZ; + static final int CHANNEL_NOT_FOUND = -1; + static final int FREQ_5GHZ = 5000; + static final int FREQ_6GHZ = 6000; @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); @@ -73,10 +85,15 @@ public class WifiHotspotRepositoryTest { WifiHotspotRepository mWifiHotspotRepository; SoftApConfiguration mSoftApConfiguration; + ArgumentCaptor mSoftApConfigCaptor = + ArgumentCaptor.forClass(SoftApConfiguration.class); @Before public void setUp() { + doReturn(SPEED_6GHZ).when(mSpeedType).getValue(); + mWifiHotspotRepository = new WifiHotspotRepository(mContext, mWifiManager); + mWifiHotspotRepository.mSpeedType = mSpeedType; mWifiHotspotRepository.mCurrentCountryCode = WIFI_CURRENT_COUNTRY_CODE; mWifiHotspotRepository.mIsDualBand = true; mWifiHotspotRepository.mIs5gAvailable = true; @@ -148,8 +165,7 @@ public class WifiHotspotRepositoryTest { @Test public void refresh_liveDataIsUsed_getConfigAndUpdateLiveData() { - // If LiveData is used then it's not null. - mWifiHotspotRepository.mSpeedType = mSpeedType; + mWifiHotspotRepository.getSpeedType(); mWifiHotspotRepository.refresh(); @@ -191,7 +207,6 @@ public class WifiHotspotRepositoryTest { mWifiHotspotRepository.mIsDualBand = false; SoftApConfiguration config = new SoftApConfiguration.Builder().setBand(BAND_2GHZ).build(); when(mWifiManager.getSoftApConfiguration()).thenReturn(config); - mWifiHotspotRepository.mSpeedType = mSpeedType; mWifiHotspotRepository.updateSpeedType(); @@ -204,7 +219,6 @@ public class WifiHotspotRepositoryTest { SoftApConfiguration config = new SoftApConfiguration.Builder() .setBand(WIFI_5GHZ_BAND_PREFERRED).build(); when(mWifiManager.getSoftApConfiguration()).thenReturn(config); - mWifiHotspotRepository.mSpeedType = mSpeedType; mWifiHotspotRepository.updateSpeedType(); @@ -218,7 +232,6 @@ public class WifiHotspotRepositoryTest { SoftApConfiguration config = new SoftApConfiguration.Builder() .setBand(WIFI_5GHZ_BAND_PREFERRED).build(); when(mWifiManager.getSoftApConfiguration()).thenReturn(config); - mWifiHotspotRepository.mSpeedType = mSpeedType; mWifiHotspotRepository.updateSpeedType(); @@ -231,7 +244,6 @@ public class WifiHotspotRepositoryTest { SoftApConfiguration config = new SoftApConfiguration.Builder() .setBand(WIFI_6GHZ_BAND_PREFERRED).build(); when(mWifiManager.getSoftApConfiguration()).thenReturn(config); - mWifiHotspotRepository.mSpeedType = mSpeedType; mWifiHotspotRepository.updateSpeedType(); @@ -245,7 +257,6 @@ public class WifiHotspotRepositoryTest { SoftApConfiguration config = new SoftApConfiguration.Builder() .setBand(WIFI_6GHZ_BAND_PREFERRED).build(); when(mWifiManager.getSoftApConfiguration()).thenReturn(config); - mWifiHotspotRepository.mSpeedType = mSpeedType; mWifiHotspotRepository.updateSpeedType(); @@ -260,7 +271,6 @@ public class WifiHotspotRepositoryTest { SoftApConfiguration config = new SoftApConfiguration.Builder() .setBand(WIFI_6GHZ_BAND_PREFERRED).build(); when(mWifiManager.getSoftApConfiguration()).thenReturn(config); - mWifiHotspotRepository.mSpeedType = mSpeedType; mWifiHotspotRepository.updateSpeedType(); @@ -273,7 +283,6 @@ public class WifiHotspotRepositoryTest { SoftApConfiguration config = new SoftApConfiguration.Builder() .setBand(WIFI_5GHZ_BAND_PREFERRED).build(); when(mWifiManager.getSoftApConfiguration()).thenReturn(config); - mWifiHotspotRepository.mSpeedType = mSpeedType; mWifiHotspotRepository.updateSpeedType(); @@ -287,10 +296,201 @@ public class WifiHotspotRepositoryTest { SoftApConfiguration config = new SoftApConfiguration.Builder() .setBand(WIFI_5GHZ_BAND_PREFERRED).build(); when(mWifiManager.getSoftApConfiguration()).thenReturn(config); - mWifiHotspotRepository.mSpeedType = mSpeedType; mWifiHotspotRepository.updateSpeedType(); verify(mSpeedType).setValue(SPEED_2GHZ); } + + @Test + public void setSpeedType_sameValue_doNotSetConfig() { + doReturn(SPEED_6GHZ).when(mSpeedType).getValue(); + + mWifiHotspotRepository.setSpeedType(SPEED_6GHZ); + + verify(mWifiManager, never()).setSoftApConfiguration(any()); + } + + @Test + public void setSpeedType_from2g5ghz_setConfigBandTo6ghzPreferred() { + mockGetSoftApConfiguration(SPEED_2GHZ_5GHZ); + + mWifiHotspotRepository.setSpeedType(SPEED_6GHZ); + + verify(mWifiManager).setSoftApConfiguration(mSoftApConfigCaptor.capture()); + assertThat(mSoftApConfigCaptor.getValue().getBand()).isEqualTo(BAND_2GHZ_5GHZ_6GHZ); + } + + @Test + public void setSpeedType_from6ghz_setConfigBandsTo2g5ghz() { + mockGetSoftApConfiguration(SPEED_6GHZ); + mWifiHotspotRepository.mIsDualBand = true; + + mWifiHotspotRepository.setSpeedType(SPEED_2GHZ_5GHZ); + + verify(mWifiManager).setSoftApConfiguration(mSoftApConfigCaptor.capture()); + SparseIntArray channels = mSoftApConfigCaptor.getValue().getChannels(); + assertThat(channels.get(BAND_2GHZ, CHANNEL_NOT_FOUND)).isNotEqualTo(CHANNEL_NOT_FOUND); + assertThat(channels.get(BAND_2GHZ_5GHZ, CHANNEL_NOT_FOUND)).isNotEqualTo(CHANNEL_NOT_FOUND); + } + + @Test + public void setSpeedType_from2ghz_setConfigBandTo5ghzPreferred() { + mockGetSoftApConfiguration(SPEED_2GHZ); + + mWifiHotspotRepository.setSpeedType(SPEED_5GHZ); + + verify(mWifiManager).setSoftApConfiguration(mSoftApConfigCaptor.capture()); + assertThat(mSoftApConfigCaptor.getValue().getBand()).isEqualTo(WIFI_5GHZ_BAND_PREFERRED); + } + + @Test + public void setSpeedType_from5ghz_setConfigBandTo6ghzPreferred() { + mockGetSoftApConfiguration(SPEED_5GHZ); + + mWifiHotspotRepository.setSpeedType(SPEED_6GHZ); + + verify(mWifiManager).setSoftApConfiguration(mSoftApConfigCaptor.capture()); + assertThat(mSoftApConfigCaptor.getValue().getBand()).isEqualTo(WIFI_6GHZ_BAND_PREFERRED); + } + + @Test + public void setSpeedType_from5gTo6ghz_setConfigBandTo2ghz() { + mockGetSoftApConfiguration(SPEED_6GHZ); + + mWifiHotspotRepository.setSpeedType(SPEED_2GHZ); + + verify(mWifiManager).setSoftApConfiguration(mSoftApConfigCaptor.capture()); + assertThat(mSoftApConfigCaptor.getValue().getBand()).isEqualTo(BAND_2GHZ); + } + + @Test + public void isDualBand_resultSameAsWifiManager() { + // Reset mIsDualBand to trigger an update + mWifiHotspotRepository.mIsDualBand = null; + when(mWifiManager.isBridgedApConcurrencySupported()).thenReturn(true); + + assertThat(mWifiHotspotRepository.isDualBand()).isTrue(); + + // Reset mIsDualBand to trigger an update + mWifiHotspotRepository.mIsDualBand = null; + when(mWifiManager.isBridgedApConcurrencySupported()).thenReturn(false); + + assertThat(mWifiHotspotRepository.isDualBand()).isFalse(); + } + + @Test + public void is5GHzBandSupported_resultSameAsWifiManager() { + // Reset mIs5gBandSupported to trigger an update + mWifiHotspotRepository.mIs5gBandSupported = null; + when(mWifiManager.is5GHzBandSupported()).thenReturn(true); + + assertThat(mWifiHotspotRepository.is5GHzBandSupported()).isTrue(); + + // Reset mIs5gBandSupported to trigger an update + mWifiHotspotRepository.mIs5gBandSupported = null; + when(mWifiManager.is5GHzBandSupported()).thenReturn(false); + + assertThat(mWifiHotspotRepository.is5GHzBandSupported()).isFalse(); + } + + @Test + public void is5gAvailable_hasUsableChannels_returnTrue() { + mWifiHotspotRepository.mIs5gBandSupported = true; + // Reset mIs5gAvailable to trigger an update + mWifiHotspotRepository.mIs5gAvailable = null; + List channels = + Arrays.asList(new WifiAvailableChannel(FREQ_5GHZ, OP_MODE_SAP)); + when(mWifiManager.getUsableChannels(WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS, OP_MODE_SAP)) + .thenReturn(channels); + + assertThat(mWifiHotspotRepository.is5gAvailable()).isTrue(); + } + + @Test + public void is5gAvailable_noUsableChannels_returnFalse() { + mWifiHotspotRepository.mIs5gBandSupported = true; + // Reset mIs5gAvailable to trigger an update + mWifiHotspotRepository.mIs5gAvailable = null; + when(mWifiManager.getUsableChannels(WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS, OP_MODE_SAP)) + .thenReturn(null); + + assertThat(mWifiHotspotRepository.is5gAvailable()).isFalse(); + } + + @Test + @UiThreadTest + public void get5gAvailable_shouldNotReturnNull() { + // Reset m5gAvailable to trigger an update + mWifiHotspotRepository.m5gAvailable = null; + + assertThat(mWifiHotspotRepository.get5gAvailable()).isNotNull(); + } + + @Test + public void is6GHzBandSupported_resultSameAsWifiManager() { + // Reset mIs6gBandSupported to trigger an update + mWifiHotspotRepository.mIs6gBandSupported = null; + when(mWifiManager.is6GHzBandSupported()).thenReturn(true); + + assertThat(mWifiHotspotRepository.is6GHzBandSupported()).isTrue(); + + // Reset mIs6gBandSupported to trigger an update + mWifiHotspotRepository.mIs6gBandSupported = null; + when(mWifiManager.is6GHzBandSupported()).thenReturn(false); + + assertThat(mWifiHotspotRepository.is6GHzBandSupported()).isFalse(); + } + + @Test + public void is6gAvailable_hasUsableChannels_returnTrue() { + mWifiHotspotRepository.mIs6gBandSupported = true; + // Reset mIs6gAvailable to trigger an update + mWifiHotspotRepository.mIs6gAvailable = null; + List channels = + Arrays.asList(new WifiAvailableChannel(FREQ_6GHZ, OP_MODE_SAP)); + when(mWifiManager.getUsableChannels(WifiScanner.WIFI_BAND_6_GHZ, OP_MODE_SAP)) + .thenReturn(channels); + + assertThat(mWifiHotspotRepository.is6gAvailable()).isTrue(); + } + + @Test + public void is6gAvailable_noUsableChannels_returnFalse() { + mWifiHotspotRepository.mIs6gBandSupported = true; + // Reset mIs6gAvailable to trigger an update + mWifiHotspotRepository.mIs6gAvailable = null; + when(mWifiManager.getUsableChannels(WifiScanner.WIFI_BAND_6_GHZ, OP_MODE_SAP)) + .thenReturn(null); + + assertThat(mWifiHotspotRepository.is6gAvailable()).isFalse(); + } + + @Test + @UiThreadTest + public void get6gAvailable_shouldNotReturnNull() { + // Reset m6gAvailable to trigger an update + mWifiHotspotRepository.m6gAvailable = null; + + assertThat(mWifiHotspotRepository.get6gAvailable()).isNotNull(); + } + + private void mockGetSoftApConfiguration(int speedType) { + doReturn(speedType).when(mSpeedType).getValue(); + mWifiHotspotRepository.mIsDualBand = true; + SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); + if (speedType == SPEED_2GHZ) { + mWifiHotspotRepository.mIsDualBand = false; + configBuilder.setBand(BAND_2GHZ); + } else if (speedType == SPEED_5GHZ) { + mWifiHotspotRepository.mIsDualBand = false; + configBuilder.setBand(BAND_2GHZ_5GHZ); + } else if (speedType == SPEED_2GHZ_5GHZ) { + int[] bands = {BAND_2GHZ, BAND_2GHZ_5GHZ}; + configBuilder.setBands(bands); + } else if (speedType == SPEED_6GHZ) { + configBuilder.setBand(BAND_2GHZ_5GHZ_6GHZ); + } + when(mWifiManager.getSoftApConfiguration()).thenReturn(configBuilder.build()); + } } diff --git a/tests/unit/src/com/android/settings/wifi/tether/WifiHotspotSpeedViewModelTest.java b/tests/unit/src/com/android/settings/wifi/tether/WifiHotspotSpeedViewModelTest.java new file mode 100644 index 00000000000..73081614d37 --- /dev/null +++ b/tests/unit/src/com/android/settings/wifi/tether/WifiHotspotSpeedViewModelTest.java @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.wifi.tether; + +import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_2GHZ; +import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_2GHZ_5GHZ; +import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_5GHZ; +import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_6GHZ; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Application; +import android.content.Context; + +import androidx.lifecycle.MutableLiveData; +import androidx.test.annotation.UiThreadTest; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.wifi.repository.WifiHotspotRepository; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.Map; + +@RunWith(AndroidJUnit4.class) +public class WifiHotspotSpeedViewModelTest { + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Spy + Context mContext = ApplicationProvider.getApplicationContext(); + @Mock + WifiHotspotRepository mWifiHotspotRepository; + @Mock + MutableLiveData mSpeedType; + @Mock + MutableLiveData m5gAvailable; + @Mock + MutableLiveData m6gAvailable; + @Mock + MutableLiveData> mSpeedInfoMapData; + + WifiHotspotSpeedViewModel mViewModel; + + @Before + public void setUp() { + FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest(); + when(featureFactory.getWifiFeatureProvider().getWifiHotspotRepository()) + .thenReturn(mWifiHotspotRepository); + when(mWifiHotspotRepository.getSpeedType()).thenReturn(mSpeedType); + when(mWifiHotspotRepository.is5GHzBandSupported()).thenReturn(true); + when(mWifiHotspotRepository.get5gAvailable()).thenReturn(m5gAvailable); + when(mWifiHotspotRepository.is6GHzBandSupported()).thenReturn(true); + when(mWifiHotspotRepository.get6gAvailable()).thenReturn(m6gAvailable); + + mViewModel = new WifiHotspotSpeedViewModel((Application) mContext); + mViewModel.mSpeedInfoMapData = mSpeedInfoMapData; + } + + @Test + @UiThreadTest + public void constructor_observeDataAndSetAutoRefresh() { + verify(mSpeedType).observeForever(mViewModel.mSpeedTypeObserver); + verify(m5gAvailable).observeForever(mViewModel.m5gAvailableObserver); + verify(m6gAvailable).observeForever(mViewModel.m6gAvailableObserver); + verify(mWifiHotspotRepository).setAutoRefresh(true); + } + + @Test + @UiThreadTest + public void constructor_supported6GHzBand_set6gVisible() { + assertThat(mViewModel.mSpeedInfo6g.mIsVisible).isTrue(); + } + + @Test + @UiThreadTest + public void constructor_notSupported6GHzBand_set6gVisible() { + when(mWifiHotspotRepository.is6GHzBandSupported()).thenReturn(false); + + WifiHotspotSpeedViewModel viewModel = new WifiHotspotSpeedViewModel((Application) mContext); + + assertThat(viewModel.mSpeedInfo6g.mIsVisible).isFalse(); + } + + @Test + @UiThreadTest + public void onCleared_removeObserverData() { + mViewModel.onCleared(); + + verify(mSpeedType).removeObserver(mViewModel.mSpeedTypeObserver); + verify(m5gAvailable).removeObserver(mViewModel.m5gAvailableObserver); + verify(m6gAvailable).removeObserver(mViewModel.m6gAvailableObserver); + } + + @Test + @UiThreadTest + public void on6gAvailableChanged_itsAvailable_setLiveData6gEnabled() { + mViewModel.mSpeedInfo6g.mIsEnabled = false; + + mViewModel.on6gAvailableChanged(true); + + verify(mSpeedInfoMapData).setValue(mViewModel.mSpeedInfoMap); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_6GHZ).mIsEnabled).isTrue(); + } + + @Test + @UiThreadTest + public void on6gAvailableChanged_notAvailable_setLiveData6gDisabled() { + mViewModel.mSpeedInfo6g.mIsEnabled = true; + + mViewModel.on6gAvailableChanged(false); + + verify(mSpeedInfoMapData).setValue(mViewModel.mSpeedInfoMap); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_6GHZ).mIsEnabled).isFalse(); + } + + @Test + @UiThreadTest + public void on5gAvailableChanged_itsAvailable_setLiveData5gEnabled() { + mViewModel.mSpeedInfo5g.mIsEnabled = false; + + mViewModel.on5gAvailableChanged(true); + + verify(mSpeedInfoMapData).setValue(mViewModel.mSpeedInfoMap); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_5GHZ).mIsEnabled).isTrue(); + } + + @Test + @UiThreadTest + public void on5gAvailableChanged_notAvailable_setLiveData5gDisabled() { + mViewModel.mSpeedInfo5g.mIsEnabled = true; + + mViewModel.on5gAvailableChanged(false); + + verify(mSpeedInfoMapData).setValue(mViewModel.mSpeedInfoMap); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_5GHZ).mIsEnabled).isFalse(); + } + + @Test + @UiThreadTest + public void on5gAvailableChanged_inSingleBand_setLiveDataToShowSingleBand() { + when(mWifiHotspotRepository.isDualBand()).thenReturn(false); + + mViewModel.on5gAvailableChanged(true); + + verify(mSpeedInfoMapData).setValue(mViewModel.mSpeedInfoMap); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_2GHZ).mIsVisible).isTrue(); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_5GHZ).mIsVisible).isTrue(); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_2GHZ_5GHZ).mIsVisible).isFalse(); + } + + @Test + @UiThreadTest + public void on5gAvailableChanged_inDualBandAnd5gUnavailable_setLiveDataToShowSingleBand() { + when(mWifiHotspotRepository.isDualBand()).thenReturn(true); + + mViewModel.on5gAvailableChanged(false); + + verify(mSpeedInfoMapData).setValue(mViewModel.mSpeedInfoMap); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_2GHZ).mIsVisible).isTrue(); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_5GHZ).mIsVisible).isTrue(); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_2GHZ_5GHZ).mIsVisible).isFalse(); + } + + @Test + @UiThreadTest + public void on5gAvailableChanged_inDualBandAnd5gAvailable_setLiveDataToShowDualBand() { + when(mWifiHotspotRepository.isDualBand()).thenReturn(true); + + mViewModel.on5gAvailableChanged(true); + + verify(mSpeedInfoMapData).setValue(mViewModel.mSpeedInfoMap); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_2GHZ).mIsVisible).isFalse(); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_5GHZ).mIsVisible).isFalse(); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_2GHZ_5GHZ).mIsVisible).isTrue(); + } + + @Test + @UiThreadTest + public void onSpeedTypeChanged_toSpeed2g_setLiveData2gChecked() { + mViewModel.mSpeedInfo2g.mIsChecked = false; + + mViewModel.onSpeedTypeChanged(SPEED_2GHZ); + + verify(mSpeedInfoMapData).setValue(mViewModel.mSpeedInfoMap); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_2GHZ).mIsChecked).isTrue(); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_5GHZ).mIsChecked).isFalse(); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_2GHZ_5GHZ).mIsChecked).isFalse(); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_6GHZ).mIsChecked).isFalse(); + } + + @Test + @UiThreadTest + public void onSpeedTypeChanged_toSpeed5g_setLiveData5gChecked() { + mViewModel.mSpeedInfo5g.mIsChecked = false; + + mViewModel.onSpeedTypeChanged(SPEED_5GHZ); + + verify(mSpeedInfoMapData).setValue(mViewModel.mSpeedInfoMap); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_2GHZ).mIsChecked).isFalse(); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_5GHZ).mIsChecked).isTrue(); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_2GHZ_5GHZ).mIsChecked).isFalse(); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_6GHZ).mIsChecked).isFalse(); + } + + @Test + @UiThreadTest + public void onSpeedTypeChanged_toSpeed2g5g_setLiveData5gChecked() { + mViewModel.mSpeedInfo2g5g.mIsChecked = false; + + mViewModel.onSpeedTypeChanged(SPEED_2GHZ_5GHZ); + + verify(mSpeedInfoMapData).setValue(mViewModel.mSpeedInfoMap); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_2GHZ).mIsChecked).isFalse(); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_5GHZ).mIsChecked).isFalse(); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_2GHZ_5GHZ).mIsChecked).isTrue(); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_6GHZ).mIsChecked).isFalse(); + } + + @Test + @UiThreadTest + public void onSpeedTypeChanged_toSpeed6g_setLiveData5gChecked() { + mViewModel.mSpeedInfo6g.mIsChecked = false; + + mViewModel.onSpeedTypeChanged(SPEED_6GHZ); + + verify(mSpeedInfoMapData).setValue(mViewModel.mSpeedInfoMap); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_2GHZ).mIsChecked).isFalse(); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_5GHZ).mIsChecked).isFalse(); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_2GHZ_5GHZ).mIsChecked).isFalse(); + assertThat(mViewModel.mSpeedInfoMap.get(SPEED_6GHZ).mIsChecked).isTrue(); + } + + @Test + @UiThreadTest + public void setSpeedType_passedToRepository() { + mViewModel.setSpeedType(SPEED_2GHZ); + + verify(mWifiHotspotRepository).setSpeedType(SPEED_2GHZ); + + mViewModel.setSpeedType(SPEED_5GHZ); + + verify(mWifiHotspotRepository).setSpeedType(SPEED_5GHZ); + + mViewModel.setSpeedType(SPEED_2GHZ_5GHZ); + + verify(mWifiHotspotRepository).setSpeedType(SPEED_2GHZ_5GHZ); + + mViewModel.setSpeedType(SPEED_6GHZ); + + verify(mWifiHotspotRepository).setSpeedType(SPEED_6GHZ); + } + + @Test + @UiThreadTest + public void getSpeedInfoMapData_shouldNotReturnNull() { + // Reset mSpeedInfoMapData to trigger an update + mViewModel.mSpeedInfoMapData = null; + + assertThat(mViewModel.getSpeedInfoMapData()).isNotNull(); + } +}