import android.content.Context;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.core.BasePreferenceController;
+import com.android.settings.wifi.WifiConnectionPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import androidx.annotation.VisibleForTesting;
// are two or more active mobile subscriptions. It shows an overview of available network
// connections with an entry for wifi (if connected) and an entry for each subscription.
public class MultiNetworkHeaderController extends BasePreferenceController implements
+ WifiConnectionPreferenceController.UpdateListener,
SubscriptionsPreferenceController.UpdateListener {
public static final String TAG = "MultiNetworkHdrCtrl";
+ private WifiConnectionPreferenceController mWifiController;
private SubscriptionsPreferenceController mSubscriptionsController;
private PreferenceCategory mPreferenceCategory;
}
public void init(Lifecycle lifecycle) {
+ mWifiController = createWifiController(lifecycle);
mSubscriptionsController = createSubscriptionsController(lifecycle);
- // TODO(asargent) - add in a controller for showing wifi status here
+ }
+
+ @VisibleForTesting
+ WifiConnectionPreferenceController createWifiController(Lifecycle lifecycle) {
+ final int prefOrder = 0;
+ return new WifiConnectionPreferenceController(mContext, lifecycle, this, mPreferenceKey,
+ prefOrder, MetricsProto.MetricsEvent.SETTINGS_NETWORK_CATEGORY);
}
@VisibleForTesting
SubscriptionsPreferenceController createSubscriptionsController(Lifecycle lifecycle) {
- return new SubscriptionsPreferenceController(mContext, lifecycle, this, mPreferenceKey, 10);
+ final int prefStartOrder = 10;
+ return new SubscriptionsPreferenceController(mContext, lifecycle, this, mPreferenceKey,
+ prefStartOrder);
}
@Override
super.displayPreference(screen);
mPreferenceCategory = (PreferenceCategory) screen.findPreference(mPreferenceKey);
mPreferenceCategory.setVisible(isAvailable());
+ mWifiController.displayPreference(screen);
mSubscriptionsController.displayPreference(screen);
}
--- /dev/null
+/*
+ * Copyright (C) 2018 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;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import com.android.settings.R;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.wifi.details.WifiNetworkDetailsFragment;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.AccessPointPreference;
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTrackerFactory;
+
+import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceScreen;
+
+/**
+ * This places a preference into a PreferenceGroup owned by some parent
+ * controller class when there is a wifi connection present.
+ */
+public class WifiConnectionPreferenceController extends AbstractPreferenceController implements
+ WifiTracker.WifiListener {
+
+ private static final String TAG = "WifiConnPrefCtrl";
+
+ private static final String KEY = "active_wifi_connection";
+
+ private UpdateListener mUpdateListener;
+ private Context mPrefContext;
+ private String mPreferenceGroupKey;
+ private PreferenceGroup mPreferenceGroup;
+ private WifiTracker mWifiTracker;
+ private AccessPointPreference mPreference;
+ private AccessPointPreference.UserBadgeCache mBadgeCache;
+ private int order;
+ private int mMetricsCategory;
+
+ /**
+ * Used to notify a parent controller that this controller has changed in availability, or has
+ * updated the content in the preference that it manages.
+ */
+ public interface UpdateListener {
+ void onChildrenUpdated();
+ }
+
+ /**
+ * @param context the context for the UI where we're placing the preference
+ * @param lifecycle for listening to lifecycle events for the UI
+ * @param updateListener for notifying a parent controller of changes
+ * @param preferenceGroupKey the key to use to lookup the PreferenceGroup where this controller
+ * will add its preference
+ * @param order the order that the preference added by this controller should use -
+ * useful when this preference needs to be ordered in a specific way
+ * relative to others in the PreferenceGroup
+ * @param metricsCategory - the category to use as the source when handling the click on the
+ * pref to go to the wifi connection detail page
+ */
+ public WifiConnectionPreferenceController(Context context, Lifecycle lifecycle,
+ UpdateListener updateListener, String preferenceGroupKey, int order,
+ int metricsCategory) {
+ super(context);
+ mUpdateListener = updateListener;
+ mPreferenceGroupKey = preferenceGroupKey;
+ mWifiTracker = WifiTrackerFactory.create(context, this, lifecycle, true /* includeSaved */,
+ true /* includeScans */);
+ this.order = order;
+ mMetricsCategory = metricsCategory;
+ mBadgeCache = new AccessPointPreference.UserBadgeCache(context.getPackageManager());
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return mWifiTracker.isConnected() && getCurrentAccessPoint() != null;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreferenceGroup = (PreferenceGroup) screen.findPreference(mPreferenceGroupKey);
+ mPrefContext = screen.getContext();
+ update();
+ }
+
+ private AccessPoint getCurrentAccessPoint() {
+ for (AccessPoint accessPoint : mWifiTracker.getAccessPoints()) {
+ if (accessPoint.isActive()) {
+ return accessPoint;
+ }
+ }
+ return null;
+ }
+
+ private void updatePreference(AccessPoint accessPoint) {
+ if (mPreference != null) {
+ mPreferenceGroup.removePreference(mPreference);
+ mPreference = null;
+ }
+ if (accessPoint == null) {
+ return;
+ }
+ if (mPrefContext != null) {
+ mPreference = new AccessPointPreference(accessPoint, mPrefContext, mBadgeCache,
+ R.drawable.ic_wifi_signal_0, false /* forSavedNetworks */);
+ mPreference.setKey(KEY);
+ mPreference.refresh();
+ mPreference.setOrder(order);
+
+ mPreference.setOnPreferenceClickListener(pref -> {
+ Bundle args = new Bundle();
+ mPreference.getAccessPoint().saveWifiState(args);
+ new SubSettingLauncher(mPrefContext)
+ .setTitleRes(R.string.pref_title_network_details)
+ .setDestination(WifiNetworkDetailsFragment.class.getName())
+ .setArguments(args)
+ .setSourceMetricsCategory(mMetricsCategory)
+ .launch();
+ return true;
+ });
+ mPreferenceGroup.addPreference(mPreference);
+ }
+ }
+
+ private void update() {
+ AccessPoint connectedAccessPoint = null;
+ if (mWifiTracker.isConnected()) {
+ connectedAccessPoint = getCurrentAccessPoint();
+ }
+ if (connectedAccessPoint == null) {
+ updatePreference(null);
+ } else {
+ if (mPreference == null || !mPreference.getAccessPoint().equals(connectedAccessPoint)) {
+ updatePreference(connectedAccessPoint);
+ } else if (mPreference != null) {
+ mPreference.refresh();
+ }
+ }
+ mUpdateListener.onChildrenUpdated();
+ }
+
+ @Override
+ public void onWifiStateChanged(int state) {
+ update();
+ }
+
+ @Override
+ public void onConnectedChanged() {
+ update();
+ }
+
+ @Override
+ public void onAccessPointsChanged() {
+ update();
+ }
+}
import android.content.Context;
import android.telephony.SubscriptionManager;
+import com.android.settings.wifi.WifiConnectionPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@Mock
private PreferenceCategory mPreferenceCategory;
@Mock
+ private WifiConnectionPreferenceController mWifiController;
+ @Mock
private SubscriptionsPreferenceController mSubscriptionsController;
@Mock
private SubscriptionManager mSubscriptionManager;
when(mPreferenceScreen.findPreference(eq(KEY_HEADER))).thenReturn(mPreferenceCategory);
mHeaderController = spy(new MultiNetworkHeaderController(mContext, KEY_HEADER));
+ doReturn(mWifiController).when(mHeaderController).createWifiController(mLifecycle);
doReturn(mSubscriptionsController).when(mHeaderController).createSubscriptionsController(
mLifecycle);
}
// When calling displayPreference, the header itself should only be visible if the
// subscriptions controller says it is available. This is a helper for test cases of this logic.
- private void displayPreferenceTest(boolean subscriptionsAvailable,
+ private void displayPreferenceTest(boolean wifiAvailable, boolean subscriptionsAvailable,
boolean setVisibleExpectedValue) {
+ when(mWifiController.isAvailable()).thenReturn(wifiAvailable);
when(mSubscriptionsController.isAvailable()).thenReturn(subscriptionsAvailable);
mHeaderController.init(mLifecycle);
}
@Test
- public void displayPreference_subscriptionsNotAvailable_categoryIsNotVisible() {
- displayPreferenceTest(false, false);
+ public void displayPreference_bothNotAvailable_categoryIsNotVisible() {
+ displayPreferenceTest(false, false, false);
+ }
+
+ @Test
+ public void displayPreference_wifiAvailableButNotSubscriptions_categoryIsNotVisible() {
+ displayPreferenceTest(true, false, false);
+ }
+
+ @Test
+ public void displayPreference_subscriptionsAvailableButNotWifi_categoryIsVisible() {
+ displayPreferenceTest(false, true, true);
}
@Test
- public void displayPreference_subscriptionsAvailable_categoryIsVisible() {
- displayPreferenceTest(true, true);
+ public void displayPreference_bothAvailable_categoryIsVisible() {
+ displayPreferenceTest(true, true, true);
}
@Test
--- /dev/null
+/*
+ * Copyright (C) 2018 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.network;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+
+import com.android.settings.wifi.WifiConnectionPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.AccessPointPreference;
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTrackerFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+@RunWith(RobolectricTestRunner.class)
+public class WifiConnectionPreferenceControllerTest {
+ private static final String KEY = "wifi_connection";
+
+ @Mock
+ WifiTracker mWifiTracker;
+ @Mock
+ PreferenceScreen mScreen;
+ @Mock
+ PreferenceCategory mPreferenceCategory;
+
+ private Context mContext;
+ private LifecycleOwner mLifecycleOwner;
+ private Lifecycle mLifecycle;
+ private WifiConnectionPreferenceController mController;
+ private int mOnChildUpdatedCount;
+ private WifiConnectionPreferenceController.UpdateListener mUpdateListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ WifiTrackerFactory.setTestingWifiTracker(mWifiTracker);
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
+ when(mScreen.findPreference(eq(KEY))).thenReturn(mPreferenceCategory);
+ when(mScreen.getContext()).thenReturn(mContext);
+ mUpdateListener = () -> mOnChildUpdatedCount++;
+
+ mController = new WifiConnectionPreferenceController(mContext, mLifecycle, mUpdateListener,
+ KEY, 0, 0);
+ }
+
+ @Test
+ public void isAvailable_noWiFiConnection_availableIsFalse() {
+ when(mWifiTracker.isConnected()).thenReturn(false);
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void displayPreference_noWiFiConnection_noPreferenceAdded() {
+ when(mWifiTracker.isConnected()).thenReturn(false);
+ when(mWifiTracker.getAccessPoints()).thenReturn(new ArrayList<>());
+ mController.displayPreference(mScreen);
+ verify(mPreferenceCategory, never()).addPreference(any());
+ }
+
+ @Test
+ public void displayPreference_hasWiFiConnection_preferenceAdded() {
+ when(mWifiTracker.isConnected()).thenReturn(true);
+ final AccessPoint accessPoint = mock(AccessPoint.class);
+ when(accessPoint.isActive()).thenReturn(true);
+ when(mWifiTracker.getAccessPoints()).thenReturn(Arrays.asList(accessPoint));
+ mController.displayPreference(mScreen);
+ verify(mPreferenceCategory).addPreference(any(AccessPointPreference.class));
+ }
+
+ @Test
+ public void onConnectedChanged_wifiBecameDisconnected_preferenceRemoved() {
+ when(mWifiTracker.isConnected()).thenReturn(true);
+ final AccessPoint accessPoint = mock(AccessPoint.class);
+
+ when(accessPoint.isActive()).thenReturn(true);
+ when(mWifiTracker.getAccessPoints()).thenReturn(Arrays.asList(accessPoint));
+ mController.displayPreference(mScreen);
+ final ArgumentCaptor<AccessPointPreference> captor = ArgumentCaptor.forClass(
+ AccessPointPreference.class);
+ verify(mPreferenceCategory).addPreference(captor.capture());
+ final AccessPointPreference pref = captor.getValue();
+
+ when(mWifiTracker.isConnected()).thenReturn(false);
+ when(mWifiTracker.getAccessPoints()).thenReturn(new ArrayList<>());
+ final int onUpdatedCountBefore = mOnChildUpdatedCount;
+ mController.onConnectedChanged();
+ verify(mPreferenceCategory).removePreference(pref);
+ assertThat(mOnChildUpdatedCount).isEqualTo(onUpdatedCountBefore + 1);
+ }
+
+
+ @Test
+ public void onAccessPointsChanged_wifiBecameConnectedToDifferentAP_preferenceReplaced() {
+ when(mWifiTracker.isConnected()).thenReturn(true);
+ final AccessPoint accessPoint1 = mock(AccessPoint.class);
+
+ when(accessPoint1.isActive()).thenReturn(true);
+ when(mWifiTracker.getAccessPoints()).thenReturn(Arrays.asList(accessPoint1));
+ mController.displayPreference(mScreen);
+ final ArgumentCaptor<AccessPointPreference> captor = ArgumentCaptor.forClass(
+ AccessPointPreference.class);
+
+
+ final AccessPoint accessPoint2 = mock(AccessPoint.class);
+ when(accessPoint1.isActive()).thenReturn(false);
+ when(accessPoint2.isActive()).thenReturn(true);
+ when(mWifiTracker.getAccessPoints()).thenReturn(Arrays.asList(accessPoint1, accessPoint2));
+ final int onUpdatedCountBefore = mOnChildUpdatedCount;
+ mController.onAccessPointsChanged();
+
+ verify(mPreferenceCategory, times(2)).addPreference(captor.capture());
+ final AccessPointPreference pref1 = captor.getAllValues().get(0);
+ final AccessPointPreference pref2 = captor.getAllValues().get(1);
+ assertThat(pref1.getAccessPoint()).isEqualTo(accessPoint1);
+ assertThat(pref2.getAccessPoint()).isEqualTo(accessPoint2);
+ verify(mPreferenceCategory).removePreference(eq(pref1));
+ assertThat(mOnChildUpdatedCount).isEqualTo(onUpdatedCountBefore + 1);
+ }
+}