OSDN Git Service

Add wifi connection info to the multi-network header
authorAntony Sargent <asargent@google.com>
Fri, 14 Dec 2018 20:45:04 +0000 (12:45 -0800)
committerAntony Sargent <asargent@google.com>
Mon, 17 Dec 2018 21:46:40 +0000 (13:46 -0800)
The Network & internet page will have a dynamic header at the top when
users have more than one mobile subscription, showing information about
connectivity.

This CL adds a preference to this header when there is a wifi connection,
showing the same information as on the wifi-page (connection strength,
speed rating if available, etc.).

Bug: 116349402
Test: make RunSettingsRoboTests
Change-Id: Ia80d6e236a4996b501372ac4cd8e46ba6c5f8841

src/com/android/settings/network/MultiNetworkHeaderController.java
src/com/android/settings/wifi/WifiConnectionPreferenceController.java [new file with mode: 0644]
tests/robotests/src/com/android/settings/network/MultiNetworkHeaderControllerTest.java
tests/robotests/src/com/android/settings/network/WifiConnectionPreferenceControllerTest.java [new file with mode: 0644]

index 881aaa2..8860c47 100644 (file)
@@ -18,7 +18,9 @@ package com.android.settings.network;
 
 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;
@@ -29,9 +31,11 @@ import androidx.preference.PreferenceScreen;
 // 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;
 
@@ -40,13 +44,22 @@ public class MultiNetworkHeaderController extends BasePreferenceController imple
     }
 
     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
@@ -54,6 +67,7 @@ public class MultiNetworkHeaderController extends BasePreferenceController imple
         super.displayPreference(screen);
         mPreferenceCategory = (PreferenceCategory) screen.findPreference(mPreferenceKey);
         mPreferenceCategory.setVisible(isAvailable());
+        mWifiController.displayPreference(screen);
         mSubscriptionsController.displayPreference(screen);
     }
 
diff --git a/src/com/android/settings/wifi/WifiConnectionPreferenceController.java b/src/com/android/settings/wifi/WifiConnectionPreferenceController.java
new file mode 100644 (file)
index 0000000..b73bce9
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * 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();
+    }
+}
index fbd7867..b4ebcc4 100644 (file)
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.when;
 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;
@@ -55,6 +56,8 @@ public class MultiNetworkHeaderControllerTest {
     @Mock
     private PreferenceCategory mPreferenceCategory;
     @Mock
+    private WifiConnectionPreferenceController mWifiController;
+    @Mock
     private SubscriptionsPreferenceController mSubscriptionsController;
     @Mock
     private SubscriptionManager mSubscriptionManager;
@@ -74,6 +77,7 @@ public class MultiNetworkHeaderControllerTest {
         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);
     }
@@ -85,8 +89,9 @@ public class MultiNetworkHeaderControllerTest {
 
     // 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);
@@ -96,13 +101,23 @@ public class MultiNetworkHeaderControllerTest {
     }
 
     @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
diff --git a/tests/robotests/src/com/android/settings/network/WifiConnectionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/WifiConnectionPreferenceControllerTest.java
new file mode 100644 (file)
index 0000000..7037318
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * 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);
+    }
+}