diff --git a/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsPreferenceController.java b/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsPreferenceController.java index b8e36a4616b..81803dd3b89 100644 --- a/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsPreferenceController.java +++ b/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsPreferenceController.java @@ -16,10 +16,8 @@ package com.android.settings.wifi.savedaccesspoints; - import android.content.Context; import android.net.wifi.WifiManager; -import android.util.Log; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; @@ -27,10 +25,6 @@ import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceScreen; import com.android.settings.core.BasePreferenceController; -import com.android.settings.R; -import com.android.settingslib.core.lifecycle.LifecycleObserver; -import com.android.settingslib.core.lifecycle.events.OnStart; -import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.wifi.AccessPoint; import com.android.settingslib.wifi.AccessPointPreference; import com.android.settingslib.wifi.AccessPointPreference.UserBadgeCache; @@ -38,21 +32,22 @@ import com.android.settingslib.wifi.WifiSavedConfigUtils; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; /** * Controller that manages a PreferenceGroup, which contains a list of saved access points. */ public class SavedAccessPointsPreferenceController extends BasePreferenceController implements - LifecycleObserver, OnStart, Preference.OnPreferenceClickListener, - WifiManager.ActionListener { + Preference.OnPreferenceClickListener { - private static final String TAG = "SavedAPPrefCtrl"; + private static final String TAG = "SavedApPrefCtrl"; private final WifiManager mWifiManager; - private final UserBadgeCache mUserBadgeCache; private PreferenceGroup mPreferenceGroup; private SavedAccessPointsWifiSettings mHost; + @VisibleForTesting + List mAccessPoints; public SavedAccessPointsPreferenceController(Context context, String preferenceKey) { @@ -68,86 +63,46 @@ public class SavedAccessPointsPreferenceController extends BasePreferenceControl @Override public int getAvailabilityStatus() { - return AVAILABLE; + return mAccessPoints.size() > 0 ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; } @Override public void displayPreference(PreferenceScreen screen) { - super.displayPreference(screen); mPreferenceGroup = screen.findPreference(getPreferenceKey()); - } - - @Override - public void onStart() { refreshSavedAccessPoints(); - } - - public void postRefreshSavedAccessPoints() { - ThreadUtils.postOnMainThread(() -> refreshSavedAccessPoints()); + updatePreference(); + super.displayPreference(screen); } @Override public boolean onPreferenceClick(Preference preference) { - final Preference preferenceInGroup = mPreferenceGroup.findPreference(preference.getKey()); if (mHost != null) { + final Preference preferenceInGroup = + mPreferenceGroup.findPreference(preference.getKey()); mHost.showWifiPage((AccessPointPreference) preferenceInGroup); } return false; } - @Override - public void onSuccess() { - postRefreshSavedAccessPoints(); + private void refreshSavedAccessPoints() { + mAccessPoints = WifiSavedConfigUtils.getAllConfigs(mContext, mWifiManager).stream() + .filter(accessPoint -> !accessPoint.isPasspointConfig()) + .sorted(SavedNetworkComparator.INSTANCE) + .collect(Collectors.toList()); } - @Override - public void onFailure(int reason) { - postRefreshSavedAccessPoints(); - } - - @VisibleForTesting - void refreshSavedAccessPoints() { - if (mPreferenceGroup == null) { - Log.w(TAG, "PreferenceGroup is null, skipping."); - return; - } - final Context prefContext = mPreferenceGroup.getContext(); - - final List accessPoints = - WifiSavedConfigUtils.getAllConfigs(mContext, mWifiManager); - Collections.sort(accessPoints, SavedNetworkComparator.INSTANCE); + private void updatePreference() { mPreferenceGroup.removeAll(); + for (AccessPoint accessPoint : mAccessPoints) { + final String key = accessPoint.getKey(); - final int accessPointsSize = accessPoints.size(); - for (int i = 0; i < accessPointsSize; ++i) { - AccessPoint ap = accessPoints.get(i); - - if (mHost != null && mHost.isSubscriptionsFeatureEnabled() - && ap.isPasspointConfig()) { - continue; - } - - String key = ap.getKey(); - AccessPointPreference preference = new AccessPointPreference(ap, prefContext, - mUserBadgeCache, true); + final AccessPointPreference preference = new AccessPointPreference(accessPoint, + mContext, mUserBadgeCache, true /* forSavedNetworks */); preference.setKey(key); preference.setIcon(null); preference.setOnPreferenceClickListener(this); + mPreferenceGroup.addPreference(preference); - preference.setOrder(i); - } - - if (mPreferenceGroup.getPreferenceCount() < 1) { - Log.w(TAG, "Saved networks activity loaded, but there are no saved networks!"); - mPreferenceGroup.setVisible(false); - } else { - mPreferenceGroup.setVisible(true); - } - - if (mHost != null && !mHost.isSubscriptionsFeatureEnabled()) { - mPreferenceGroup.setVisible(true); - mPreferenceGroup.setTitle(null); - mPreferenceGroup.setLayoutResource(R.layout.preference_category_no_label); } } } diff --git a/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettings.java b/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettings.java index cf2fc99cb7e..863e87a268e 100644 --- a/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettings.java +++ b/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettings.java @@ -20,13 +20,13 @@ import android.annotation.Nullable; import android.app.settings.SettingsEnums; import android.content.Context; import android.os.Bundle; -import android.util.FeatureFlagUtils; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.PreferenceScreen; import com.android.settings.R; -import com.android.settings.core.FeatureFlags; import com.android.settings.core.SubSettingLauncher; import com.android.settings.dashboard.DashboardFragment; -import com.android.settings.development.featureflags.FeatureFlagPersistent; import com.android.settings.wifi.WifiSettings; import com.android.settings.wifi.details.WifiNetworkDetailsFragment; import com.android.settingslib.wifi.AccessPoint; @@ -39,7 +39,8 @@ public class SavedAccessPointsWifiSettings extends DashboardFragment { private static final String TAG = "SavedAccessPoints"; - private Bundle mAccessPointSavedState; + @VisibleForTesting + Bundle mAccessPointSavedState; private AccessPoint mSelectedAccessPoint; // Instance state key @@ -76,10 +77,22 @@ public class SavedAccessPointsWifiSettings extends DashboardFragment { if (savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) { mAccessPointSavedState = savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE); + } else { + mAccessPointSavedState = null; } } } + @Override + public void onStart() { + super.onStart(); + if (mAccessPointSavedState != null) { + final PreferenceScreen screen = getPreferenceScreen(); + use(SavedAccessPointsPreferenceController.class).displayPreference(screen); + use(SubscribedAccessPointsPreferenceController.class).displayPreference(screen); + } + } + public void showWifiPage(@Nullable AccessPointPreference accessPoint) { removeDialog(WifiSettings.WIFI_DIALOG_ID); @@ -117,9 +130,4 @@ public class SavedAccessPointsWifiSettings extends DashboardFragment { outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState); } } - - boolean isSubscriptionsFeatureEnabled() { - return FeatureFlagUtils.isEnabled(getContext(), FeatureFlags.MOBILE_NETWORK_V2) - && FeatureFlagPersistent.isEnabled(getContext(), FeatureFlags.NETWORK_INTERNET_V2); - } } diff --git a/src/com/android/settings/wifi/savedaccesspoints/SubscribedAccessPointsPreferenceController.java b/src/com/android/settings/wifi/savedaccesspoints/SubscribedAccessPointsPreferenceController.java index 048999a5811..4d7dc60b4dd 100644 --- a/src/com/android/settings/wifi/savedaccesspoints/SubscribedAccessPointsPreferenceController.java +++ b/src/com/android/settings/wifi/savedaccesspoints/SubscribedAccessPointsPreferenceController.java @@ -16,10 +16,8 @@ package com.android.settings.wifi.savedaccesspoints; - import android.content.Context; import android.net.wifi.WifiManager; -import android.util.Log; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; @@ -27,10 +25,6 @@ import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceScreen; import com.android.settings.core.BasePreferenceController; -import com.android.settings.utils.PreferenceGroupChildrenCache; -import com.android.settingslib.core.lifecycle.LifecycleObserver; -import com.android.settingslib.core.lifecycle.events.OnStart; -import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.wifi.AccessPoint; import com.android.settingslib.wifi.AccessPointPreference; import com.android.settingslib.wifi.AccessPointPreference.UserBadgeCache; @@ -38,29 +32,28 @@ import com.android.settingslib.wifi.WifiSavedConfigUtils; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; /** * Controller that manages a PreferenceGroup, which contains a list of subscribed access points. */ -// TODO(b/127206629): Code refactor to avoid duplicated coding after removed feature flag. public class SubscribedAccessPointsPreferenceController extends BasePreferenceController implements - LifecycleObserver, OnStart, Preference.OnPreferenceClickListener, - WifiManager.ActionListener { + Preference.OnPreferenceClickListener { - private static final String TAG = "SubscribedAPPrefCtrl"; + private static final String TAG = "SubscribedApPrefCtrl"; private final WifiManager mWifiManager; - private final PreferenceGroupChildrenCache mChildrenCache; private final UserBadgeCache mUserBadgeCache; private PreferenceGroup mPreferenceGroup; private SavedAccessPointsWifiSettings mHost; + @VisibleForTesting + List mAccessPoints; public SubscribedAccessPointsPreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); mUserBadgeCache = new AccessPointPreference.UserBadgeCache(context.getPackageManager()); mWifiManager = context.getSystemService(WifiManager.class); - mChildrenCache = new PreferenceGroupChildrenCache(); } public SubscribedAccessPointsPreferenceController setHost(SavedAccessPointsWifiSettings host) { @@ -70,89 +63,46 @@ public class SubscribedAccessPointsPreferenceController extends BasePreferenceCo @Override public int getAvailabilityStatus() { - return AVAILABLE; + return mAccessPoints.size() > 0 ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; } @Override public void displayPreference(PreferenceScreen screen) { - super.displayPreference(screen); mPreferenceGroup = screen.findPreference(getPreferenceKey()); - } - - @Override - public void onStart() { refreshSubscribedAccessPoints(); - } - - public void postRefreshSubscribedAccessPoints() { - ThreadUtils.postOnMainThread(() -> refreshSubscribedAccessPoints()); + updatePreference(); + super.displayPreference(screen); } @Override public boolean onPreferenceClick(Preference preference) { if (mHost != null) { - mHost.showWifiPage((AccessPointPreference) preference); + final Preference preferenceInGroup = + mPreferenceGroup.findPreference(preference.getKey()); + mHost.showWifiPage((AccessPointPreference) preferenceInGroup); } return false; } - @Override - public void onSuccess() { - postRefreshSubscribedAccessPoints(); + private void refreshSubscribedAccessPoints() { + mAccessPoints = WifiSavedConfigUtils.getAllConfigs(mContext, mWifiManager).stream() + .filter(accessPoint -> accessPoint.isPasspointConfig()) + .sorted(SavedNetworkComparator.INSTANCE) + .collect(Collectors.toList()); } - @Override - public void onFailure(int reason) { - postRefreshSubscribedAccessPoints(); - } + private void updatePreference() { + mPreferenceGroup.removeAll(); + for (AccessPoint accessPoint : mAccessPoints) { + final String key = accessPoint.getKey(); - @VisibleForTesting - void refreshSubscribedAccessPoints() { - if (mPreferenceGroup == null) { - Log.w(TAG, "PreferenceGroup is null, skipping."); - return; - } + final AccessPointPreference preference = new AccessPointPreference(accessPoint, + mContext, mUserBadgeCache, true /* forSavedNetworks */); + preference.setKey(key); + preference.setIcon(null); + preference.setOnPreferenceClickListener(this); - if (mHost != null && !mHost.isSubscriptionsFeatureEnabled()) { - mPreferenceGroup.setVisible(false); - return; - } - - final Context prefContext = mPreferenceGroup.getContext(); - - final List accessPoints = - WifiSavedConfigUtils.getAllConfigs(mContext, mWifiManager); - Collections.sort(accessPoints, SavedNetworkComparator.INSTANCE); - mChildrenCache.cacheRemoveAllPrefs(mPreferenceGroup); - - final int accessPointsSize = accessPoints.size(); - for (int i = 0; i < accessPointsSize; ++i) { - AccessPoint ap = accessPoints.get(i); - if (!ap.isPasspointConfig()) { - continue; - } - - final String key = ap.getKey(); - AccessPointPreference preference = - (AccessPointPreference) mChildrenCache.getCachedPreference(key); - if (preference == null) { - preference = new AccessPointPreference(ap, prefContext, mUserBadgeCache, true); - preference.setKey(key); - preference.setIcon(null); - preference.setOnPreferenceClickListener(this); - mPreferenceGroup.addPreference(preference); - } - preference.setOrder(i); - } - - mChildrenCache.removeCachedPrefs(mPreferenceGroup); - - if (mPreferenceGroup.getPreferenceCount() < 1) { - Log.w(TAG, "Subscribed networks activity loaded," - + " but there are no subscribed networks!"); - mPreferenceGroup.setVisible(false); - } else { - mPreferenceGroup.setVisible(true); + mPreferenceGroup.addPreference(preference); } } } diff --git a/tests/robotests/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsPreferenceControllerTest.java index 196c97bf152..73e12972884 100644 --- a/tests/robotests/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsPreferenceControllerTest.java @@ -17,11 +17,11 @@ package com.android.settings.wifi.savedaccesspoints; import static com.android.settings.core.BasePreferenceController.AVAILABLE; +import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -30,16 +30,19 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; +import android.os.Bundle; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceScreen; -import com.android.settings.core.FeatureFlags; -import com.android.settings.development.featureflags.FeatureFlagPersistent; import com.android.settings.testutils.shadow.ShadowAccessPoint; import com.android.settings.testutils.shadow.ShadowWifiManager; +import com.android.settingslib.wifi.AccessPoint; import com.android.settingslib.wifi.AccessPointPreference; +import java.util.ArrayList; +import java.util.Arrays; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -79,46 +82,20 @@ public class SavedAccessPointsPreferenceControllerTest { } @Test - public void getAvailability_alwaysAvailable() { + public void getAvailability_noSavedAccessPoint_shouldNotAvailable() { + mController.mAccessPoints = new ArrayList<>(); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); + } + + @Test + public void getAvailability_oneSavedAccessPoint_shouldAvailable() { + final AccessPoint accessPoint = new AccessPoint(mContext, new Bundle() /* savedState */); + mController.mAccessPoints = new ArrayList(Arrays.asList(accessPoint)); + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); } - @Test - public void onStart_shouldRefreshApList() { - doNothing().when(mController).refreshSavedAccessPoints(); - - mController.onStart(); - - verify(mController).refreshSavedAccessPoints(); - } - - @Test - public void postRefresh_shouldRefreshApList() { - doNothing().when(mController).refreshSavedAccessPoints(); - - mController.postRefreshSavedAccessPoints(); - - verify(mController).refreshSavedAccessPoints(); - } - - @Test - public void forget_onSuccess_shouldRefreshApList() { - doNothing().when(mController).refreshSavedAccessPoints(); - - mController.onSuccess(); - - verify(mController).refreshSavedAccessPoints(); - } - - @Test - public void forget_onFailure_shouldRefreshApList() { - doNothing().when(mController).refreshSavedAccessPoints(); - - mController.onFailure(0 /* reason */); - - verify(mController).refreshSavedAccessPoints(); - } - @Test @Config(shadows = ShadowAccessPoint.class) public void refreshSavedAccessPoints_shouldListNonSubscribedAPs() { @@ -131,7 +108,6 @@ public class SavedAccessPointsPreferenceControllerTest { final ArgumentCaptor captor = ArgumentCaptor.forClass(AccessPointPreference.class); mController.displayPreference(mPreferenceScreen); - mController.refreshSavedAccessPoints(); verify(mPreferenceCategory).addPreference(captor.capture()); @@ -142,13 +118,10 @@ public class SavedAccessPointsPreferenceControllerTest { @Test @Config(shadows = ShadowAccessPoint.class) public void refreshSavedAccessPoints_shouldNotListSubscribedAPs() { - FeatureFlagPersistent.setEnabled(mContext, FeatureFlags.NETWORK_INTERNET_V2, true); - mWifiManager.addOrUpdatePasspointConfiguration( SubscribedAccessPointsPreferenceControllerTest.createMockPasspointConfiguration()); mController.displayPreference(mPreferenceScreen); - mController.refreshSavedAccessPoints(); verify(mPreferenceCategory, never()).addPreference(any(AccessPointPreference.class)); } diff --git a/tests/robotests/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettingsTest.java index cda400585b9..923f05ebf07 100644 --- a/tests/robotests/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettingsTest.java +++ b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettingsTest.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.spy; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settingslib.core.AbstractPreferenceController; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/tests/robotests/src/com/android/settings/wifi/savedaccesspoints/SubscribedAccessPointsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints/SubscribedAccessPointsPreferenceControllerTest.java index 581f5314504..f9307db3766 100644 --- a/tests/robotests/src/com/android/settings/wifi/savedaccesspoints/SubscribedAccessPointsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints/SubscribedAccessPointsPreferenceControllerTest.java @@ -17,11 +17,11 @@ package com.android.settings.wifi.savedaccesspoints; import static com.android.settings.core.BasePreferenceController.AVAILABLE; +import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -32,16 +32,19 @@ import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.hotspot2.pps.HomeSp; +import android.os.Bundle; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceScreen; -import com.android.settings.core.FeatureFlags; -import com.android.settings.development.featureflags.FeatureFlagPersistent; import com.android.settings.testutils.shadow.ShadowAccessPoint; import com.android.settings.testutils.shadow.ShadowWifiManager; +import com.android.settingslib.wifi.AccessPoint; import com.android.settingslib.wifi.AccessPointPreference; +import java.util.ArrayList; +import java.util.Arrays; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -78,51 +81,23 @@ public class SubscribedAccessPointsPreferenceControllerTest { when(mPreferenceScreen.findPreference(mController.getPreferenceKey())) .thenReturn(mPreferenceCategory); when(mPreferenceCategory.getContext()).thenReturn(mContext); - - FeatureFlagPersistent.setEnabled(mContext, FeatureFlags.NETWORK_INTERNET_V2, true); } @Test - public void getAvailability_alwaysAvailable() { + public void getAvailability_noSavedAccessPoint_shouldNotAvailable() { + mController.mAccessPoints = new ArrayList<>(); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); + } + + @Test + public void getAvailability_oneSavedAccessPoint_shouldAvailable() { + final AccessPoint accessPoint = new AccessPoint(mContext, new Bundle() /* savedState */); + mController.mAccessPoints = new ArrayList(Arrays.asList(accessPoint)); + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); } - @Test - public void onStart_shouldRefreshApList() { - doNothing().when(mController).refreshSubscribedAccessPoints(); - - mController.onStart(); - - verify(mController).refreshSubscribedAccessPoints(); - } - - @Test - public void postRefresh_shouldRefreshApList() { - doNothing().when(mController).refreshSubscribedAccessPoints(); - - mController.postRefreshSubscribedAccessPoints(); - - verify(mController).refreshSubscribedAccessPoints(); - } - - @Test - public void forget_onSuccess_shouldRefreshApList() { - doNothing().when(mController).refreshSubscribedAccessPoints(); - - mController.onSuccess(); - - verify(mController).refreshSubscribedAccessPoints(); - } - - @Test - public void forget_onFailure_shouldRefreshApList() { - doNothing().when(mController).refreshSubscribedAccessPoints(); - - mController.onFailure(0 /* reason */); - - verify(mController).refreshSubscribedAccessPoints(); - } - @Test @Config(shadows = ShadowAccessPoint.class) public void refreshSubscribedAccessPoints_shouldNotListNonSubscribedAPs() { @@ -133,7 +108,6 @@ public class SubscribedAccessPointsPreferenceControllerTest { mWifiManager.addNetwork(config); mController.displayPreference(mPreferenceScreen); - mController.refreshSubscribedAccessPoints(); verify(mPreferenceCategory, never()).addPreference(any(AccessPointPreference.class)); } @@ -144,7 +118,6 @@ public class SubscribedAccessPointsPreferenceControllerTest { mWifiManager.addOrUpdatePasspointConfiguration(createMockPasspointConfiguration()); mController.displayPreference(mPreferenceScreen); - mController.refreshSubscribedAccessPoints(); final ArgumentCaptor captor = ArgumentCaptor.forClass(AccessPointPreference.class);