diff --git a/res/values/strings.xml b/res/values/strings.xml index 3703832a7d8..d87e1de16ea 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -2063,6 +2063,21 @@ IPv6 addresses + + Hotspot device details + + Internet source + + Wi\u2011Fi + + Mobile data + + Ethernet + + Hotspot connection + + Connection strength + Saved networks diff --git a/res/xml/wifi_network_details_fragment2.xml b/res/xml/wifi_network_details_fragment2.xml index eb9add14f66..e3464c215d7 100644 --- a/res/xml/wifi_network_details_fragment2.xml +++ b/res/xml/wifi_network_details_fragment2.xml @@ -40,6 +40,30 @@ android:key="buttons" android:selectable="false"/> + + + + + + + + + + mWifiDialogListeners = new ArrayList<>(); @VisibleForTesting List mControllers; + private boolean mIsInstantHotspotFeatureEnabled = + SharedConnectivityRepository.isDeviceConfigEnabled(); + @VisibleForTesting + WifiNetworkDetailsViewModel mWifiNetworkDetailsViewModel; public WifiNetworkDetailsFragment() { super(UserManager.DISALLOW_CONFIG_WIFI); @@ -207,6 +229,10 @@ public class WifiNetworkDetailsFragment extends RestrictedDashboardFragment impl setupNetworksDetailTracker(); final WifiEntry wifiEntry = mNetworkDetailsTracker.getWifiEntry(); + if (mIsInstantHotspotFeatureEnabled) { + getWifiNetworkDetailsViewModel().setWifiEntry(wifiEntry); + } + final WifiSecondSummaryController2 wifiSecondSummaryController2 = new WifiSecondSummaryController2(context); wifiSecondSummaryController2.setWifiEntry(wifiEntry); @@ -335,5 +361,98 @@ public class WifiNetworkDetailsFragment extends RestrictedDashboardFragment impl } controller.displayPreference(screen); } + if (mIsInstantHotspotFeatureEnabled) { + getWifiNetworkDetailsViewModel().setWifiEntry(mNetworkDetailsTracker.getWifiEntry()); + } + } + + private WifiNetworkDetailsViewModel getWifiNetworkDetailsViewModel() { + if (mWifiNetworkDetailsViewModel == null) { + mWifiNetworkDetailsViewModel = FeatureFactory.getFeatureFactory() + .getWifiFeatureProvider().getWifiNetworkDetailsViewModel(this); + mWifiNetworkDetailsViewModel.getHotspotNetworkData() + .observe(this, this::onHotspotNetworkChanged); + } + return mWifiNetworkDetailsViewModel; + } + + @VisibleForTesting + void onHotspotNetworkChanged(WifiNetworkDetailsViewModel.HotspotNetworkData data) { + PreferenceScreen screen = getPreferenceScreen(); + if (screen == null) { + return; + } + if (data == null) { + screen.findPreference(KEY_HOTSPOT_DEVICE_CATEGORY).setVisible(false); + screen.findPreference(KEY_HOTSPOT_CONNECTION_CATEGORY).setVisible(false); + if (mWifiDetailPreferenceController2 != null) { + mWifiDetailPreferenceController2.setSignalStrengthTitle(R.string.wifi_signal); + } + return; + } + screen.findPreference(KEY_HOTSPOT_DEVICE_CATEGORY).setVisible(true); + updateInternetSource(data.getNetworkType(), data.getUpstreamConnectionStrength()); + updateBattery(data.isBatteryCharging(), data.getBatteryPercentage()); + + screen.findPreference(KEY_HOTSPOT_CONNECTION_CATEGORY).setVisible(true); + if (mWifiDetailPreferenceController2 != null) { + mWifiDetailPreferenceController2 + .setSignalStrengthTitle(R.string.hotspot_connection_strength); + } + } + + @VisibleForTesting + void updateInternetSource(int networkType, int upstreamConnectionStrength) { + Preference internetSource = getPreferenceScreen() + .findPreference(KEY_HOTSPOT_DEVICE_INTERNET_SOURCE); + Drawable drawable; + if (networkType == HotspotNetwork.NETWORK_TYPE_WIFI) { + internetSource.setSummary(R.string.internet_source_wifi); + drawable = getContext().getDrawable( + WifiUtils.getInternetIconResource(upstreamConnectionStrength, false)); + } else if (networkType == HotspotNetwork.NETWORK_TYPE_CELLULAR) { + internetSource.setSummary(R.string.internet_source_mobile_data); + drawable = getMobileDataIcon(upstreamConnectionStrength); + } else if (networkType == HotspotNetwork.NETWORK_TYPE_ETHERNET) { + internetSource.setSummary(R.string.internet_source_ethernet); + drawable = getContext().getDrawable(R.drawable.ic_settings_ethernet); + } else { + internetSource.setSummary(R.string.summary_placeholder); + drawable = null; + } + if (drawable != null) { + drawable.setTintList( + Utils.getColorAttr(getContext(), android.R.attr.colorControlNormal)); + } + internetSource.setIcon(drawable); + } + + @VisibleForTesting + Drawable getMobileDataIcon(int level) { + return MobileNetworkUtils.getSignalStrengthIcon(getContext(), level, + SignalStrength.NUM_SIGNAL_STRENGTH_BINS, NO_CELL_DATA_TYPE_ICON, false, false); + } + + @VisibleForTesting + void updateBattery(boolean isChanging, int percentage) { + Preference battery = getPreferenceScreen().findPreference(KEY_HOTSPOT_DEVICE_BATTERY); + battery.setSummary(formatPercentage(percentage)); + ThemedBatteryDrawable drawable = getBatteryDrawable(); + if (drawable != null) { + drawable.setCharging(isChanging); + drawable.setBatteryLevel(percentage); + } + battery.setIcon(drawable); + } + + @VisibleForTesting + ThemedBatteryDrawable getBatteryDrawable() { + int frameColor = getContext() + .getColor(com.android.settingslib.R.color.meter_background_color); + ThemedBatteryDrawable drawable = new ThemedBatteryDrawable(getContext(), frameColor); + ColorFilter colorFilter = Utils.getAlphaInvariantColorFilterForColor( + Utils.getColorAttrDefaultColor(getContext(), android.R.attr.colorControlNormal)); + drawable.setColorFilter(colorFilter); + return drawable; } } diff --git a/src/com/android/settings/wifi/details/WifiNetworkDetailsViewModel.java b/src/com/android/settings/wifi/details/WifiNetworkDetailsViewModel.java new file mode 100644 index 00000000000..07445a32729 --- /dev/null +++ b/src/com/android/settings/wifi/details/WifiNetworkDetailsViewModel.java @@ -0,0 +1,117 @@ +/* + * 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.details; + +import android.app.Application; + +import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; + +import com.android.settings.overlay.FeatureFactory; +import com.android.wifitrackerlib.HotspotNetworkEntry; +import com.android.wifitrackerlib.WifiEntry; + +import org.jetbrains.annotations.NotNull; + +/** + * Wi-Fi Network Details ViewModel + */ +public class WifiNetworkDetailsViewModel extends AndroidViewModel { + private static final String TAG = "WifiNetworkDetailsViewModel"; + + @VisibleForTesting + MutableLiveData mHotspotNetworkData = new MutableLiveData<>(); + + public WifiNetworkDetailsViewModel(@NotNull Application application) { + super(application); + } + + /** Sets the {@link WifiEntry} class */ + public void setWifiEntry(WifiEntry wifiEntry) { + if (!(wifiEntry instanceof HotspotNetworkEntry)) { + log("post HotspotNetworkData:null"); + mHotspotNetworkData.postValue(null); + return; + } + HotspotNetworkEntry entry = (HotspotNetworkEntry) wifiEntry; + HotspotNetworkData data = new HotspotNetworkData( + entry.getNetworkType(), + entry.getUpstreamConnectionStrength(), + entry.getBatteryPercentage(), + entry.isBatteryCharging()); + log("post HotspotNetworkData:" + data); + mHotspotNetworkData.postValue(data); + } + + /** Gets the {@link HotspotNetworkData} LiveData */ + public LiveData getHotspotNetworkData() { + return mHotspotNetworkData; + } + + /** The {@link HotspotNetworkData} class */ + static class HotspotNetworkData { + private int mNetworkType; + private int mUpstreamConnectionStrength; + private int mBatteryPercentage; + private boolean mIsBatteryCharging; + + HotspotNetworkData(int networkType, int upstreamConnectionStrength, + int batteryPercentage, + boolean isBatteryCharging) { + mNetworkType = networkType; + mUpstreamConnectionStrength = upstreamConnectionStrength; + mBatteryPercentage = batteryPercentage; + mIsBatteryCharging = isBatteryCharging; + } + + /** Gets the network type */ + public int getNetworkType() { + return mNetworkType; + } + + /** Gets the upstream connection strength */ + public int getUpstreamConnectionStrength() { + return mUpstreamConnectionStrength; + } + + /** Gets the battery percentage */ + public int getBatteryPercentage() { + return mBatteryPercentage; + } + + /** Returns true if the battery is charging */ + public boolean isBatteryCharging() { + return mIsBatteryCharging; + } + + @Override + public String toString() { + return getClass().getSimpleName() + + ":{networkType:" + mNetworkType + + ", upstreamConnectionStrength:" + mUpstreamConnectionStrength + + ", batteryPercentage:" + mBatteryPercentage + + ", isBatteryCharging:" + mIsBatteryCharging + + " }"; + } + } + + private void log(String msg) { + FeatureFactory.getFeatureFactory().getWifiFeatureProvider().verboseLog(TAG, msg); + } +} diff --git a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java index 0da56e370b9..ac0e149b094 100644 --- a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java +++ b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java @@ -21,6 +21,8 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID; +import static com.android.settingslib.wifi.WifiUtils.getHotspotIconResource; + import android.app.Activity; import android.app.AlertDialog; import android.app.settings.SettingsEnums; @@ -86,6 +88,7 @@ import com.android.settingslib.core.lifecycle.events.OnResume; import com.android.settingslib.utils.StringUtil; import com.android.settingslib.widget.ActionButtonsPreference; import com.android.settingslib.widget.LayoutPreference; +import com.android.wifitrackerlib.HotspotNetworkEntry; import com.android.wifitrackerlib.WifiEntry; import com.android.wifitrackerlib.WifiEntry.ConnectCallback; import com.android.wifitrackerlib.WifiEntry.DisconnectCallback; @@ -172,7 +175,8 @@ public class WifiDetailPreferenceController2 extends AbstractPreferenceControlle // UI elements - in order of appearance private ActionButtonsPreference mButtonsPref; - private EntityHeaderController mEntityHeaderController; + @VisibleForTesting + EntityHeaderController mEntityHeaderController; private Preference mSignalStrengthPref; private Preference mTxLinkSpeedPref; private Preference mRxLinkSpeedPref; @@ -533,6 +537,8 @@ public class WifiDetailPreferenceController2 extends AbstractPreferenceControlle private void refreshPage() { Log.d(TAG, "Update UI!"); + // refresh header icon + refreshEntryHeaderIcon(); // refresh header refreshEntityHeader(); @@ -561,8 +567,33 @@ public class WifiDetailPreferenceController2 extends AbstractPreferenceControlle refreshWifiType(); } + @VisibleForTesting + void refreshEntryHeaderIcon() { + if (mEntityHeaderController == null) { + return; + } + Drawable drawable = getWifiDrawable(mWifiEntry); + mEntityHeaderController + .setIcon(redrawIconForHeader(drawable)) + .done(true /* rebind */); + } + + /** + * Returns a Wi-Fi icon {@link Drawable}. + * + * @param wifiEntry {@link WifiEntry} + */ + @VisibleForTesting + Drawable getWifiDrawable(WifiEntry wifiEntry) { + if (wifiEntry instanceof HotspotNetworkEntry) { + int deviceType = ((HotspotNetworkEntry) wifiEntry).getDeviceType(); + return mContext.getDrawable(getHotspotIconResource(deviceType)); + } + return mIconInjector.getIcon(wifiEntry.shouldShowXLevelIcon(), wifiEntry.getLevel()); + } + private void refreshRssiViews() { - final int signalLevel = mWifiEntry.getLevel(); + int signalLevel = mWifiEntry.getLevel(); // Disappears signal view if not in range. e.g. for saved networks. if (signalLevel == WifiEntry.WIFI_LEVEL_UNREACHABLE) { @@ -571,19 +602,13 @@ public class WifiDetailPreferenceController2 extends AbstractPreferenceControlle return; } - final boolean showX = mWifiEntry.shouldShowXLevelIcon(); - + boolean showX = mWifiEntry.shouldShowXLevelIcon(); if (mRssiSignalLevel == signalLevel && mShowX == showX) { return; } mRssiSignalLevel = signalLevel; mShowX = showX; Drawable wifiIcon = mIconInjector.getIcon(mShowX, mRssiSignalLevel); - - if (mEntityHeaderController != null) { - mEntityHeaderController.setIcon(redrawIconForHeader(wifiIcon)).done(true /* rebind */); - } - Drawable wifiIconDark = wifiIcon.getConstantState().newDrawable().mutate(); wifiIconDark.setTintList(Utils.getColorAttr(mContext, android.R.attr.colorControlNormal)); mSignalStrengthPref.setIcon(wifiIconDark); @@ -1120,4 +1145,11 @@ public class WifiDetailPreferenceController2 extends AbstractPreferenceControlle public void onSignInResult(@SignInStatus int status) { refreshPage(); } + + /** Sets signal strength title */ + public void setSignalStrengthTitle(int titleResId) { + if (mSignalStrengthPref != null) { + mSignalStrengthPref.setTitle(titleResId); + } + } } diff --git a/src/com/android/settings/wifi/factory/WifiFeatureProvider.java b/src/com/android/settings/wifi/factory/WifiFeatureProvider.java index 3f0d62f31b0..9e7365ee309 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.details.WifiNetworkDetailsViewModel; import com.android.settings.wifi.repository.SharedConnectivityRepository; import com.android.settings.wifi.repository.WifiHotspotRepository; import com.android.settings.wifi.tether.WifiHotspotSecurityViewModel; @@ -134,6 +135,17 @@ public class WifiFeatureProvider { return viewModel; } + /** + * Gets WifiNetworkDetailsViewModel + */ + public WifiNetworkDetailsViewModel getWifiNetworkDetailsViewModel( + @NotNull ViewModelStoreOwner owner) { + WifiNetworkDetailsViewModel viewModel = + new ViewModelProvider(owner).get(WifiNetworkDetailsViewModel.class); + verboseLog(TAG, "getWifiNetworkDetailsViewModel():" + viewModel); + return viewModel; + } + /** * Send a {@link Log#VERBOSE} log message. * diff --git a/src/com/android/settings/wifi/repository/SharedConnectivityRepository.java b/src/com/android/settings/wifi/repository/SharedConnectivityRepository.java index fa34b0011df..5b1a1fd9320 100644 --- a/src/com/android/settings/wifi/repository/SharedConnectivityRepository.java +++ b/src/com/android/settings/wifi/repository/SharedConnectivityRepository.java @@ -58,8 +58,7 @@ public class SharedConnectivityRepository { MutableLiveData mSettingsState = new MutableLiveData<>(); public SharedConnectivityRepository(@NonNull Context appContext) { - this(appContext, - DeviceConfig.getBoolean(DEVICE_CONFIG_NAMESPACE, DEVICE_CONFIG_KEY, false)); + this(appContext, isDeviceConfigEnabled()); } @VisibleForTesting @@ -181,4 +180,11 @@ public class SharedConnectivityRepository { private void log(String msg) { FeatureFactory.getFeatureFactory().getWifiFeatureProvider().verboseLog(TAG, msg); } + + /** + * Returns true if Shared Connectivity feature is enabled. + */ + public static boolean isDeviceConfigEnabled() { + return DeviceConfig.getBoolean(DEVICE_CONFIG_NAMESPACE, DEVICE_CONFIG_KEY, false); + } } diff --git a/tests/robotests/src/com/android/settings/wifi/details/WifiNetworkDetailsFragmentTest.java b/tests/robotests/src/com/android/settings/wifi/details/WifiNetworkDetailsFragmentTest.java index 4f742549f24..bd8b5be50a0 100644 --- a/tests/robotests/src/com/android/settings/wifi/details/WifiNetworkDetailsFragmentTest.java +++ b/tests/robotests/src/com/android/settings/wifi/details/WifiNetworkDetailsFragmentTest.java @@ -16,7 +16,19 @@ package com.android.settings.wifi.details; +import static android.net.wifi.sharedconnectivity.app.HotspotNetwork.NETWORK_TYPE_CELLULAR; +import static android.net.wifi.sharedconnectivity.app.HotspotNetwork.NETWORK_TYPE_ETHERNET; +import static android.net.wifi.sharedconnectivity.app.HotspotNetwork.NETWORK_TYPE_UNKNOWN; +import static android.net.wifi.sharedconnectivity.app.HotspotNetwork.NETWORK_TYPE_WIFI; +import static android.telephony.SignalStrength.SIGNAL_STRENGTH_GREAT; + import static com.android.settings.wifi.WifiSettings.WIFI_DIALOG_ID; +import static com.android.settings.wifi.details.WifiNetworkDetailsFragment.KEY_HOTSPOT_CONNECTION_CATEGORY; +import static com.android.settings.wifi.details.WifiNetworkDetailsFragment.KEY_HOTSPOT_DEVICE_BATTERY; +import static com.android.settings.wifi.details.WifiNetworkDetailsFragment.KEY_HOTSPOT_DEVICE_CATEGORY; +import static com.android.settings.wifi.details.WifiNetworkDetailsFragment.KEY_HOTSPOT_DEVICE_INTERNET_SOURCE; +import static com.android.settingslib.Utils.formatPercentage; +import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MAX; import static com.google.common.truth.Truth.assertThat; @@ -29,25 +41,37 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.app.settings.SettingsEnums; +import android.content.Context; +import android.graphics.drawable.Drawable; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.widget.TextView; import androidx.preference.Preference; +import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; +import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; +import com.android.settings.wifi.WifiUtils; +import com.android.settings.wifi.details2.WifiDetailPreferenceController2; import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.graph.ThemedBatteryDrawable; import com.android.wifitrackerlib.NetworkDetailsTracker; import com.android.wifitrackerlib.WifiEntry; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; @@ -56,23 +80,63 @@ import java.util.ArrayList; @RunWith(RobolectricTestRunner.class) public class WifiNetworkDetailsFragmentTest { - private static final String TEST_PREFERENCE_KEY = "TEST_PREFERENCE_KEY"; + static final String TEST_PREFERENCE_KEY = "TEST_PREFERENCE_KEY"; + static final int BATTERY_PERCENTAGE_MAX = 100; + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Spy + Context mContext = ApplicationProvider.getApplicationContext(); + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + PreferenceManager mPreferenceManager; + @Mock + Preference mHotspotDeviceCategory; + @Mock + Preference mInternetSource; + @Mock + Preference mBattery; + @Mock + Preference mHotspotConnectionCategory; + @Mock + Menu mMenu; + @Mock + Drawable mDrawable; + @Mock + WifiDetailPreferenceController2 mWifiDetailPreferenceController2; @Mock WifiEntry mWifiEntry; @Mock NetworkDetailsTracker mNetworkDetailsTracker; @Mock - Menu mMenu; - private WifiNetworkDetailsFragment mFragment; + WifiNetworkDetailsViewModel.HotspotNetworkData mHotspotNetworkData; + + FakeFragment mFragment; + PreferenceScreen mScreen; + ArgumentCaptor mThemedBatteryDrawableCaptor = + ArgumentCaptor.forClass(ThemedBatteryDrawable.class); @Before public void setUp() { - MockitoAnnotations.initMocks(this); doReturn(mWifiEntry).when(mNetworkDetailsTracker).getWifiEntry(); doReturn(true).when(mWifiEntry).isSaved(); + doReturn(NETWORK_TYPE_WIFI).when(mHotspotNetworkData).getNetworkType(); + doReturn(WIFI_LEVEL_MAX).when(mHotspotNetworkData).getUpstreamConnectionStrength(); + doReturn(BATTERY_PERCENTAGE_MAX).when(mHotspotNetworkData).getBatteryPercentage(); + doReturn(true).when(mHotspotNetworkData).isBatteryCharging(); - mFragment = new WifiNetworkDetailsFragment(); + mFragment = spy(new FakeFragment()); + doReturn(mPreferenceManager).when(mFragment).getPreferenceManager(); + doReturn(mContext).when(mPreferenceManager).getContext(); + doReturn(mContext).when(mFragment).getContext(); + mScreen = spy(new PreferenceScreen(mContext, /* attrs= */ null)); + doReturn(mPreferenceManager).when(mScreen).getPreferenceManager(); + doReturn(mScreen).when(mFragment).getPreferenceScreen(); + + doReturn(mHotspotDeviceCategory).when(mScreen).findPreference(KEY_HOTSPOT_DEVICE_CATEGORY); + doReturn(mInternetSource).when(mScreen).findPreference(KEY_HOTSPOT_DEVICE_INTERNET_SOURCE); + doReturn(mBattery).when(mScreen).findPreference(KEY_HOTSPOT_DEVICE_BATTERY); + doReturn(mHotspotConnectionCategory).when(mScreen) + .findPreference(KEY_HOTSPOT_CONNECTION_CATEGORY); mFragment.mNetworkDetailsTracker = mNetworkDetailsTracker; } @@ -94,7 +158,7 @@ public class WifiNetworkDetailsFragmentTest { @Test public void onCreateOptionsMenu_shouldSetCorrectIcon() { - final MenuItem menuItem = mock(MenuItem.class); + MenuItem menuItem = mock(MenuItem.class); doReturn(menuItem).when(mMenu).add(anyInt(), eq(Menu.FIRST), anyInt(), anyInt()); mFragment.onCreateOptionsMenu(mMenu, mock(MenuInflater.class)); @@ -122,47 +186,129 @@ public class WifiNetworkDetailsFragmentTest { @Test public void restrictUi_shouldShowRestrictedText() { - final FakeFragment fragment = spy(new FakeFragment()); - final PreferenceScreen screen = mock(PreferenceScreen.class); - final TextView restrictedText = mock(TextView.class); - doReturn(screen).when(fragment).getPreferenceScreen(); - doReturn(false).when(fragment).isUiRestrictedByOnlyAdmin(); - doReturn(restrictedText).when(fragment).getEmptyTextView(); + TextView restrictedText = mock(TextView.class); + doReturn(mScreen).when(mFragment).getPreferenceScreen(); + doReturn(false).when(mFragment).isUiRestrictedByOnlyAdmin(); + doReturn(restrictedText).when(mFragment).getEmptyTextView(); - fragment.restrictUi(); + mFragment.restrictUi(); verify(restrictedText).setText(anyInt()); } @Test public void restrictUi_shouldRemoveAllPreferences() { - final FakeFragment fragment = spy(new FakeFragment()); - final PreferenceScreen screen = mock(PreferenceScreen.class); - doReturn(screen).when(fragment).getPreferenceScreen(); - doReturn(true).when(fragment).isUiRestrictedByOnlyAdmin(); + doReturn(mScreen).when(mFragment).getPreferenceScreen(); + doReturn(true).when(mFragment).isUiRestrictedByOnlyAdmin(); - fragment.restrictUi(); + mFragment.restrictUi(); - verify(screen).removeAll(); + verify(mScreen).removeAll(); } @Test public void refreshPreferences_controllerShouldUpdateStateAndDisplayPreference() { - final FakeFragment fragment = spy(new FakeFragment()); - final PreferenceScreen screen = mock(PreferenceScreen.class); - final Preference preference = mock(Preference.class); - final TestController controller = mock(TestController.class); - doReturn(screen).when(fragment).getPreferenceScreen(); - doReturn(preference).when(screen).findPreference(TEST_PREFERENCE_KEY); + Preference preference = mock(Preference.class); + TestController controller = mock(TestController.class); + doReturn(mScreen).when(mFragment).getPreferenceScreen(); + doReturn(preference).when(mScreen).findPreference(TEST_PREFERENCE_KEY); doReturn(TEST_PREFERENCE_KEY).when(controller).getPreferenceKey(); - fragment.mControllers = new ArrayList<>(); - fragment.mControllers.add(controller); - fragment.addPreferenceController(controller); + mFragment.mControllers = new ArrayList<>(); + mFragment.mControllers.add(controller); + mFragment.addPreferenceController(controller); - fragment.refreshPreferences(); + mFragment.refreshPreferences(); verify(controller).updateState(preference); - verify(controller).displayPreference(screen); + verify(controller).displayPreference(mScreen); + } + + @Test + public void onHotspotNetworkChanged_dataNull_hotspotSetVisibleFalse() { + mFragment.mWifiDetailPreferenceController2 = mWifiDetailPreferenceController2; + + mFragment.onHotspotNetworkChanged(null); + + verify(mHotspotDeviceCategory).setVisible(false); + verify(mHotspotConnectionCategory).setVisible(false); + verify(mWifiDetailPreferenceController2).setSignalStrengthTitle(R.string.wifi_signal); + } + + @Test + public void onHotspotNetworkChanged_dataNotNull_hotspotSetVisibleTrue() { + mFragment.mWifiDetailPreferenceController2 = mWifiDetailPreferenceController2; + + mFragment.onHotspotNetworkChanged(mHotspotNetworkData); + + verify(mHotspotDeviceCategory).setVisible(true); + verify(mFragment).updateInternetSource(mHotspotNetworkData.getNetworkType(), + mHotspotNetworkData.getUpstreamConnectionStrength()); + verify(mFragment).updateBattery(mHotspotNetworkData.isBatteryCharging(), + mHotspotNetworkData.getBatteryPercentage()); + verify(mHotspotConnectionCategory).setVisible(true); + verify(mWifiDetailPreferenceController2) + .setSignalStrengthTitle(R.string.hotspot_connection_strength); + } + + @Test + public void updateInternetSource_networkTypeWifi_setWifiResource() { + doReturn(mDrawable).when(mContext) + .getDrawable(WifiUtils.getInternetIconResource(WIFI_LEVEL_MAX, false)); + + mFragment.updateInternetSource(NETWORK_TYPE_WIFI, WIFI_LEVEL_MAX); + + verify(mInternetSource).setSummary(R.string.internet_source_wifi); + verify(mInternetSource).setIcon(mDrawable); + } + + @Test + public void updateInternetSource_networkTypeMobileData_setMobileDataResource() { + doReturn(mDrawable).when(mFragment).getMobileDataIcon(SIGNAL_STRENGTH_GREAT); + + mFragment.updateInternetSource(NETWORK_TYPE_CELLULAR, SIGNAL_STRENGTH_GREAT); + + verify(mInternetSource).setSummary(R.string.internet_source_mobile_data); + verify(mInternetSource).setIcon(mDrawable); + } + + @Test + public void updateInternetSource_networkTypeEthernet_setEthernetResource() { + doReturn(mDrawable).when(mContext).getDrawable(R.drawable.ic_settings_ethernet); + + mFragment.updateInternetSource(NETWORK_TYPE_ETHERNET, 0 /* don't care */); + + verify(mInternetSource).setSummary(R.string.internet_source_ethernet); + verify(mInternetSource).setIcon(mDrawable); + } + + @Test + public void updateInternetSource_networkTypeUnknown_setPlaceholderResource() { + mFragment.updateInternetSource(NETWORK_TYPE_UNKNOWN, 0 /* don't care */); + + verify(mInternetSource).setSummary(R.string.summary_placeholder); + verify(mInternetSource).setIcon(null); + } + + @Test + public void updateBattery_hiPercentageNoCharging_setResourceCorrect() { + mFragment.updateBattery(false /* isChanging */, BATTERY_PERCENTAGE_MAX); + + verify(mBattery).setSummary(formatPercentage(BATTERY_PERCENTAGE_MAX)); + verify(mBattery).setIcon(mThemedBatteryDrawableCaptor.capture()); + ThemedBatteryDrawable drawable = mThemedBatteryDrawableCaptor.getValue(); + assertThat(drawable.getCharging()).isFalse(); + assertThat(drawable.getBatteryLevel()).isEqualTo(BATTERY_PERCENTAGE_MAX); + } + + @Test + public void updateBattery_lowPercentageWithCharging_setResourceCorrect() { + mFragment.updateBattery(true /* isChanging */, 0 /* percentage */); + + verify(mBattery).setSummary(formatPercentage(0)); + verify(mBattery).setIcon(mThemedBatteryDrawableCaptor.capture()); + ThemedBatteryDrawable drawable = mThemedBatteryDrawableCaptor.getValue(); + assertThat(drawable.getCharging()).isTrue(); + assertThat(drawable.getBatteryLevel()).isEqualTo(0); } // Fake WifiNetworkDetailsFragment to override the protected method as public. diff --git a/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java b/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java index 7c9f6655ee5..c573c94c41e 100644 --- a/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java +++ b/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java @@ -15,8 +15,13 @@ */ package com.android.settings.wifi.details2; +import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE_TYPE_PHONE; + +import static com.android.settingslib.wifi.WifiUtils.getHotspotIconResource; + import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; @@ -83,6 +88,7 @@ import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.utils.StringUtil; import com.android.settingslib.widget.ActionButtonsPreference; import com.android.settingslib.widget.LayoutPreference; +import com.android.wifitrackerlib.HotspotNetworkEntry; import com.android.wifitrackerlib.NetworkDetailsTracker; import com.android.wifitrackerlib.WifiEntry; import com.android.wifitrackerlib.WifiEntry.ConnectCallback; @@ -524,12 +530,12 @@ public class WifiDetailPreferenceController2Test { } @Test - public void entityHeader_shouldNotHaveIconSetForNotInRangeNetwork() { + public void entityHeader_shouldHaveIconSetForNotInRangeNetwork() { setUpForNotInRangeNetwork(); displayAndResume(); - verify(mMockHeaderController, never()).setIcon(any(Drawable.class)); + verify(mMockHeaderController).setIcon(any(Drawable.class)); } @Test @@ -1814,6 +1820,71 @@ public class WifiDetailPreferenceController2Test { assertThat(info.getDisplayName().toString()).isEqualTo("sim2"); } + @Test + public void refreshEntryHeaderIcon_entityHeaderControllerNull_doNothing() { + setUpSpyController(); + mController.mEntityHeaderController = null; + + mController.refreshEntryHeaderIcon(); + + verify(mController, never()).getWifiDrawable(any()); + } + + @Test + public void refreshEntryHeaderIcon_entityHeaderControllerNotNull_setIcon() { + setUpSpyController(); + mController.mEntityHeaderController = mMockHeaderController; + + mController.refreshEntryHeaderIcon(); + + verify(mController).getWifiDrawable(any()); + verify(mMockHeaderController).setIcon(any(Drawable.class)); + } + + @Test + public void getWifiDrawable_withHotspotNetworkEntry_returnHotspotDrawable() { + setUpSpyController(); + HotspotNetworkEntry entry = mock(HotspotNetworkEntry.class); + when(entry.getDeviceType()).thenReturn(DEVICE_TYPE_PHONE); + + mController.getWifiDrawable(entry); + + verify(mContext).getDrawable(getHotspotIconResource(DEVICE_TYPE_PHONE)); + } + + @Test + public void getWifiDrawable_withWifiEntryNotShowXLevelIcon_getIconWithInternet() { + setUpSpyController(); + when(mMockWifiEntry.getLevel()).thenReturn(WifiEntry.WIFI_LEVEL_MAX); + when(mMockWifiEntry.shouldShowXLevelIcon()).thenReturn(false); + + mController.getWifiDrawable(mMockWifiEntry); + + verify(mMockIconInjector).getIcon(eq(false) /* noInternet */, anyInt()); + } + + @Test + public void getWifiDrawable_withWifiEntryShowXLevelIcon_getIconWithNoInternet() { + setUpSpyController(); + when(mMockWifiEntry.getLevel()).thenReturn(WifiEntry.WIFI_LEVEL_MAX); + when(mMockWifiEntry.shouldShowXLevelIcon()).thenReturn(true); + + mController.getWifiDrawable(mMockWifiEntry); + + verify(mMockIconInjector).getIcon(eq(true) /* noInternet */, anyInt()); + verify(mMockIconInjector).getIcon(eq(true) /* noInternet */, anyInt()); + } + + @Test + public void setSignalStrengthTitle_prefNotNull_setPrefTitle() { + setUpSpyController(); + mController.displayPreference(mMockScreen); + + mController.setSignalStrengthTitle(R.string.hotspot_connection_strength); + + verify(mMockSignalStrengthPref).setTitle(R.string.hotspot_connection_strength); + } + private SubscriptionInfo mockSubscriptionInfo(int subId, String displayName, int carrierId) { SubscriptionInfo info = mock(SubscriptionInfo.class); when(info.getSubscriptionId()).thenReturn(subId); diff --git a/tests/unit/src/com/android/settings/wifi/details/WifiNetworkDetailsViewModelTest.java b/tests/unit/src/com/android/settings/wifi/details/WifiNetworkDetailsViewModelTest.java new file mode 100644 index 00000000000..5f2c561340b --- /dev/null +++ b/tests/unit/src/com/android/settings/wifi/details/WifiNetworkDetailsViewModelTest.java @@ -0,0 +1,134 @@ +/* + * 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.details; + +import static android.net.wifi.sharedconnectivity.app.HotspotNetwork.NETWORK_TYPE_CELLULAR; +import static android.net.wifi.sharedconnectivity.app.HotspotNetwork.NETWORK_TYPE_ETHERNET; +import static android.net.wifi.sharedconnectivity.app.HotspotNetwork.NETWORK_TYPE_WIFI; +import static android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + +import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MAX; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Application; + +import androidx.lifecycle.MutableLiveData; +import androidx.test.core.app.ApplicationProvider; + +import com.android.wifitrackerlib.HotspotNetworkEntry; +import com.android.wifitrackerlib.WifiEntry; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +public class WifiNetworkDetailsViewModelTest { + + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Spy + Application mApplication = ApplicationProvider.getApplicationContext(); + @Mock + MutableLiveData mHotspotNetworkData; + @Mock + HotspotNetworkEntry mHotspotNetworkEntry; + + WifiNetworkDetailsViewModel mViewModel; + ArgumentCaptor mHotspotNetworkDataCaptor = + ArgumentCaptor.forClass(WifiNetworkDetailsViewModel.HotspotNetworkData.class); + + @Before + public void setUp() { + mViewModel = new WifiNetworkDetailsViewModel(mApplication); + mViewModel.mHotspotNetworkData = mHotspotNetworkData; + } + + @Test + public void setWifiEntry_notHotspotNetworkEntry_postValueNull() { + mViewModel.setWifiEntry(mock(WifiEntry.class)); + + verify(mHotspotNetworkData).postValue(null); + } + + @Test + public void setWifiEntry_hotspotNetworkEntryWifi_postValueCorrect() { + when(mHotspotNetworkEntry.getNetworkType()).thenReturn(NETWORK_TYPE_WIFI); + when(mHotspotNetworkEntry.getUpstreamConnectionStrength()).thenReturn(WIFI_LEVEL_MAX); + when(mHotspotNetworkEntry.getBatteryPercentage()).thenReturn(100); + when(mHotspotNetworkEntry.isBatteryCharging()).thenReturn(false); + + + mViewModel.setWifiEntry(mHotspotNetworkEntry); + + verify(mHotspotNetworkData).postValue(mHotspotNetworkDataCaptor.capture()); + WifiNetworkDetailsViewModel.HotspotNetworkData data = mHotspotNetworkDataCaptor.getValue(); + assertThat(data.getNetworkType()).isEqualTo(NETWORK_TYPE_WIFI); + assertThat(data.getUpstreamConnectionStrength()).isEqualTo(WIFI_LEVEL_MAX); + assertThat(data.getBatteryPercentage()).isEqualTo(100); + assertThat(data.isBatteryCharging()).isEqualTo(false); + } + + @Test + public void setWifiEntry_hotspotNetworkEntryMobileData_postValueCorrect() { + when(mHotspotNetworkEntry.getNetworkType()).thenReturn(NETWORK_TYPE_CELLULAR); + when(mHotspotNetworkEntry.getUpstreamConnectionStrength()) + .thenReturn(SIGNAL_STRENGTH_NONE_OR_UNKNOWN); + when(mHotspotNetworkEntry.getBatteryPercentage()).thenReturn(0); + when(mHotspotNetworkEntry.isBatteryCharging()).thenReturn(true); + + + mViewModel.setWifiEntry(mHotspotNetworkEntry); + + verify(mHotspotNetworkData).postValue(mHotspotNetworkDataCaptor.capture()); + WifiNetworkDetailsViewModel.HotspotNetworkData data = mHotspotNetworkDataCaptor.getValue(); + assertThat(data.getNetworkType()).isEqualTo(NETWORK_TYPE_CELLULAR); + assertThat(data.getUpstreamConnectionStrength()).isEqualTo(SIGNAL_STRENGTH_NONE_OR_UNKNOWN); + assertThat(data.getBatteryPercentage()).isEqualTo(0); + assertThat(data.isBatteryCharging()).isEqualTo(true); + } + + @Test + public void setWifiEntry_hotspotNetworkEntryEthernet_postValueCorrect() { + when(mHotspotNetworkEntry.getNetworkType()).thenReturn(NETWORK_TYPE_ETHERNET); + when(mHotspotNetworkEntry.getBatteryPercentage()).thenReturn(50); + when(mHotspotNetworkEntry.isBatteryCharging()).thenReturn(true); + + + mViewModel.setWifiEntry(mHotspotNetworkEntry); + + verify(mHotspotNetworkData).postValue(mHotspotNetworkDataCaptor.capture()); + WifiNetworkDetailsViewModel.HotspotNetworkData data = mHotspotNetworkDataCaptor.getValue(); + assertThat(data.getNetworkType()).isEqualTo(NETWORK_TYPE_ETHERNET); + assertThat(data.getBatteryPercentage()).isEqualTo(50); + assertThat(data.isBatteryCharging()).isEqualTo(true); + } + + @Test + public void getSecuritySummary_returnNotNull() { + assertThat(mViewModel.getHotspotNetworkData()).isNotNull(); + } +}