Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 82a3f258 authored by Arc Wang's avatar Arc Wang
Browse files

[Wi-Fi] Replace WifiTracker with WifiTracker2 for SavedAccessPointsWifiSettings2

Use SavedNetworkTracker instead of WifiTracker, it provides Wi-Fi signal information
for saved networks. This is the main UX difference of this change.

Bug: 144543677
Test: make RunSettingsRoboTests ROBOTEST_FILTER=com.android.settings.wifi.
Change-Id: I546ad08ffd2f6f736023cb1f3e1cb344a5842e14
parent 9096329b
Loading
Loading
Loading
Loading
+19 −30
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package com.android.settings.wifi.savedaccesspoints2;

import android.content.Context;
import android.net.wifi.WifiManager;

import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
@@ -25,13 +24,11 @@ import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;

import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.wifi.AccessPoint;
import com.android.settingslib.wifi.AccessPointPreference;
import com.android.settingslib.wifi.AccessPointPreference.UserBadgeCache;
import com.android.settingslib.wifi.WifiSavedConfigUtils;
import com.android.settingslib.wifi.WifiEntryPreference;
import com.android.wifitrackerlib.WifiEntry;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Controller that manages a PreferenceGroup, which contains a list of saved access points.
@@ -39,17 +36,13 @@ import java.util.stream.Collectors;
public class SavedAccessPointsPreferenceController2 extends BasePreferenceController implements
        Preference.OnPreferenceClickListener {

    protected final WifiManager mWifiManager;
    private final UserBadgeCache mUserBadgeCache;
    private PreferenceGroup mPreferenceGroup;
    private SavedAccessPointsWifiSettings2 mHost;
    @VisibleForTesting
    List<AccessPoint> mAccessPoints;
    List<WifiEntry> mWifiEntries = new ArrayList<>();

    public SavedAccessPointsPreferenceController2(Context context, String preferenceKey) {
        super(context, preferenceKey);
        mUserBadgeCache = new AccessPointPreference.UserBadgeCache(context.getPackageManager());
        mWifiManager = context.getSystemService(WifiManager.class);
    }

    /**
@@ -62,43 +55,39 @@ public class SavedAccessPointsPreferenceController2 extends BasePreferenceContro

    @Override
    public int getAvailabilityStatus() {
        return mAccessPoints.size() > 0 ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
        return mWifiEntries.size() > 0 ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
    }

    @Override
    public void displayPreference(PreferenceScreen screen) {
        mPreferenceGroup = screen.findPreference(getPreferenceKey());
        refreshSavedAccessPoints();
        updatePreference();
        super.displayPreference(screen);
    }

    void displayPreference(PreferenceScreen screen, List<WifiEntry> wifiEntries) {
        if (wifiEntries == null || wifiEntries.isEmpty()) {
            mWifiEntries.clear();
        } else {
            mWifiEntries = wifiEntries;
        }

        displayPreference(screen);
    }

    @Override
    public boolean onPreferenceClick(Preference preference) {
        if (mHost != null) {
            final Preference preferenceInGroup =
                    mPreferenceGroup.findPreference(preference.getKey());
            mHost.showWifiPage((AccessPointPreference) preferenceInGroup);
            mHost.showWifiPage(preference.getKey(), preference.getTitle());
        }
        return false;
    }

    protected void refreshSavedAccessPoints() {
        mAccessPoints = WifiSavedConfigUtils.getAllConfigs(mContext, mWifiManager).stream()
                .filter(accessPoint -> !accessPoint.isPasspointConfig())
                .sorted(SavedNetworkComparator2.INSTANCE)
                .collect(Collectors.toList());
    }

    private void updatePreference() {
        mPreferenceGroup.removeAll();
        for (AccessPoint accessPoint : mAccessPoints) {
            final String key = accessPoint.getKey();

            final AccessPointPreference preference = new AccessPointPreference(accessPoint,
                    mContext, mUserBadgeCache, true /* forSavedNetworks */);
            preference.setKey(key);
            preference.setIcon(null);
        for (WifiEntry wifiEntry : mWifiEntries) {
            final WifiEntryPreference preference = new WifiEntryPreference(mContext, wifiEntry);
            preference.setKey(wifiEntry.getKey());
            preference.setOnPreferenceClickListener(this);

            mPreferenceGroup.addPreference(preference);
+86 −52
Original line number Diff line number Diff line
@@ -16,12 +16,22 @@

package com.android.settings.wifi.savedaccesspoints2;

import android.annotation.Nullable;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkScoreManager;
import android.net.wifi.WifiManager;
import android.os.Bundle;

import androidx.annotation.VisibleForTesting;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
import android.os.SimpleClock;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.preference.PreferenceScreen;

import com.android.settings.R;
@@ -29,22 +39,29 @@ import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.wifi.WifiSettings;
import com.android.settings.wifi.details2.WifiNetworkDetailsFragment2;
import com.android.settingslib.wifi.AccessPoint;
import com.android.settingslib.wifi.AccessPointPreference;
import com.android.wifitrackerlib.SavedNetworkTracker;

import java.time.Clock;
import java.time.ZoneOffset;

/**
 * UI to manage saved networks/access points.
 */
public class SavedAccessPointsWifiSettings2 extends DashboardFragment {
public class SavedAccessPointsWifiSettings2 extends DashboardFragment
        implements SavedNetworkTracker.SavedNetworkTrackerCallback {

    private static final String TAG = "SavedAccessPoints2";

    @VisibleForTesting
    Bundle mAccessPointSavedState;
    private AccessPoint mSelectedAccessPoint;
    // Key of a Bundle to save/restore the selected WifiEntry
    static final String KEY_KEY = "key_key";

    // Max age of tracked WifiEntries
    private static final long MAX_SCAN_AGE_MILLIS = 15_000;
    // Interval between initiating SavedNetworkTracker scans
    private static final long SCAN_INTERVAL_MILLIS = 10_000;

    // Instance state key
    private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state";
    private SavedNetworkTracker mSavedNetworkTracker;
    private HandlerThread mWorkerThread;

    @Override
    public int getMetricsCategory() {
@@ -64,73 +81,90 @@ public class SavedAccessPointsWifiSettings2 extends DashboardFragment {
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        use(SavedAccessPointsPreferenceController2.class)
                .setHost(this);
        use(SubscribedAccessPointsPreferenceController2.class)
                .setHost(this);
        use(SavedAccessPointsPreferenceController2.class).setHost(this);
        use(SubscribedAccessPointsPreferenceController2.class).setHost(this);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            if (savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) {
                mAccessPointSavedState =
                        savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE);
            } else {
                mAccessPointSavedState = null;
            }

        final Context context = getContext();
        mWorkerThread = new HandlerThread(TAG
                + "{" + Integer.toHexString(System.identityHashCode(this)) + "}",
                Process.THREAD_PRIORITY_BACKGROUND);
        mWorkerThread.start();
        final Clock elapsedRealtimeClock = new SimpleClock(ZoneOffset.UTC) {
            @Override
            public long millis() {
                return SystemClock.elapsedRealtime();
            }
        };
        mSavedNetworkTracker = new SavedNetworkTracker(getSettingsLifecycle(), context,
                context.getSystemService(WifiManager.class),
                context.getSystemService(ConnectivityManager.class),
                context.getSystemService(NetworkScoreManager.class),
                new Handler(Looper.getMainLooper()),
                mWorkerThread.getThreadHandler(),
                elapsedRealtimeClock,
                MAX_SCAN_AGE_MILLIS,
                SCAN_INTERVAL_MILLIS,
                this);
    }

    @Override
    public void onStart() {
        super.onStart();
        if (mAccessPointSavedState != null) {
            final PreferenceScreen screen = getPreferenceScreen();
            use(SavedAccessPointsPreferenceController2.class).displayPreference(screen);
            use(SubscribedAccessPointsPreferenceController2.class).displayPreference(screen);

        onSavedWifiEntriesChanged();
        onSubscriptionWifiEntriesChanged();
    }

    @Override
    public void onDestroy() {
        mWorkerThread.quit();

        super.onDestroy();
    }

    /**
     * Shows {@link WifiNetworkDetailsFragment2} for assigned {@link AccessPointPreference}.
     * Shows {@link WifiNetworkDetailsFragment2} for assigned key of {@link WifiEntry}.
     */
    public void showWifiPage(@Nullable AccessPointPreference accessPoint) {
    public void showWifiPage(@NonNull String key, CharSequence title) {
        removeDialog(WifiSettings.WIFI_DIALOG_ID);

        if (accessPoint != null) {
            // Save the access point and edit mode
            mSelectedAccessPoint = accessPoint.getAccessPoint();
        } else {
            // No access point is selected. Clear saved state.
            mSelectedAccessPoint = null;
            mAccessPointSavedState = null;
        if (TextUtils.isEmpty(key)) {
            Log.e(TAG, "Not able to show WifiEntry of an empty key");
            return;
        }

        if (mSelectedAccessPoint == null) {
            mSelectedAccessPoint = new AccessPoint(getActivity(), mAccessPointSavedState);
        }
        final Bundle savedState = new Bundle();
        mSelectedAccessPoint.saveWifiState(savedState);
        final Bundle bundle = new Bundle();
        bundle.putString(KEY_KEY, key);

        new SubSettingLauncher(getContext())
                .setTitleText(mSelectedAccessPoint.getTitle())
                .setTitleText(title)
                .setDestination(WifiNetworkDetailsFragment2.class.getName())
                .setArguments(savedState)
                .setArguments(bundle)
                .setSourceMetricsCategory(getMetricsCategory())
                .launch();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        // If the dialog is showing (indicated by the existence of mSelectedAccessPoint), then we
        // save its state.
        if (mSelectedAccessPoint != null) {
            mAccessPointSavedState = new Bundle();
            mSelectedAccessPoint.saveWifiState(mAccessPointSavedState);
            outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState);
    public void onWifiStateChanged() {
        // Do nothing.
    }

    @Override
    public void onSavedWifiEntriesChanged() {
        final PreferenceScreen screen = getPreferenceScreen();
        use(SavedAccessPointsPreferenceController2.class)
                .displayPreference(screen, mSavedNetworkTracker.getSavedWifiEntries());
    }

    @Override
    public void onSubscriptionWifiEntriesChanged() {
        final PreferenceScreen screen = getPreferenceScreen();
        use(SubscribedAccessPointsPreferenceController2.class)
                .displayPreference(screen, mSavedNetworkTracker.getSubscriptionWifiEntries());
    }
}
+0 −43
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.savedaccesspoints2;

import android.icu.text.Collator;

import com.android.settingslib.wifi.AccessPoint;

import java.util.Comparator;

/**
 * For {@link AccessPoint} sorting before desplaying.
 */
public final class SavedNetworkComparator2 {
    public static final Comparator<AccessPoint> INSTANCE =
            new Comparator<AccessPoint>() {
                final Collator mCollator = Collator.getInstance();

                @Override
                public int compare(AccessPoint ap1, AccessPoint ap2) {
                    return mCollator.compare(
                            nullToEmpty(ap1.getTitle()), nullToEmpty(ap2.getTitle()));
                }

                private String nullToEmpty(String string) {
                    return (string == null) ? "" : string;
                }
            };
}
+0 −12
Original line number Diff line number Diff line
@@ -18,10 +18,6 @@ package com.android.settings.wifi.savedaccesspoints2;

import android.content.Context;

import com.android.settingslib.wifi.WifiSavedConfigUtils;

import java.util.stream.Collectors;

/**
 * Controller that manages a PreferenceGroup, which contains a list of subscribed access points.
 */
@@ -31,12 +27,4 @@ public class SubscribedAccessPointsPreferenceController2 extends
    public SubscribedAccessPointsPreferenceController2(Context context, String preferenceKey) {
        super(context, preferenceKey);
    }

    @Override
    protected void refreshSavedAccessPoints() {
        mAccessPoints = WifiSavedConfigUtils.getAllConfigs(mContext, mWifiManager).stream()
                .filter(accessPoint -> accessPoint.isPasspointConfig())
                .sorted(SavedNetworkComparator2.INSTANCE)
                .collect(Collectors.toList());
    }
}
+18 −40
Original line number Diff line number Diff line
@@ -21,24 +21,18 @@ import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_U

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
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.testutils.shadow.ShadowAccessPoint;
import com.android.settings.testutils.shadow.ShadowWifiManager;
import com.android.settingslib.wifi.AccessPoint;
import com.android.settingslib.wifi.AccessPointPreference;
import com.android.settingslib.wifi.WifiEntryPreference;
import com.android.wifitrackerlib.WifiEntry;

import org.junit.Before;
import org.junit.Test;
@@ -48,13 +42,12 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowWifiManager.class})
public class SavedAccessPointsPreferenceController2Test {

    @Mock
@@ -63,7 +56,6 @@ public class SavedAccessPointsPreferenceController2Test {
    private PreferenceCategory mPreferenceCategory;

    private Context mContext;
    private WifiManager mWifiManager;
    private SavedAccessPointsWifiSettings2 mSettings;
    private SavedAccessPointsPreferenceController2 mController;

@@ -71,7 +63,6 @@ public class SavedAccessPointsPreferenceController2Test {
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mContext = RuntimeEnvironment.application;
        mWifiManager = mContext.getSystemService(WifiManager.class);
        mSettings = spy(new SavedAccessPointsWifiSettings2());
        mController = spy(new SavedAccessPointsPreferenceController2(mContext, "test_key"));
        mController.setHost(mSettings);
@@ -83,46 +74,33 @@ public class SavedAccessPointsPreferenceController2Test {

    @Test
    public void getAvailability_noSavedAccessPoint_shouldNotAvailable() {
        mController.mAccessPoints = new ArrayList<>();
        mController.mWifiEntries = 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<AccessPoint>(Arrays.asList(accessPoint));
        final WifiEntry mockWifiEntry = mock(WifiEntry.class);
        mController.mWifiEntries = Arrays.asList(mockWifiEntry);

        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
    }

    @Test
    @Config(shadows = ShadowAccessPoint.class)
    public void displayPreference_oneAccessPoint_shouldListNonSubscribedAPs() {
        final WifiConfiguration config = new WifiConfiguration();
        config.SSID = "SSID";
        config.BSSID = "BSSID";
        config.networkId = 2;
        mWifiManager.addNetwork(config);

        final ArgumentCaptor<AccessPointPreference> captor =
                ArgumentCaptor.forClass(AccessPointPreference.class);
        mController.displayPreference(mPreferenceScreen);
    public void displayPreference_oneAccessPoint_shouldListIt() {
        final String title = "ssid_title";
        final WifiEntry mockWifiEntry = mock(WifiEntry.class);
        when(mockWifiEntry.getTitle()).thenReturn(title);
        final ArgumentCaptor<WifiEntryPreference> captor =
                ArgumentCaptor.forClass(WifiEntryPreference.class);

        verify(mPreferenceCategory).addPreference(captor.capture());

        final AccessPointPreference pref = captor.getValue();
        assertThat(pref.getTitle()).isEqualTo(config.SSID);
    }
        mController.displayPreference(mPreferenceScreen, Arrays.asList(mockWifiEntry));

    @Test
    @Config(shadows = ShadowAccessPoint.class)
    public void displayPreference_onePasspoint_shouldNotListSubscribedAPs() {
        mWifiManager.addOrUpdatePasspointConfiguration(
                SubscribedAccessPointsPreferenceController2Test.createMockPasspointConfiguration());

        mController.displayPreference(mPreferenceScreen);
        verify(mPreferenceCategory).addPreference(captor.capture());

        verify(mPreferenceCategory, never()).addPreference(any(AccessPointPreference.class));
        final List<WifiEntryPreference> prefs = captor.getAllValues();
        assertThat(prefs.size()).isEqualTo(1);
        assertThat(prefs.get(0).getTitle()).isEqualTo(title);
    }
}
Loading