From 27b3821313a802de291c423ce61a303622524be3 Mon Sep 17 00:00:00 2001 From: Weng Su Date: Tue, 15 Aug 2023 22:22:23 +0800 Subject: [PATCH] Add Instant hotspot preference - Add Instant hotspot preference to Wi-Fi hotspot settings - Wait onServiceConnected callback and then getSettingsState - Use the PendingIntent provided by SharedConnectivitySettingsState to launch Instant hotspot settings Bug: 268550769 Test: manual test atest -c WifiTetherSettingsTest atest -c WifiTetherViewModelTest \ SharedConnectivityRepositoryTest Merged-In: I343599e6127d9b1cb4af661dcc80a8683589c7b8 Change-Id: I343599e6127d9b1cb4af661dcc80a8683589c7b8 --- res/values/strings.xml | 7 + res/xml/wifi_tether_settings.xml | 6 + .../wifi/factory/WifiFeatureProvider.java | 13 ++ .../SharedConnectivityRepository.java | 184 ++++++++++++++++++ .../wifi/tether/WifiTetherSettings.java | 33 ++++ .../wifi/tether/WifiTetherViewModel.java | 70 ++++++- .../wifi/tether/WifiTetherSettingsTest.java | 51 +++++ .../SharedConnectivityRepositoryTest.java | 139 +++++++++++++ .../wifi/tether/WifiTetherViewModelTest.java | 83 +++++++- 9 files changed, 582 insertions(+), 4 deletions(-) create mode 100644 src/com/android/settings/wifi/repository/SharedConnectivityRepository.java create mode 100644 tests/unit/src/com/android/settings/wifi/repository/SharedConnectivityRepositoryTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 9a4812f1194..468f3fa79ec 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -2098,6 +2098,13 @@ Security settings may change if you change the hotspot’s frequency + + Instant hotspot + + On + + Off + Turning hotspot on\u2026 diff --git a/res/xml/wifi_tether_settings.xml b/res/xml/wifi_tether_settings.xml index a85d9ea94db..b8b810fd462 100644 --- a/res/xml/wifi_tether_settings.xml +++ b/res/xml/wifi_tether_settings.xml @@ -59,4 +59,10 @@ 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 c61cf51074a..3f0d62f31b0 100644 --- a/src/com/android/settings/wifi/factory/WifiFeatureProvider.java +++ b/src/com/android/settings/wifi/factory/WifiFeatureProvider.java @@ -26,6 +26,7 @@ import androidx.annotation.NonNull; import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelStoreOwner; +import com.android.settings.wifi.repository.SharedConnectivityRepository; import com.android.settings.wifi.repository.WifiHotspotRepository; import com.android.settings.wifi.tether.WifiHotspotSecurityViewModel; import com.android.settings.wifi.tether.WifiHotspotSpeedViewModel; @@ -44,6 +45,7 @@ public class WifiFeatureProvider { private TetheringManager mTetheringManager; private WifiVerboseLogging mWifiVerboseLogging; private WifiHotspotRepository mWifiHotspotRepository; + private SharedConnectivityRepository mSharedConnectivityRepository; public WifiFeatureProvider(@NonNull Context appContext) { mAppContext = appContext; @@ -92,6 +94,17 @@ public class WifiFeatureProvider { return mWifiHotspotRepository; } + /** + * Gets SharedConnectivityRepository + */ + public SharedConnectivityRepository getSharedConnectivityRepository() { + if (mSharedConnectivityRepository == null) { + mSharedConnectivityRepository = new SharedConnectivityRepository(mAppContext); + verboseLog(TAG, "getSharedConnectivityRepository():" + mSharedConnectivityRepository); + } + return mSharedConnectivityRepository; + } + /** * Gets WifiTetherViewModel */ diff --git a/src/com/android/settings/wifi/repository/SharedConnectivityRepository.java b/src/com/android/settings/wifi/repository/SharedConnectivityRepository.java new file mode 100644 index 00000000000..5daa5f35fd6 --- /dev/null +++ b/src/com/android/settings/wifi/repository/SharedConnectivityRepository.java @@ -0,0 +1,184 @@ +/* + * 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.repository; + +import android.app.PendingIntent; +import android.content.Context; +import android.net.wifi.sharedconnectivity.app.HotspotNetwork; +import android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus; +import android.net.wifi.sharedconnectivity.app.KnownNetwork; +import android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus; +import android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback; +import android.net.wifi.sharedconnectivity.app.SharedConnectivityManager; +import android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState; +import android.os.HandlerThread; +import android.provider.DeviceConfig; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; +import androidx.annotation.WorkerThread; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; + +import com.android.settings.overlay.FeatureFactory; + +import java.util.List; +import java.util.concurrent.Executor; + +/** + * Shared Connectivity Repository for {@link SharedConnectivityManager} + */ +public class SharedConnectivityRepository { + private static final String TAG = "SharedConnectivityRepository"; + private static final String DEVICE_CONFIG_NAMESPACE = "wifi"; + private static final String DEVICE_CONFIG_KEY = "shared_connectivity_enabled"; + + private Context mAppContext; + private SharedConnectivityManager mManager; + private ClientCallback mClientCallback = new ClientCallback(); + private HandlerThread mWorkerThread = new HandlerThread(TAG); + private Executor mWorkerExecutor = cmd -> mWorkerThread.getThreadHandler().post(cmd); + private Runnable mLaunchSettingsRunnable = () -> handleLaunchSettings(); + @VisibleForTesting + MutableLiveData mSettingsState = new MutableLiveData<>(); + + public SharedConnectivityRepository(@NonNull Context appContext) { + this(appContext, + DeviceConfig.getBoolean(DEVICE_CONFIG_NAMESPACE, DEVICE_CONFIG_KEY, false)); + } + + @VisibleForTesting + SharedConnectivityRepository(@NonNull Context appContext, boolean isConfigEnabled) { + mAppContext = appContext; + if (!isConfigEnabled) { + return; + } + mManager = mAppContext.getSystemService(SharedConnectivityManager.class); + if (mManager == null) { + Log.w(TAG, "Failed to get SharedConnectivityManager"); + return; + } + mWorkerThread.start(); + mManager.registerCallback(mWorkerExecutor, mClientCallback); + } + + /** + * Return whether Wi-Fi Shared Connectivity service is available or not. + * + * @return {@code true} if Wi-Fi Shared Connectivity service is available + */ + public boolean isServiceAvailable() { + return mManager != null; + } + + /** + * Gets SharedConnectivitySettingsState LiveData + */ + public LiveData getSettingsState() { + return mSettingsState; + } + + /** + * Launch Instant Hotspot Settings + */ + public void launchSettings() { + mWorkerExecutor.execute(mLaunchSettingsRunnable); + } + + @WorkerThread + @VisibleForTesting + void handleLaunchSettings() { + if (mManager == null) { + return; + } + SharedConnectivitySettingsState state = mManager.getSettingsState(); + log("handleLaunchSettings(), state:" + state); + if (state == null) { + Log.e(TAG, "No SettingsState to launch Instant Hotspot settings"); + return; + } + PendingIntent intent = state.getInstantTetherSettingsPendingIntent(); + if (intent == null) { + Log.e(TAG, "No PendingIntent to launch Instant Hotspot settings"); + return; + } + sendSettingsIntent(intent); + } + + @WorkerThread + @VisibleForTesting + void sendSettingsIntent(@NonNull PendingIntent intent) { + try { + log("sendSettingsIntent(), sent intent:" + intent); + intent.send(); + } catch (PendingIntent.CanceledException e) { + Log.e(TAG, "Failed to launch Instant Hotspot settings", e); + } + } + + @WorkerThread + class ClientCallback implements SharedConnectivityClientCallback { + + @Override + public void onHotspotNetworkConnectionStatusChanged(HotspotNetworkConnectionStatus status) { + log("onHotspotNetworkConnectionStatusChanged(), status:" + status); + } + + @Override + public void onHotspotNetworksUpdated(List networks) { + log("onHotspotNetworksUpdated(), networks:" + networks); + } + + @Override + public void onKnownNetworkConnectionStatusChanged(KnownNetworkConnectionStatus status) { + log("onKnownNetworkConnectionStatusChanged(), status:" + status); + } + + @Override + public void onKnownNetworksUpdated(List networks) { + log("onKnownNetworksUpdated(), networks:" + networks); + } + + @Override + public void onRegisterCallbackFailed(Exception e) { + Log.e(TAG, "onRegisterCallbackFailed(), e:" + e); + } + + @Override + public void onServiceConnected() { + SharedConnectivitySettingsState state = mManager.getSettingsState(); + Log.d(TAG, "onServiceConnected(), Manager#getSettingsState:" + state); + mSettingsState.postValue(state); + } + + @Override + public void onServiceDisconnected() { + log("onServiceDisconnected()"); + } + + @Override + public void onSharedConnectivitySettingsChanged(SharedConnectivitySettingsState state) { + Log.d(TAG, "onSharedConnectivitySettingsChanged(), state:" + state); + mSettingsState.postValue(state); + } + } + + private void log(String msg) { + FeatureFactory.getFactory(mAppContext).getWifiFeatureProvider().verboseLog(TAG, msg); + } +} diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java index d8c39081c94..fa897b789ed 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherSettings.java +++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java @@ -76,6 +76,8 @@ public class WifiTetherSettings extends RestrictedDashboardFragment static final String KEY_WIFI_HOTSPOT_SECURITY = "wifi_hotspot_security"; @VisibleForTesting static final String KEY_WIFI_HOTSPOT_SPEED = "wifi_hotspot_speed"; + @VisibleForTesting + static final String KEY_INSTANT_HOTSPOT = "wifi_hotspot_instant"; @VisibleForTesting SettingsMainSwitchBar mMainSwitchBar; @@ -103,6 +105,8 @@ public class WifiTetherSettings extends RestrictedDashboardFragment Preference mWifiHotspotSecurity; @VisibleForTesting Preference mWifiHotspotSpeed; + @VisibleForTesting + Preference mInstantHotspot; static { TETHER_STATE_CHANGE_FILTER = new IntentFilter(WIFI_AP_STATE_CHANGED_ACTION); @@ -148,6 +152,7 @@ public class WifiTetherSettings extends RestrictedDashboardFragment .getWifiTetherViewModel(this); if (mWifiTetherViewModel != null) { setupSpeedFeature(mWifiTetherViewModel.isSpeedFeatureAvailable()); + setupInstantHotspot(mWifiTetherViewModel.isInstantHotspotFeatureAvailable()); mWifiTetherViewModel.getRestarting().observe(this, this::onRestartingChanged); } } @@ -167,6 +172,24 @@ public class WifiTetherSettings extends RestrictedDashboardFragment } } + @VisibleForTesting + void setupInstantHotspot(boolean isFeatureAvailable) { + if (!isFeatureAvailable) { + return; + } + mInstantHotspot = findPreference(KEY_INSTANT_HOTSPOT); + if (mInstantHotspot == null) { + Log.e(TAG, "Failed to find Instant Hotspot preference:" + KEY_INSTANT_HOTSPOT); + return; + } + mWifiTetherViewModel.getInstantHotspotSummary() + .observe(this, this::onInstantHotspotChanged); + mInstantHotspot.setOnPreferenceClickListener(p -> { + mWifiTetherViewModel.launchInstantHotspotSettings(); + return true; + }); + } + @Override public void onAttach(Context context) { super.onAttach(context); @@ -278,6 +301,16 @@ public class WifiTetherSettings extends RestrictedDashboardFragment setLoading(restarting, false); } + @VisibleForTesting + void onInstantHotspotChanged(String summary) { + if (summary == null) { + mInstantHotspot.setVisible(false); + return; + } + mInstantHotspot.setVisible(true); + mInstantHotspot.setSummary(summary); + } + @VisibleForTesting SoftApConfiguration buildNewConfig() { SoftApConfiguration currentConfig = mWifiTetherViewModel.getSoftApConfiguration(); diff --git a/src/com/android/settings/wifi/tether/WifiTetherViewModel.java b/src/com/android/settings/wifi/tether/WifiTetherViewModel.java index fb2160fbed6..b0a18a85c89 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherViewModel.java +++ b/src/com/android/settings/wifi/tether/WifiTetherViewModel.java @@ -28,7 +28,9 @@ import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_6 import android.app.Application; import android.net.wifi.SoftApConfiguration; +import android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState; +import androidx.annotation.VisibleForTesting; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; @@ -36,6 +38,8 @@ import androidx.lifecycle.Observer; import com.android.settings.R; import com.android.settings.overlay.FeatureFactory; +import com.android.settings.wifi.factory.WifiFeatureProvider; +import com.android.settings.wifi.repository.SharedConnectivityRepository; import com.android.settings.wifi.repository.WifiHotspotRepository; import org.jetbrains.annotations.NotNull; @@ -48,6 +52,8 @@ import java.util.Map; */ public class WifiTetherViewModel extends AndroidViewModel { private static final String TAG = "WifiTetherViewModel"; + static final int RES_INSTANT_HOTSPOT_SUMMARY_ON = R.string.wifi_hotspot_instant_summary_on; + static final int RES_INSTANT_HOTSPOT_SUMMARY_OFF = R.string.wifi_hotspot_instant_summary_off; static Map sSecuritySummaryResMap = new HashMap<>(); @@ -75,10 +81,23 @@ public class WifiTetherViewModel extends AndroidViewModel { protected final Observer mSecurityTypeObserver = st -> onSecurityTypeChanged(st); protected final Observer mSpeedTypeObserver = st -> onSpeedTypeChanged(st); + private SharedConnectivityRepository mSharedConnectivityRepository; + @VisibleForTesting + MutableLiveData mInstantHotspotSummary = new MutableLiveData<>(); + @VisibleForTesting + Observer mInstantHotspotStateObserver = + state -> onInstantHotspotStateChanged(state); + public WifiTetherViewModel(@NotNull Application application) { super(application); - mWifiHotspotRepository = FeatureFactory.getFactory(application).getWifiFeatureProvider() - .getWifiHotspotRepository(); + WifiFeatureProvider featureProvider = FeatureFactory.getFactory(application) + .getWifiFeatureProvider(); + mWifiHotspotRepository = featureProvider.getWifiHotspotRepository(); + mSharedConnectivityRepository = featureProvider.getSharedConnectivityRepository(); + if (mSharedConnectivityRepository.isServiceAvailable()) { + mSharedConnectivityRepository.getSettingsState() + .observeForever(mInstantHotspotStateObserver); + } } @Override @@ -89,6 +108,10 @@ public class WifiTetherViewModel extends AndroidViewModel { if (mSpeedSummary != null) { mWifiHotspotRepository.getSpeedType().removeObserver(mSpeedTypeObserver); } + if (mSharedConnectivityRepository.isServiceAvailable()) { + mSharedConnectivityRepository.getSettingsState() + .removeObserver(mInstantHotspotStateObserver); + } } /** @@ -169,4 +192,47 @@ public class WifiTetherViewModel extends AndroidViewModel { public LiveData getRestarting() { return mWifiHotspotRepository.getRestarting(); } + + /** + * Return whether Wi-Fi Instant Hotspot feature is available or not. + * + * @return {@code true} if Wi-Fi Instant Hotspot feature is available + */ + public boolean isInstantHotspotFeatureAvailable() { + return mSharedConnectivityRepository.isServiceAvailable(); + } + + /** + * Gets InstantHotspotSummary + */ + public LiveData getInstantHotspotSummary() { + return mInstantHotspotSummary; + } + + @VisibleForTesting + void onInstantHotspotStateChanged(SharedConnectivitySettingsState state) { + log("onInstantHotspotStateChanged(), state:" + state); + if (state == null) { + mInstantHotspotSummary.setValue(null); + return; + } + mInstantHotspotSummary.setValue(getInstantHotspotSummary(state.isInstantTetherEnabled())); + } + + private String getInstantHotspotSummary(boolean enabled) { + return getApplication().getString( + enabled ? RES_INSTANT_HOTSPOT_SUMMARY_ON : RES_INSTANT_HOTSPOT_SUMMARY_OFF); + } + + /** + * Launch Instant Hotspot Settings + */ + public void launchInstantHotspotSettings() { + mSharedConnectivityRepository.launchSettings(); + } + + private void log(String msg) { + FeatureFactory.getFactory(getApplication().getApplicationContext()).getWifiFeatureProvider() + .verboseLog(TAG, msg); + } } 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 75d49feafe4..e67717d190d 100644 --- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java +++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java @@ -23,6 +23,7 @@ import static android.view.View.VISIBLE; import static com.android.settings.wifi.WifiUtils.setCanShowWifiHotspotCached; import static com.android.settings.wifi.repository.WifiHotspotRepository.BAND_2GHZ_5GHZ_6GHZ; +import static com.android.settings.wifi.tether.WifiTetherSettings.KEY_INSTANT_HOTSPOT; import static com.android.settings.wifi.tether.WifiTetherSettings.KEY_WIFI_HOTSPOT_SECURITY; import static com.android.settings.wifi.tether.WifiTetherSettings.KEY_WIFI_HOTSPOT_SPEED; @@ -90,6 +91,7 @@ public class WifiTetherSettingsTest { private static final String[] WIFI_REGEXS = {"wifi_regexs"}; private static final String SSID = "ssid"; private static final String PASSWORD = "password"; + private static final String SUMMARY = "summary"; @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); @@ -133,6 +135,10 @@ public class WifiTetherSettingsTest { private WifiTetherAutoOffPreferenceController mWifiTetherAutoOffPreferenceController; @Mock private WifiTetherMaximizeCompatibilityPreferenceController mMaxCompatibilityPrefController; + @Mock + private Preference mInstantHotspot; + @Mock + private LiveData mInstantHotspotSummary; private WifiTetherSettings mSettings; @@ -155,8 +161,10 @@ public class WifiTetherSettingsTest { when(provider.getWifiTetherViewModel(mock(ViewModelStoreOwner.class))) .thenReturn(mWifiTetherViewModel); when(mWifiTetherViewModel.isSpeedFeatureAvailable()).thenReturn(false); + when(mWifiTetherViewModel.isInstantHotspotFeatureAvailable()).thenReturn(true); when(mWifiTetherViewModel.getSecuritySummary()).thenReturn(mSecuritySummary); when(mWifiTetherViewModel.getSpeedSummary()).thenReturn(mSpeedSummary); + when(mWifiTetherViewModel.getInstantHotspotSummary()).thenReturn(mInstantHotspotSummary); mSettings = spy(new WifiTetherSettings(mWifiRestriction)); mSettings.mMainSwitchBar = mMainSwitchBar; @@ -172,6 +180,8 @@ public class WifiTetherSettingsTest { mSettings.mWifiTetherViewModel = mWifiTetherViewModel; when(mSettings.findPreference(KEY_WIFI_HOTSPOT_SECURITY)).thenReturn(mWifiHotspotSecurity); when(mSettings.findPreference(KEY_WIFI_HOTSPOT_SPEED)).thenReturn(mWifiHotspotSpeed); + when(mSettings.findPreference(KEY_INSTANT_HOTSPOT)).thenReturn(mInstantHotspot); + mSettings.mInstantHotspot = mInstantHotspot; } @Test @@ -372,6 +382,47 @@ public class WifiTetherSettingsTest { verify(mSettings).setLoading(true, false); } + @Test + public void setupInstantHotspot_featureNotAvailable_doNothing() { + mSettings.setupInstantHotspot(false /* isFeatureAvailable */); + + verify(mSettings, never()).findPreference(KEY_INSTANT_HOTSPOT); + verify(mWifiTetherViewModel, never()).getInstantHotspotSummary(); + } + + @Test + public void setupInstantHotspot_featureAvailable_doSetup() { + when(mWifiTetherViewModel.isInstantHotspotFeatureAvailable()).thenReturn(true); + + mSettings.setupInstantHotspot(true /* isFeatureAvailable */); + + verify(mSettings).findPreference(KEY_INSTANT_HOTSPOT); + verify(mInstantHotspotSummary).observe(any(), any()); + verify(mInstantHotspot).setOnPreferenceClickListener(any()); + } + + @Test + public void onInstantHotspotChanged_nullRecord_setVisibleFalse() { + mSettings.onInstantHotspotChanged(null); + + verify(mInstantHotspot).setVisible(false); + } + + @Test + public void onInstantHotspotChanged_summaryNull_setVisibleFalse() { + mSettings.onInstantHotspotChanged(null); + + verify(mInstantHotspot).setVisible(false); + } + + @Test + public void onInstantHotspotChanged_summaryNotNull_setVisibleAndSummary() { + mSettings.onInstantHotspotChanged(SUMMARY); + + verify(mInstantHotspot).setVisible(true); + verify(mInstantHotspot).setSummary(SUMMARY); + } + @Test public void buildNewConfig_speedFeatureIsAvailableAndPasswordChanged_bandShouldNotBeLost() { String newPassword = "new" + PASSWORD; diff --git a/tests/unit/src/com/android/settings/wifi/repository/SharedConnectivityRepositoryTest.java b/tests/unit/src/com/android/settings/wifi/repository/SharedConnectivityRepositoryTest.java new file mode 100644 index 00000000000..4aef5520f5c --- /dev/null +++ b/tests/unit/src/com/android/settings/wifi/repository/SharedConnectivityRepositoryTest.java @@ -0,0 +1,139 @@ +/* + * 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.repository; + +import static android.app.PendingIntent.FLAG_IMMUTABLE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.net.wifi.sharedconnectivity.app.SharedConnectivityManager; +import android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState; + +import androidx.test.core.app.ApplicationProvider; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +public class SharedConnectivityRepositoryTest { + + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Spy + private Context mContext = ApplicationProvider.getApplicationContext(); + @Mock + private SharedConnectivityManager mManager; + + private SharedConnectivityRepository mRepository; + private PendingIntent mIntent = PendingIntent + .getActivity(mContext, 0, new Intent("test"), FLAG_IMMUTABLE); + private SharedConnectivitySettingsState mState = new SharedConnectivitySettingsState.Builder() + .setInstantTetherSettingsPendingIntent(mIntent).build(); + + @Before + public void setUp() { + when(mContext.getSystemService(SharedConnectivityManager.class)).thenReturn(mManager); + when(mManager.getSettingsState()).thenReturn(mState); + + mRepository = spy(new SharedConnectivityRepository(mContext, true /* isConfigEnabled */)); + } + + @Test + public void constructor_configEnabled_registerCallback() { + verify(mManager).registerCallback(any(), any()); + } + + @Test + public void constructor_configNotEnabled_doNotRegisterCallback() { + SharedConnectivityManager manager = mock(SharedConnectivityManager.class); + when(mContext.getSystemService(SharedConnectivityManager.class)).thenReturn(manager); + + mRepository = new SharedConnectivityRepository(mContext, false /* isConfigEnabled */); + + verify(manager, never()).registerCallback(any(), any()); + } + + @Test + public void isServiceAvailable_configEnabled_returnTrue() { + mRepository = new SharedConnectivityRepository(mContext, true /* isConfigEnabled */); + + assertThat(mRepository.isServiceAvailable()).isTrue(); + } + + @Test + public void isServiceAvailable_configNotEnabled_returnFalse() { + mRepository = new SharedConnectivityRepository(mContext, false /* isConfigEnabled */); + + assertThat(mRepository.isServiceAvailable()).isFalse(); + } + + @Test + public void getSettingsState_isNotNull() { + assertThat(mRepository.getSettingsState()).isNotNull(); + } + + @Test + public void handleLaunchSettings_managerNull_doNothing() { + when(mContext.getSystemService(SharedConnectivityManager.class)).thenReturn(null); + mRepository = spy(new SharedConnectivityRepository(mContext, true /* isConfigEnabled */)); + + mRepository.handleLaunchSettings(); + + verify(mRepository, never()).sendSettingsIntent(mIntent); + } + + @Test + public void handleLaunchSettings_stageNull_doNothing() { + when(mManager.getSettingsState()).thenReturn(null); + + mRepository.handleLaunchSettings(); + + verify(mRepository, never()).sendSettingsIntent(mIntent); + } + + @Test + public void handleLaunchSettings_intentNull_doNothing() { + mState = new SharedConnectivitySettingsState.Builder() + .setInstantTetherSettingsPendingIntent(null).build(); + when(mManager.getSettingsState()).thenReturn(mState); + + mRepository.handleLaunchSettings(); + + verify(mRepository, never()).sendSettingsIntent(mIntent); + } + + @Test + public void handleLaunchSettings_allReady_sendSettingsIntent() { + mRepository.handleLaunchSettings(); + + verify(mRepository).sendSettingsIntent(mIntent); + } +} diff --git a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherViewModelTest.java b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherViewModelTest.java index af1f62b1a16..1c1473f0da2 100644 --- a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherViewModelTest.java +++ b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherViewModelTest.java @@ -16,6 +16,9 @@ package com.android.settings.wifi.tether; +import static com.android.settings.wifi.tether.WifiTetherViewModel.RES_INSTANT_HOTSPOT_SUMMARY_OFF; +import static com.android.settings.wifi.tether.WifiTetherViewModel.RES_INSTANT_HOTSPOT_SUMMARY_ON; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.verify; @@ -23,12 +26,15 @@ import static org.mockito.Mockito.when; import android.app.Application; import android.net.wifi.SoftApConfiguration; +import android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState; 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.SharedConnectivityRepository; import com.android.settings.wifi.repository.WifiHotspotRepository; import org.junit.Before; @@ -36,6 +42,7 @@ 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; @@ -45,8 +52,8 @@ import java.util.concurrent.Executor; public class WifiTetherViewModelTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); - @Mock - Application mApplication; + @Spy + Application mApplication = ApplicationProvider.getApplicationContext(); @Mock Executor mExecutor; @Mock @@ -57,6 +64,12 @@ public class WifiTetherViewModelTest { MutableLiveData mSpeedType; @Mock private MutableLiveData mRestarting; + @Mock + private SharedConnectivityRepository mSharedConnectivityRepository; + @Mock + private MutableLiveData mSettingsState; + @Mock + private MutableLiveData mInstantHotspotSummary; WifiTetherViewModel mViewModel; @@ -70,8 +83,18 @@ public class WifiTetherViewModelTest { when(mWifiHotspotRepository.getSecurityType()).thenReturn(mSecurityType); when(mWifiHotspotRepository.getSpeedType()).thenReturn(mSpeedType); when(mWifiHotspotRepository.getRestarting()).thenReturn(mRestarting); + when(featureFactory.getWifiFeatureProvider().getSharedConnectivityRepository()) + .thenReturn(mSharedConnectivityRepository); + when(mSharedConnectivityRepository.isServiceAvailable()).thenReturn(true); + when(mSharedConnectivityRepository.getSettingsState()).thenReturn(mSettingsState); mViewModel = new WifiTetherViewModel(mApplication); + mViewModel.mInstantHotspotSummary = mInstantHotspotSummary; + } + + @Test + public void constructor_observeData() { + verify(mSettingsState).observeForever(mViewModel.mInstantHotspotStateObserver); } @Test @@ -83,6 +106,7 @@ public class WifiTetherViewModelTest { verify(mSecurityType).removeObserver(mViewModel.mSecurityTypeObserver); verify(mSpeedType).removeObserver(mViewModel.mSpeedTypeObserver); + verify(mSettingsState).removeObserver(mViewModel.mInstantHotspotStateObserver); } @Test @@ -141,4 +165,59 @@ public class WifiTetherViewModelTest { public void getRestarting_shouldNotReturnNull() { assertThat(mViewModel.getRestarting()).isNotNull(); } + + @Test + public void isInstantHotspotFeatureAvailable_serviceAvailable_returnTrue() { + when(mSharedConnectivityRepository.isServiceAvailable()).thenReturn(true); + + assertThat(mViewModel.isInstantHotspotFeatureAvailable()).isTrue(); + } + + @Test + public void isInstantHotspotFeatureAvailable_serviceNotAvailable_returnFalse() { + when(mSharedConnectivityRepository.isServiceAvailable()).thenReturn(false); + + assertThat(mViewModel.isInstantHotspotFeatureAvailable()).isFalse(); + } + + @Test + public void getInstantHotspotSummary_isNotNull() { + assertThat(mViewModel.getInstantHotspotSummary()).isNotNull(); + } + + @Test + public void onInstantHotspotStateChanged_stageNull_summarySetValueNull() { + mViewModel.onInstantHotspotStateChanged(null); + + verify(mInstantHotspotSummary).setValue(null); + } + + @Test + public void onInstantHotspotStateChanged_stateEnabled_summarySetValueOn() { + SharedConnectivitySettingsState state = new SharedConnectivitySettingsState.Builder() + .setInstantTetherEnabled(true).build(); + + mViewModel.onInstantHotspotStateChanged(state); + + verify(mInstantHotspotSummary) + .setValue(mApplication.getString(RES_INSTANT_HOTSPOT_SUMMARY_ON)); + } + + @Test + public void onInstantHotspotStateChanged_stateNotEnabled_recordVisibleSummaryOff() { + SharedConnectivitySettingsState state = new SharedConnectivitySettingsState.Builder() + .setInstantTetherEnabled(false).build(); + + mViewModel.onInstantHotspotStateChanged(state); + + verify(mInstantHotspotSummary) + .setValue(mApplication.getString(RES_INSTANT_HOTSPOT_SUMMARY_OFF)); + } + + @Test + public void launchInstantHotspotSettings_launchSettingsByRepository() { + mViewModel.launchInstantHotspotSettings(); + + verify(mSharedConnectivityRepository).launchSettings(); + } }