From 631105a2af0e8e2ac37a1a8d727765a89a28b48b Mon Sep 17 00:00:00 2001 From: Yu-Ting Tseng Date: Fri, 3 Mar 2023 14:08:27 -0800 Subject: [PATCH 1/6] Remove CACHED_PROC_STATES which is unused. Test: atest ProcessStatsTest Bug: 271160990 Bug: 260242581 Change-Id: I62ed4eac102cc8f9c9ce7ab1325410cddc834ca1 --- src/com/android/settings/applications/ProcessStatsUi.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/com/android/settings/applications/ProcessStatsUi.java b/src/com/android/settings/applications/ProcessStatsUi.java index f49d638723c..ab334a8dd67 100644 --- a/src/com/android/settings/applications/ProcessStatsUi.java +++ b/src/com/android/settings/applications/ProcessStatsUi.java @@ -137,11 +137,6 @@ public class ProcessStatsUi extends ProcessStatsBase { ProcessStats.STATE_TOP }; - public static final int[] CACHED_PROC_STATES = new int[] { - ProcessStats.STATE_CACHED_ACTIVITY, ProcessStats.STATE_CACHED_ACTIVITY_CLIENT, - ProcessStats.STATE_CACHED_EMPTY - }; - public static String makeDuration(long time) { StringBuilder sb = new StringBuilder(32); TimeUtils.formatDuration(time, sb); From 0da11313430345b6f8fe46976eef3401376619ae Mon Sep 17 00:00:00 2001 From: Ankita Vyas Date: Tue, 7 Mar 2023 13:06:30 +0000 Subject: [PATCH 2/6] Display toast for clone app creation and completion in App Info page. Bug: 271990959 Test: manual Change-Id: I93641c1bf675ac531fbf8201203d5141aafd39cc --- res/values/strings.xml | 4 ++++ .../android/settings/spa/app/appinfo/AppCreateButton.kt | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/res/values/strings.xml b/res/values/strings.xml index b7b98f26038..2ad3664b93c 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -6778,6 +6778,10 @@ Creating… Cloned + + Creating %1$s clone + + Created %1$s clone Languages, gestures, time, backup diff --git a/src/com/android/settings/spa/app/appinfo/AppCreateButton.kt b/src/com/android/settings/spa/app/appinfo/AppCreateButton.kt index 1414626ede3..088680d9a0c 100644 --- a/src/com/android/settings/spa/app/appinfo/AppCreateButton.kt +++ b/src/com/android/settings/spa/app/appinfo/AppCreateButton.kt @@ -32,6 +32,7 @@ import com.android.settingslib.spa.widget.button.ActionButton import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import android.widget.Toast; class AppCreateButton(packageInfoPresenter: PackageInfoPresenter) { private val context = packageInfoPresenter.context @@ -55,10 +56,16 @@ class AppCreateButton(packageInfoPresenter: PackageInfoPresenter) { val cloneBackend = CloneBackend.getInstance(context) FeatureFactory.getFactory(context).metricsFeatureProvider.action(context, SettingsEnums.ACTION_CREATE_CLONE_APP) + val appLabel = app.loadLabel(context.packageManager) + Toast.makeText(context, context.getString(R.string.cloned_app_creation_toast_summary, + appLabel),Toast.LENGTH_SHORT).show() coroutineScope.launch { enabledState.value = false val result = installCloneApp(app, cloneBackend) if (result == CloneBackend.SUCCESS) { + Toast.makeText(context, + context.getString(R.string.cloned_app_created_toast_summary, appLabel), + Toast.LENGTH_SHORT).show() navController.navigate(getRoute(app.packageName, cloneBackend.cloneUserId), /* popUpCurrent*/ true) } else { From 1cc772d30dd5eee5670cceff42f97078880f57ea Mon Sep 17 00:00:00 2001 From: Justin Ghan Date: Wed, 8 Feb 2023 17:02:43 -0800 Subject: [PATCH 3/6] Support stylus handwriting in settings search bar Bug: 270708561 Test: make RunSettingsRoboTests ROBOTEST_FILTER=com.android.settings.search Change-Id: Iec6d6deb68b834654b2bbc11d96227db5319b0f0 --- .../search/SearchFeatureProvider.java | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/com/android/settings/search/SearchFeatureProvider.java b/src/com/android/settings/search/SearchFeatureProvider.java index b14a4d7fbf5..cd096ec6e16 100644 --- a/src/com/android/settings/search/SearchFeatureProvider.java +++ b/src/com/android/settings/search/SearchFeatureProvider.java @@ -126,16 +126,24 @@ public interface SearchFeatureProvider { true /* finishSecondaryWithPrimary */, false /* clearTop */); - toolbar.setOnClickListener(tb -> { - FeatureFactory.getFactory(context).getSlicesFeatureProvider() - .indexSliceDataAsync(context); + toolbar.setOnClickListener(tb -> startSearchActivity(context, activity, pageId, intent)); - FeatureFactory.getFactory(context).getMetricsFeatureProvider() - .logSettingsTileClick(KEY_HOMEPAGE_SEARCH_BAR, pageId); + toolbar.setHandwritingDelegatorCallback( + () -> startSearchActivity(context, activity, pageId, intent)); + toolbar.setAllowedHandwritingDelegatePackage(intent.getPackage()); + } - final Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(activity).toBundle(); - activity.startActivity(intent, bundle); - }); + /** Start the search activity. */ + private static void startSearchActivity( + Context context, FragmentActivity activity, int pageId, Intent intent) { + FeatureFactory.getFactory(context).getSlicesFeatureProvider() + .indexSliceDataAsync(context); + + FeatureFactory.getFactory(context).getMetricsFeatureProvider() + .logSettingsTileClick(KEY_HOMEPAGE_SEARCH_BAR, pageId); + + final Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(activity).toBundle(); + activity.startActivity(intent, bundle); } Intent buildSearchIntent(Context context, int pageId); From 4837c05b3f209dd97c6bb0123fc1e6691188a6e5 Mon Sep 17 00:00:00 2001 From: Vania Januar Date: Mon, 6 Mar 2023 14:30:58 +0000 Subject: [PATCH 4/6] Remove battery event time detection from StylusUpdater. StylusManager battery listener now triggers whenever the stylus battery becomes stale, so we no longer need to track event time from the updater. Bug: 270422546 Test: StylusDeviceUpdaterTest Change-Id: I36f5dc132a95f7d4649cec2bda1d457edc1f64bd --- .../stylus/StylusDeviceUpdater.java | 23 ++++--------- .../stylus/StylusDeviceUpdaterTest.java | 32 ++++++------------- 2 files changed, 17 insertions(+), 38 deletions(-) diff --git a/src/com/android/settings/connecteddevice/stylus/StylusDeviceUpdater.java b/src/com/android/settings/connecteddevice/stylus/StylusDeviceUpdater.java index 46ef3fbbc80..da6e1789b0c 100644 --- a/src/com/android/settings/connecteddevice/stylus/StylusDeviceUpdater.java +++ b/src/com/android/settings/connecteddevice/stylus/StylusDeviceUpdater.java @@ -22,7 +22,6 @@ import android.hardware.input.InputManager; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.os.SystemClock; import android.util.Log; import android.view.InputDevice; @@ -57,11 +56,11 @@ public class StylusDeviceUpdater implements InputManager.InputDeviceListener, private final InputManager mInputManager; private final MetricsFeatureProvider mMetricsFeatureProvider; - private long mLastUsiSeenTime = 0; private Context mContext; @VisibleForTesting Integer mLastDetectedUsiId; + BatteryState mLastBatteryState; @VisibleForTesting Preference mUsiPreference; @@ -75,7 +74,6 @@ public class StylusDeviceUpdater implements InputManager.InputDeviceListener, mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); mContext = context; mInputManager = context.getSystemService(InputManager.class); - } /** @@ -132,13 +130,8 @@ public class StylusDeviceUpdater implements InputManager.InputDeviceListener, @Override public void onBatteryStateChanged(int deviceId, long eventTimeMillis, @NonNull BatteryState batteryState) { - if (batteryState.isPresent()) { - mLastUsiSeenTime = eventTimeMillis; - mLastDetectedUsiId = deviceId; - } else { - mLastUsiSeenTime = -1; - mLastDetectedUsiId = null; - } + mLastBatteryState = batteryState; + mLastDetectedUsiId = deviceId; forceUpdate(); } @@ -184,7 +177,7 @@ public class StylusDeviceUpdater implements InputManager.InputDeviceListener, } private boolean shouldShowUsiPreference() { - return isUsiConnectionValid() && !hasConnectedBluetoothStylusDevice(); + return isUsiBatteryValid() && !hasConnectedBluetoothStylusDevice(); } @VisibleForTesting @@ -206,11 +199,9 @@ public class StylusDeviceUpdater implements InputManager.InputDeviceListener, } @VisibleForTesting - boolean isUsiConnectionValid() { - // battery listener uses uptimeMillis as its eventTime - long currentTime = SystemClock.uptimeMillis(); - long usiValidityDuration = 60 * 60 * 1000; // 1 hour - return mLastUsiSeenTime > 0 && currentTime - usiValidityDuration <= mLastUsiSeenTime; + boolean isUsiBatteryValid() { + return mLastBatteryState != null + && mLastBatteryState.isPresent() && mLastBatteryState.getCapacity() > 0f; } private void launchDeviceDetails() { diff --git a/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDeviceUpdaterTest.java index 10f37275712..7fb17e2eb2c 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDeviceUpdaterTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDeviceUpdaterTest.java @@ -127,7 +127,7 @@ public class StylusDeviceUpdaterTest { @Test public void click_usiPreference_launchUsiDetailsPage() { doReturn(mSettingsActivity).when(mDashboardFragment).getContext(); - doReturn(true).when(mStylusDeviceUpdater).isUsiConnectionValid(); + doReturn(true).when(mStylusDeviceUpdater).isUsiBatteryValid(); doReturn(false).when(mStylusDeviceUpdater).hasConnectedBluetoothStylusDevice(); mStylusDeviceUpdater.forceUpdate(); mStylusDeviceUpdater.mLastDetectedUsiId = 1; @@ -144,7 +144,7 @@ public class StylusDeviceUpdaterTest { @Test public void forceUpdate_addsUsiPreference_validUsiDevice() { - doReturn(true).when(mStylusDeviceUpdater).isUsiConnectionValid(); + doReturn(true).when(mStylusDeviceUpdater).isUsiBatteryValid(); doReturn(false).when(mStylusDeviceUpdater).hasConnectedBluetoothStylusDevice(); mStylusDeviceUpdater.forceUpdate(); @@ -154,7 +154,7 @@ public class StylusDeviceUpdaterTest { @Test public void forceUpdate_doesNotAddPreference_invalidUsiDevice() { - doReturn(false).when(mStylusDeviceUpdater).isUsiConnectionValid(); + doReturn(false).when(mStylusDeviceUpdater).isUsiBatteryValid(); doReturn(false).when(mStylusDeviceUpdater).hasConnectedBluetoothStylusDevice(); mStylusDeviceUpdater.forceUpdate(); @@ -164,12 +164,12 @@ public class StylusDeviceUpdaterTest { @Test public void forceUpdate_removesUsiPreference_existingPreference_invalidUsiDevice() { - doReturn(true).when(mStylusDeviceUpdater).isUsiConnectionValid(); + doReturn(true).when(mStylusDeviceUpdater).isUsiBatteryValid(); doReturn(false).when(mStylusDeviceUpdater).hasConnectedBluetoothStylusDevice(); mStylusDeviceUpdater.forceUpdate(); - doReturn(false).when(mStylusDeviceUpdater).isUsiConnectionValid(); + doReturn(false).when(mStylusDeviceUpdater).isUsiBatteryValid(); mStylusDeviceUpdater.forceUpdate(); assertThat(mStylusDeviceUpdater.mUsiPreference).isNull(); @@ -177,7 +177,7 @@ public class StylusDeviceUpdaterTest { @Test public void forceUpdate_doesNotAddUsiPreference_bluetoothStylusConnected() { - doReturn(true).when(mStylusDeviceUpdater).isUsiConnectionValid(); + doReturn(true).when(mStylusDeviceUpdater).isUsiBatteryValid(); doReturn(true).when(mStylusDeviceUpdater).hasConnectedBluetoothStylusDevice(); mStylusDeviceUpdater.forceUpdate(); @@ -187,7 +187,7 @@ public class StylusDeviceUpdaterTest { @Test public void forceUpdate_addsUsiPreference_bluetoothStylusDisconnected() { - doReturn(true).when(mStylusDeviceUpdater).isUsiConnectionValid(); + doReturn(true).when(mStylusDeviceUpdater).isUsiBatteryValid(); doReturn(true).when(mStylusDeviceUpdater).hasConnectedBluetoothStylusDevice(); mStylusDeviceUpdater.forceUpdate(); @@ -199,7 +199,7 @@ public class StylusDeviceUpdaterTest { @Test public void forceUpdate_removesUsiPreference_existingPreference_bluetoothStylusConnected() { - doReturn(true).when(mStylusDeviceUpdater).isUsiConnectionValid(); + doReturn(true).when(mStylusDeviceUpdater).isUsiBatteryValid(); doReturn(false).when(mStylusDeviceUpdater).hasConnectedBluetoothStylusDevice(); mStylusDeviceUpdater.forceUpdate(); doReturn(true).when(mStylusDeviceUpdater).hasConnectedBluetoothStylusDevice(); @@ -218,7 +218,7 @@ public class StylusDeviceUpdaterTest { mStylusDeviceUpdater.onBatteryStateChanged(1, SystemClock.uptimeMillis(), batteryState); - assertThat(mStylusDeviceUpdater.isUsiConnectionValid()).isTrue(); + assertThat(mStylusDeviceUpdater.isUsiBatteryValid()).isTrue(); } @Test @@ -230,19 +230,7 @@ public class StylusDeviceUpdaterTest { mStylusDeviceUpdater.onBatteryStateChanged(1, SystemClock.uptimeMillis(), batteryState); - assertThat(mStylusDeviceUpdater.isUsiConnectionValid()).isFalse(); - } - - @Test - public void onBatteryStateChanged_ddetectsInvalidUsi_staleBatteryEventTime() { - doReturn(false).when(mStylusDeviceUpdater).hasConnectedBluetoothStylusDevice(); - BatteryState batteryState = mock(BatteryState.class); - doReturn(true).when(batteryState).isPresent(); - doReturn(0.5f).when(batteryState).getCapacity(); - - mStylusDeviceUpdater.onBatteryStateChanged(1, 0, batteryState); - - assertThat(mStylusDeviceUpdater.isUsiConnectionValid()).isFalse(); + assertThat(mStylusDeviceUpdater.isUsiBatteryValid()).isFalse(); } @Test From 0c3957db8b7b1233bb7876195b9de4671520b711 Mon Sep 17 00:00:00 2001 From: ykhung Date: Thu, 9 Mar 2023 14:28:31 +0800 Subject: [PATCH 5/6] Add configChanges for PowerUsageSummaryActivity for rotation Add configChanges to avoid the battery settings main page is recreated while rotating the device to improve rotation performance, but we still need to investigate the PowerUsageAdvanced re-create case Bug: 271380711 Test: presubmit & manual Change-Id: I7629de89d9d23b08fc82369e1f9f79081e99625a --- AndroidManifest.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index d82fc942dff..dc519e95928 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -3073,7 +3073,8 @@ android:name="Settings$PowerUsageSummaryActivity" android:label="@string/power_usage_summary_title" android:exported="true" - android:icon="@drawable/ic_homepage_battery"> + android:icon="@drawable/ic_homepage_battery" + android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize"> From bcca5ebf1a5eae2dbbd59500e54cb38b7e494313 Mon Sep 17 00:00:00 2001 From: Weng Su Date: Tue, 7 Mar 2023 23:38:38 +0800 Subject: [PATCH 6/6] Add "Speed & compatibility" preference to Wi-Fi hotspot Settings - Show 4 speed types in summary - 2.4 Ghz - 5 Ghz - 2.4 and 5 GHz - 6 GHz Bug: 245258763 Test: manual test atest -c WifiTetherViewModelTest atest -c WifiHotspotRepositoryTest make RunSettingsRoboTests ROBOTEST_FILTER=WifiTetherSettingsTest Change-Id: I6deb41cb355b0ceb1f1fd2d84408a83b90433e7d --- res/values/config.xml | 3 + res/values/strings.xml | 10 + res/xml/wifi_tether_settings.xml | 6 + .../wifi/factory/WifiFeatureProvider.java | 13 ++ .../repository/WifiHotspotRepository.java | 215 ++++++++++++++++++ .../wifi/tether/WifiTetherSettings.java | 22 +- .../wifi/tether/WifiTetherViewModel.java | 103 +++++++++ .../wifi/tether/WifiTetherSettingsTest.java | 23 ++ .../repository/WifiHotspotRepositoryTest.java | 188 +++++++++++++++ .../wifi/tether/WifiTetherViewModelTest.java | 108 +++++++++ 10 files changed, 690 insertions(+), 1 deletion(-) create mode 100644 src/com/android/settings/wifi/tether/WifiTetherViewModel.java create mode 100644 tests/unit/src/com/android/settings/wifi/tether/WifiTetherViewModelTest.java diff --git a/res/values/config.xml b/res/values/config.xml index 43643d35266..ef786be59eb 100755 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -363,6 +363,9 @@ true + + false + true diff --git a/res/values/strings.xml b/res/values/strings.xml index 86bbb0614f9..a79b1d4d877 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -2015,6 +2015,16 @@ Helps other devices find this hotspot. Reduces hotspot connection speed. Helps other devices find this hotspot. Increases battery usage. + + Speed & compatibility + + 2.4 GHz / Any device can connect + + 5 GHz / Most devices can connect + + 6 GHz / Few devices can connect + + 2.4 and 5 GHz / Any device can connect Turning hotspot on\u2026 diff --git a/res/xml/wifi_tether_settings.xml b/res/xml/wifi_tether_settings.xml index 21f347bb021..1cd5f48ca0d 100644 --- a/res/xml/wifi_tether_settings.xml +++ b/res/xml/wifi_tether_settings.xml @@ -45,4 +45,10 @@ + + diff --git a/src/com/android/settings/wifi/factory/WifiFeatureProvider.java b/src/com/android/settings/wifi/factory/WifiFeatureProvider.java index 2f5f3ea88a3..433ff0c07ad 100644 --- a/src/com/android/settings/wifi/factory/WifiFeatureProvider.java +++ b/src/com/android/settings/wifi/factory/WifiFeatureProvider.java @@ -20,8 +20,13 @@ import android.content.Context; import android.net.wifi.WifiManager; 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.WifiTetherViewModel; + +import org.jetbrains.annotations.NotNull; /** * Wi-Fi Feature Provider @@ -55,5 +60,13 @@ public class WifiFeatureProvider { } return mWifiHotspotRepository; } + + /** + * Get WifiTetherViewModel + */ + public WifiTetherViewModel getWifiTetherViewModel(@NotNull ViewModelStoreOwner owner) { + return new ViewModelProvider(owner).get(WifiTetherViewModel.class); + } + } diff --git a/src/com/android/settings/wifi/repository/WifiHotspotRepository.java b/src/com/android/settings/wifi/repository/WifiHotspotRepository.java index b8a25dc84be..ee1ceea600e 100644 --- a/src/com/android/settings/wifi/repository/WifiHotspotRepository.java +++ b/src/com/android/settings/wifi/repository/WifiHotspotRepository.java @@ -16,13 +16,27 @@ package com.android.settings.wifi.repository; +import static android.net.wifi.SoftApConfiguration.BAND_2GHZ; +import static android.net.wifi.SoftApConfiguration.BAND_5GHZ; +import static android.net.wifi.SoftApConfiguration.BAND_6GHZ; +import static android.net.wifi.WifiAvailableChannel.OP_MODE_SAP; + 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.text.TextUtils; +import android.util.Log; import androidx.annotation.NonNull; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.Transformations; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.UUID; import java.util.function.Consumer; @@ -30,6 +44,36 @@ import java.util.function.Consumer; * Wi-Fi Hotspot Repository */ 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; + /** Wi-Fi hotspot band 2.4GHz and 5GHz. */ + public static final int BAND_2GHZ_5GHZ = BAND_2GHZ | BAND_5GHZ; + /** Wi-Fi hotspot band 2.4GHz and 5GHz and 6GHz. */ + public static final int BAND_2GHZ_5GHZ_6GHZ = BAND_2GHZ | BAND_5GHZ | BAND_6GHZ; + + /** Wi-Fi hotspot speed unknown. */ + public static final int SPEED_UNKNOWN = 0; + /** Wi-Fi hotspot speed 2.4GHz. */ + public static final int SPEED_2GHZ = 1; + /** Wi-Fi hotspot speed 5GHz. */ + public static final int SPEED_5GHZ = 2; + /** Wi-Fi hotspot speed 2.4GHz and 5GHz. */ + public static final int SPEED_2GHZ_5GHZ = 3; + /** Wi-Fi hotspot speed 6GHz. */ + public static final int SPEED_6GHZ = 4; + + protected static Map sSpeedMap = new HashMap<>(); + + static { + sSpeedMap.put(BAND_UNKNOWN, SPEED_UNKNOWN); + sSpeedMap.put(BAND_2GHZ, SPEED_2GHZ); + sSpeedMap.put(BAND_5GHZ, SPEED_5GHZ); + sSpeedMap.put(BAND_6GHZ, SPEED_6GHZ); + sSpeedMap.put(BAND_2GHZ_5GHZ, SPEED_2GHZ_5GHZ); + } protected final Context mAppContext; protected final WifiManager mWifiManager; @@ -37,6 +81,14 @@ public class WifiHotspotRepository { protected String mLastPassword; protected LastPasswordListener mLastPasswordListener = new LastPasswordListener(); + protected MutableLiveData mSpeedType; + + protected Boolean mIsDualBand; + protected Boolean mIs5gAvailable; + protected Boolean mIs6gAvailable; + protected String mCurrentCountryCode; + protected ActiveCountryCodeChangedCallback mActiveCountryCodeChangedCallback; + public WifiHotspotRepository(@NonNull Context appContext, @NonNull WifiManager wifiManager) { mAppContext = appContext; mWifiManager = wifiManager; @@ -73,4 +125,167 @@ public class WifiHotspotRepository { //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx return randomUUID.substring(0, 8) + randomUUID.substring(9, 13); } + + /** + * Sets the tethered Wi-Fi AP Configuration. + * + * @param config A valid SoftApConfiguration specifying the configuration of the SAP. + */ + public void setSoftApConfiguration(@NonNull SoftApConfiguration config) { + mWifiManager.setSoftApConfiguration(config); + refresh(); + } + + /** + * Refresh data from the SoftApConfiguration. + */ + public void refresh() { + updateSpeedType(); + } + + /** + * Set to auto refresh data. + * + * @param enabled whether the auto refresh should be enabled or not. + */ + public void setAutoRefresh(boolean enabled) { + if (enabled) { + startAutoRefresh(); + } else { + stopAutoRefresh(); + } + } + + /** + * Gets SpeedType LiveData + */ + public LiveData getSpeedType() { + if (mSpeedType == null) { + mSpeedType = new MutableLiveData<>(); + updateSpeedType(); + } + return Transformations.distinctUntilChanged(mSpeedType); + } + + protected void updateSpeedType() { + if (mSpeedType == null) { + return; + } + SoftApConfiguration config = mWifiManager.getSoftApConfiguration(); + if (config == null) { + mSpeedType.setValue(SPEED_UNKNOWN); + return; + } + int keyBand = config.getBand(); + logd("updateSpeedType(), getBand():" + keyBand); + if (!is5gAvailable()) { + keyBand &= ~BAND_5GHZ; + } + if (!is6gAvailable()) { + keyBand &= ~BAND_6GHZ; + } + if ((keyBand & BAND_6GHZ) != 0) { + keyBand = BAND_6GHZ; + } else if (isDualBand() && is5gAvailable()) { + keyBand = BAND_2GHZ_5GHZ; + } else if ((keyBand & BAND_5GHZ) != 0) { + keyBand = BAND_5GHZ; + } else if ((keyBand & BAND_2GHZ) != 0) { + keyBand = BAND_2GHZ; + } else { + keyBand = 0; + } + logd("updateSpeedType(), keyBand:" + keyBand); + mSpeedType.setValue(sSpeedMap.get(keyBand)); + } + + protected boolean isDualBand() { + if (mIsDualBand == null) { + mIsDualBand = mWifiManager.isBridgedApConcurrencySupported(); + logd("isDualBand():" + mIsDualBand); + } + return mIsDualBand; + } + + protected 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); + } + return mIs5gAvailable; + } + + protected boolean is6gAvailable() { + if (mIs6gAvailable == null) { + mIs6gAvailable = mWifiManager.is6GHzBandSupported() + && isBandAvailable(WifiScanner.WIFI_BAND_6_GHZ); + logd("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} + */ + protected boolean isBandAvailable(int band) { + List channels = mWifiManager.getUsableChannels(band, OP_MODE_SAP); + return (channels != null && channels.size() > 0); + } + + protected void purgeRefreshData() { + mIsDualBand = null; + mIs5gAvailable = null; + mIs6gAvailable = null; + } + + protected void startAutoRefresh() { + if (mActiveCountryCodeChangedCallback != null) { + return; + } + logd("startMonitorSoftApConfiguration()"); + mActiveCountryCodeChangedCallback = new ActiveCountryCodeChangedCallback(); + mWifiManager.registerActiveCountryCodeChangedCallback(mAppContext.getMainExecutor(), + mActiveCountryCodeChangedCallback); + } + + protected void stopAutoRefresh() { + if (mActiveCountryCodeChangedCallback == null) { + return; + } + logd("stopMonitorSoftApConfiguration()"); + mWifiManager.unregisterActiveCountryCodeChangedCallback(mActiveCountryCodeChangedCallback); + mActiveCountryCodeChangedCallback = null; + } + + protected class ActiveCountryCodeChangedCallback implements + WifiManager.ActiveCountryCodeChangedCallback { + @Override + public void onActiveCountryCodeChanged(String country) { + logd("onActiveCountryCodeChanged(), country:" + country); + mCurrentCountryCode = country; + purgeRefreshData(); + refresh(); + } + + @Override + public void onCountryCodeInactive() { + logd("onCountryCodeInactive()"); + mCurrentCountryCode = null; + purgeRefreshData(); + refresh(); + } + } + + private static void logd(String msg) { + if (DEBUG) { + Log.d(TAG, msg); + } + } } diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java index 47dba765e5f..3a3691a4b30 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherSettings.java +++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java @@ -34,11 +34,13 @@ import android.util.Log; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.core.FeatureFlags; import com.android.settings.dashboard.RestrictedDashboardFragment; +import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.widget.SettingsMainSwitchBar; import com.android.settings.wifi.WifiUtils; @@ -69,6 +71,8 @@ public class WifiTetherSettings extends RestrictedDashboardFragment @VisibleForTesting static final String KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY = WifiTetherMaximizeCompatibilityPreferenceController.PREF_KEY; + @VisibleForTesting + static final String KEY_WIFI_HOTSPOT_SPEED = "wifi_hotspot_speed"; private WifiTetherSwitchBarController mSwitchBarController; private WifiTetherSSIDPreferenceController mSSIDPreferenceController; @@ -84,6 +88,11 @@ public class WifiTetherSettings extends RestrictedDashboardFragment @VisibleForTesting TetherChangeReceiver mTetherChangeReceiver; + @VisibleForTesting + WifiTetherViewModel mWifiTetherViewModel; + @VisibleForTesting + Preference mWifiHotspotSpeed; + static { TETHER_STATE_CHANGE_FILTER = new IntentFilter(WIFI_AP_STATE_CHANGED_ACTION); } @@ -120,6 +129,13 @@ public class WifiTetherSettings extends RestrictedDashboardFragment setIfOnlyAvailableForAdmins(true); mUnavailable = isUiRestricted() || !mWifiRestriction.isHotspotAvailable(getContext()); + + mWifiTetherViewModel = FeatureFactory.getFactory(getContext()).getWifiFeatureProvider() + .getWifiTetherViewModel(this); + mWifiHotspotSpeed = findPreference(KEY_WIFI_HOTSPOT_SPEED); + if (mWifiHotspotSpeed != null && mWifiHotspotSpeed.isVisible()) { + mWifiTetherViewModel.getSpeedSummary().observe(this, this::onSpeedSummaryChanged); + } } @Override @@ -175,6 +191,7 @@ public class WifiTetherSettings extends RestrictedDashboardFragment // Handle the initial state after register the receiver. updateDisplayWithNewConfig(); } + mWifiTetherViewModel.refresh(); } @Override @@ -189,6 +206,9 @@ public class WifiTetherSettings extends RestrictedDashboardFragment } } + protected void onSpeedSummaryChanged(Integer summaryResId) { + mWifiHotspotSpeed.setSummary(summaryResId); + } @Override protected int getPreferenceScreenResId() { @@ -228,7 +248,7 @@ public class WifiTetherSettings extends RestrictedDashboardFragment mRestartWifiApAfterConfigChange = true; mSwitchBarController.stopTether(); } - mWifiManager.setSoftApConfiguration(config); + mWifiTetherViewModel.setSoftApConfiguration(config); } private SoftApConfiguration buildNewConfig() { diff --git a/src/com/android/settings/wifi/tether/WifiTetherViewModel.java b/src/com/android/settings/wifi/tether/WifiTetherViewModel.java new file mode 100644 index 00000000000..4abca023606 --- /dev/null +++ b/src/com/android/settings/wifi/tether/WifiTetherViewModel.java @@ -0,0 +1,103 @@ +/* + * 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.repository.WifiHotspotRepository.SPEED_UNKNOWN; + +import android.app.Application; +import android.net.wifi.SoftApConfiguration; + +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.Transformations; + +import com.android.settings.R; +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 ViewModel + */ +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); + } + + protected final WifiHotspotRepository mWifiHotspotRepository; + protected MutableLiveData mSpeedSummary; + + public WifiTetherViewModel(@NotNull Application application) { + super(application); + mWifiHotspotRepository = FeatureFactory.getFactory(application).getWifiFeatureProvider() + .getWifiHotspotRepository(); + mWifiHotspotRepository.setAutoRefresh(true); + } + + @Override + protected void onCleared() { + mWifiHotspotRepository.setAutoRefresh(false); + } + + /** + * Sets the tethered Wi-Fi AP Configuration. + * + * @param config A valid SoftApConfiguration specifying the configuration of the SAP. + */ + public void setSoftApConfiguration(SoftApConfiguration config) { + mWifiHotspotRepository.setSoftApConfiguration(config); + } + + /** + * Refresh data from the SoftApConfiguration. + */ + public void refresh() { + mWifiHotspotRepository.refresh(); + } + + /** + * Gets SpeedSummary LiveData + */ + public LiveData getSpeedSummary() { + if (mSpeedSummary == null) { + mSpeedSummary = new MutableLiveData<>(); + mWifiHotspotRepository.getSpeedType().observeForever(this::onSpeedTypeChanged); + } + return Transformations.distinctUntilChanged(mSpeedSummary); + } + + protected void onSpeedTypeChanged(Integer speedType) { + mSpeedSummary.setValue(sSpeedSummaryResMap.get(speedType)); + } +} 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 7f8a06d1521..582f792b788 100644 --- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java +++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java @@ -41,6 +41,8 @@ import android.util.FeatureFlagUtils; import android.widget.TextView; import androidx.fragment.app.FragmentActivity; +import androidx.lifecycle.ViewModelStoreOwner; +import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import androidx.test.core.app.ApplicationProvider; @@ -49,6 +51,8 @@ import com.android.settings.core.FeatureFlags; import com.android.settings.dashboard.RestrictedDashboardFragment; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowFragment; +import com.android.settings.wifi.factory.WifiFeatureProvider; +import com.android.settings.wifi.repository.WifiHotspotRepository; import org.junit.Before; import org.junit.Rule; @@ -90,6 +94,10 @@ public class WifiTetherSettingsTest { private PreferenceScreen mPreferenceScreen; @Mock private TextView mEmptyTextView; + @Mock + WifiTetherViewModel mWifiTetherViewModel; + @Mock + WifiHotspotRepository mWifiHotspotRepository; private WifiTetherSettings mWifiTetherSettings; @@ -106,6 +114,11 @@ public class WifiTetherSettingsTest { when(mWifiRestriction.isTetherAvailable(mContext)).thenReturn(true); when(mWifiRestriction.isHotspotAvailable(mContext)).thenReturn(true); + WifiFeatureProvider provider = FakeFeatureFactory.setupForTest().getWifiFeatureProvider(); + when(provider.getWifiHotspotRepository()).thenReturn(mWifiHotspotRepository); + when(provider.getWifiTetherViewModel(mock(ViewModelStoreOwner.class))) + .thenReturn(mWifiTetherViewModel); + mWifiTetherSettings = new WifiTetherSettings(mWifiRestriction); } @@ -142,6 +155,16 @@ public class WifiTetherSettingsTest { verify(mEmptyTextView).setText(anyInt()); } + @Test + public void onSpeedSummaryChanged_canNotShowWifiHotspot_returnFalse() { + int stringResId = R.string.wifi_hotspot_speed_6g_summary; + mWifiTetherSettings.mWifiHotspotSpeed = mock(Preference.class); + + mWifiTetherSettings.onSpeedSummaryChanged(stringResId); + + verify(mWifiTetherSettings.mWifiHotspotSpeed).setSummary(stringResId); + } + @Test public void createPreferenceControllers_getPreferenceControllersNotEmpty() { assertThat(WifiTetherSettings.SEARCH_INDEX_DATA_PROVIDER.getPreferenceControllers(mContext)) 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 58a9d1c87a2..52f02f5f14a 100644 --- a/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java +++ b/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java @@ -16,12 +16,21 @@ 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 com.android.settings.wifi.repository.WifiHotspotRepository.BAND_2GHZ_5GHZ; +import static com.android.settings.wifi.repository.WifiHotspotRepository.BAND_2GHZ_5GHZ_6GHZ; +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.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -30,6 +39,8 @@ import android.content.Context; import android.net.wifi.SoftApConfiguration; import android.net.wifi.WifiManager; +import androidx.lifecycle.MutableLiveData; +import androidx.test.annotation.UiThreadTest; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -46,6 +57,10 @@ import org.mockito.junit.MockitoRule; public class WifiHotspotRepositoryTest { static final String WIFI_SSID = "wifi_ssid"; static final String WIFI_PASSWORD = "wifi_password"; + static final String WIFI_CURRENT_COUNTRY_CODE = "US"; + + static final int WIFI_5GHZ_BAND_PREFERRED = BAND_2GHZ_5GHZ; + static final int WIFI_6GHZ_BAND_PREFERRED = BAND_2GHZ_5GHZ_6GHZ; @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); @@ -53,6 +68,8 @@ public class WifiHotspotRepositoryTest { Context mContext = ApplicationProvider.getApplicationContext(); @Mock WifiManager mWifiManager; + @Mock + MutableLiveData mSpeedType; WifiHotspotRepository mWifiHotspotRepository; SoftApConfiguration mSoftApConfiguration; @@ -60,6 +77,10 @@ public class WifiHotspotRepositoryTest { @Before public void setUp() { mWifiHotspotRepository = new WifiHotspotRepository(mContext, mWifiManager); + mWifiHotspotRepository.mCurrentCountryCode = WIFI_CURRENT_COUNTRY_CODE; + mWifiHotspotRepository.mIsDualBand = true; + mWifiHotspotRepository.mIs5gAvailable = true; + mWifiHotspotRepository.mIs6gAvailable = true; } @Test @@ -105,4 +126,171 @@ public class WifiHotspotRepositoryTest { assertThat(password).isNotEqualTo(WIFI_PASSWORD); assertThat(password.length()).isNotEqualTo(0); } + + @Test + public void setSoftApConfiguration_setConfigByWifiManager() { + SoftApConfiguration config = new SoftApConfiguration.Builder().build(); + + mWifiHotspotRepository.setSoftApConfiguration(config); + + verify(mWifiManager).setSoftApConfiguration(config); + } + + @Test + public void refresh_liveDataNotUsed_doNothing() { + // If LiveData is not used then it's null. + mWifiHotspotRepository.mSpeedType = null; + + mWifiHotspotRepository.refresh(); + + verify(mWifiManager, never()).getSoftApConfiguration(); + } + + @Test + public void refresh_liveDataIsUsed_getConfigAndUpdateLiveData() { + // If LiveData is used then it's not null. + mWifiHotspotRepository.mSpeedType = mSpeedType; + + mWifiHotspotRepository.refresh(); + + verify(mWifiManager).getSoftApConfiguration(); + verify(mSpeedType).setValue(anyInt()); + } + + @Test + public void setAutoRefresh_setEnabled_registerCallback() { + mWifiHotspotRepository.mActiveCountryCodeChangedCallback = null; + + mWifiHotspotRepository.setAutoRefresh(true); + + verify(mWifiManager).registerActiveCountryCodeChangedCallback(any(), any()); + } + + @Test + public void setAutoRefresh_setDisabled_registerCallback() { + mWifiHotspotRepository.setAutoRefresh(true); + + mWifiHotspotRepository.setAutoRefresh(false); + + verify(mWifiManager).unregisterActiveCountryCodeChangedCallback(any()); + } + + @Test + @UiThreadTest + public void getSpeedType_shouldNotReturnNull() { + // If LiveData is not used then it's null. + mWifiHotspotRepository.mSpeedType = null; + SoftApConfiguration config = new SoftApConfiguration.Builder().setBand(BAND_2GHZ).build(); + when(mWifiManager.getSoftApConfiguration()).thenReturn(config); + + assertThat(mWifiHotspotRepository.getSpeedType()).isNotNull(); + } + + @Test + public void updateSpeedType_singleBand2g_get2gSpeedType() { + mWifiHotspotRepository.mIsDualBand = false; + SoftApConfiguration config = new SoftApConfiguration.Builder().setBand(BAND_2GHZ).build(); + when(mWifiManager.getSoftApConfiguration()).thenReturn(config); + mWifiHotspotRepository.mSpeedType = mSpeedType; + + mWifiHotspotRepository.updateSpeedType(); + + verify(mSpeedType).setValue(SPEED_2GHZ); + } + + @Test + public void updateSpeedType_singleBand5gPreferred_get5gSpeedType() { + mWifiHotspotRepository.mIsDualBand = false; + 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_5GHZ); + } + + @Test + public void updateSpeedType_singleBand5gPreferredBut5gUnavailable_get2gSpeedType() { + mWifiHotspotRepository.mIsDualBand = false; + mWifiHotspotRepository.mIs5gAvailable = false; + 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 updateSpeedType_singleBand6gPreferred_get6gSpeedType() { + mWifiHotspotRepository.mIsDualBand = false; + SoftApConfiguration config = new SoftApConfiguration.Builder() + .setBand(WIFI_6GHZ_BAND_PREFERRED).build(); + when(mWifiManager.getSoftApConfiguration()).thenReturn(config); + mWifiHotspotRepository.mSpeedType = mSpeedType; + + mWifiHotspotRepository.updateSpeedType(); + + verify(mSpeedType).setValue(SPEED_6GHZ); + } + + @Test + public void updateSpeedType_singleBand6gPreferredBut6gUnavailable_get5gSpeedType() { + mWifiHotspotRepository.mIsDualBand = false; + mWifiHotspotRepository.mIs6gAvailable = false; + SoftApConfiguration config = new SoftApConfiguration.Builder() + .setBand(WIFI_6GHZ_BAND_PREFERRED).build(); + when(mWifiManager.getSoftApConfiguration()).thenReturn(config); + mWifiHotspotRepository.mSpeedType = mSpeedType; + + mWifiHotspotRepository.updateSpeedType(); + + verify(mSpeedType).setValue(SPEED_5GHZ); + } + + @Test + public void updateSpeedType_singleBand6gPreferredBut5gAnd6gUnavailable_get2gSpeedType() { + mWifiHotspotRepository.mIsDualBand = false; + mWifiHotspotRepository.mIs5gAvailable = false; + mWifiHotspotRepository.mIs6gAvailable = false; + SoftApConfiguration config = new SoftApConfiguration.Builder() + .setBand(WIFI_6GHZ_BAND_PREFERRED).build(); + when(mWifiManager.getSoftApConfiguration()).thenReturn(config); + mWifiHotspotRepository.mSpeedType = mSpeedType; + + mWifiHotspotRepository.updateSpeedType(); + + verify(mSpeedType).setValue(SPEED_2GHZ); + } + + @Test + public void updateSpeedType_dualBand2gAnd5g_get2gAnd5gSpeedType() { + mWifiHotspotRepository.mIsDualBand = true; + 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_5GHZ); + } + + @Test + public void updateSpeedType_dualBand2gAnd5gBut5gUnavailable_get2gSpeedType() { + mWifiHotspotRepository.mIsDualBand = true; + mWifiHotspotRepository.mIs5gAvailable = false; + 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); + } } diff --git a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherViewModelTest.java b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherViewModelTest.java new file mode 100644 index 00000000000..6724dd5178c --- /dev/null +++ b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherViewModelTest.java @@ -0,0 +1,108 @@ +/* + * 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.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Application; +import android.net.wifi.SoftApConfiguration; + +import androidx.lifecycle.MutableLiveData; +import androidx.test.annotation.UiThreadTest; +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.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.concurrent.Executor; + +@RunWith(AndroidJUnit4.class) +public class WifiTetherViewModelTest { + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Mock + Application mApplication; + @Mock + Executor mExecutor; + @Mock + WifiHotspotRepository mWifiHotspotRepository; + @Mock + MutableLiveData mSpeedType; + + WifiTetherViewModel mViewModel; + + @Before + public void setUp() { + when(mApplication.getMainExecutor()).thenReturn(mExecutor); + + FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest(); + when(featureFactory.getWifiFeatureProvider().getWifiHotspotRepository()) + .thenReturn(mWifiHotspotRepository); + when(mWifiHotspotRepository.getSpeedType()).thenReturn(mSpeedType); + + mViewModel = new WifiTetherViewModel(mApplication); + } + + @Test + public void constructor_setAutoRefreshTrue() { + verify(mWifiHotspotRepository).setAutoRefresh(true); + } + + @Test + public void onCleared_setAutoRefreshFalse() { + mViewModel.onCleared(); + + verify(mWifiHotspotRepository).setAutoRefresh(false); + } + + @Test + public void setSoftApConfiguration_setConfigByRepository() { + SoftApConfiguration config = new SoftApConfiguration.Builder().build(); + + mViewModel.setSoftApConfiguration(config); + + verify(mWifiHotspotRepository).setSoftApConfiguration(config); + } + + @Test + public void refresh_refreshByRepository() { + mViewModel.refresh(); + + verify(mWifiHotspotRepository).refresh(); + } + + @Test + @UiThreadTest + public void getSpeedSummary_returnNotNull() { + mViewModel.mSpeedSummary = null; + + mViewModel.getSpeedSummary(); + + assertThat(mViewModel.mSpeedSummary).isNotNull(); + } +}