/*
 * Copyright (C) 2020 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 android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLED;
import static android.os.UserManager.DISALLOW_CONFIG_WIFI;

import static com.android.wifitrackerlib.WifiEntry.CONNECTED_STATE_CONNECTED;

import android.app.Activity;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.location.LocationManager;
import android.net.NetworkTemplate;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.core.view.MenuProvider;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import androidx.recyclerview.widget.RecyclerView;

import com.android.settings.AirplaneModeEnabler;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilitySetupWizardUtils;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.RestrictedDashboardFragment;
import com.android.settings.datausage.DataUsagePreference;
import com.android.settings.datausage.DataUsageUtils;
import com.android.settings.location.WifiScanningFragment;
import com.android.settings.network.MobileDataEnabledListener;
import com.android.settings.network.telephony.MobileDataPreferenceController;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.wifi.AddNetworkFragment;
import com.android.settings.wifi.AddWifiNetworkPreference;
import com.android.settings.wifi.ConfigureWifiEntryFragment;
import com.android.settings.wifi.ConnectedWifiEntryPreference;
import com.android.settings.wifi.LongPressWifiEntryPreference;
import com.android.settings.wifi.WifiConfigUiBase2;
import com.android.settings.wifi.WifiDialog2;
import com.android.settings.wifi.WifiPickerTrackerHelper;
import com.android.settings.wifi.WifiUtils;
import com.android.settings.wifi.details.WifiNetworkDetailsFragment;
import com.android.settings.wifi.dpp.WifiDppUtils;
import com.android.settingslib.HelpUtils;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.search.Indexable;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.utils.StringUtil;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.FooterPreference;
import com.android.settingslib.widget.LayoutPreference;
import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
import com.android.settingslib.wifi.WifiSavedConfigUtils;
import com.android.wifi.flags.Flags;
import com.android.wifitrackerlib.WifiEntry;
import com.android.wifitrackerlib.WifiEntry.ConnectCallback;
import com.android.wifitrackerlib.WifiPickerTracker;

import com.google.android.setupcompat.template.FooterButtonStyleUtils;
import com.google.android.setupcompat.util.WizardManagerHelper;
import com.google.android.setupdesign.GlifPreferenceLayout;

import java.util.List;
import java.util.Optional;

/**
 * UI for Mobile network and Wi-Fi network settings.
 */
@SearchIndexable
public class NetworkProviderSettings extends RestrictedDashboardFragment
        implements Indexable, WifiPickerTracker.WifiPickerTrackerCallback,
        WifiDialog2.WifiDialog2Listener, DialogInterface.OnDismissListener,
        AirplaneModeEnabler.OnAirplaneModeChangedListener, InternetUpdater.InternetChangeListener,
        MobileDataEnabledListener.Client {

    private static final String TAG = "NetworkProviderSettings";
    // IDs of context menu
    static final int MENU_ID_CONNECT = Menu.FIRST + 1;
    @VisibleForTesting
    static final int MENU_ID_DISCONNECT = Menu.FIRST + 2;
    @VisibleForTesting
    static final int MENU_ID_FORGET = Menu.FIRST + 3;
    static final int MENU_ID_MODIFY = Menu.FIRST + 4;
    static final int MENU_FIX_CONNECTIVITY = Menu.FIRST + 5;
    static final int MENU_ID_SHARE = Menu.FIRST + 6;

    @VisibleForTesting
    static final int ADD_NETWORK_REQUEST = 2;
    static final int CONFIG_NETWORK_REQUEST = 3;
    static final int MANAGE_SUBSCRIPTION = 4;

    private static final String PREF_KEY_AIRPLANE_MODE_MSG = "airplane_mode_message";
    private static final String PREF_KEY_EMPTY_WIFI_LIST = "wifi_empty_list";
    @VisibleForTesting
    static final String PREF_KEY_WIFI_TOGGLE = "main_toggle_wifi";
    // TODO(b/70983952): Rename these to use WifiEntry instead of AccessPoint.
    @VisibleForTesting
    static final String PREF_KEY_CONNECTED_ACCESS_POINTS = "connected_access_point";
    @VisibleForTesting
    static final String PREF_KEY_FIRST_ACCESS_POINTS = "first_access_points";
    private static final String PREF_KEY_ACCESS_POINTS = "access_points";
    @VisibleForTesting
    static final String PREF_KEY_ADD_WIFI_NETWORK = "add_wifi_network";
    private static final String PREF_KEY_CONFIGURE_NETWORK_SETTINGS = "configure_network_settings";
    private static final String PREF_KEY_SAVED_NETWORKS = "saved_networks";
    @VisibleForTesting
    static final String PREF_KEY_DATA_USAGE = "non_carrier_data_usage";
    private static final String PREF_KEY_RESET_INTERNET = "resetting_your_internet";
    private static final String PREF_KEY_WIFI_STATUS_MESSAGE = "wifi_status_message_footer";

    private static final int REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER = 0;

    public static final int WIFI_DIALOG_ID = 1;

    // Instance state keys
    private static final String SAVE_DIALOG_MODE = "dialog_mode";
    private static final String SAVE_DIALOG_WIFIENTRY_KEY = "wifi_ap_key";

    // Cache at onCreateContextMenu and use at onContextItemSelected. Don't use it in other methods.
    private WifiEntry mSelectedWifiEntry;

    // Save the dialog details
    private int mDialogMode;
    private String mDialogWifiEntryKey;
    private WifiEntry mDialogWifiEntry;

    // This boolean extra specifies whether to enable the Next button when connected. Used by
    // account creation outside of setup wizard.
    private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect";

    // Enable the Next button when a Wi-Fi network is connected.
    private boolean mEnableNextOnConnection;

    private boolean mIsInSetupWizard;

    // This string extra specifies a network to open the connect dialog on, so the user can enter
    // network credentials.  This is used by quick settings for secured networks, among other
    // things.
    private static final String EXTRA_START_CONNECT_SSID = "wifi_start_connect_ssid";
    private String mOpenSsid;

    private boolean mIsViewLoading;
    @VisibleForTesting
    final Runnable mRemoveLoadingRunnable = () -> {
        if (mIsViewLoading) {
            setLoading(false, false);
            mIsViewLoading = false;
        }
    };

    @VisibleForTesting
    final Runnable mUpdateWifiEntryPreferencesRunnable = () -> {
        updateWifiEntryPreferences();
        View view = getView();
        if (view != null) {
            view.postDelayed(mRemoveLoadingRunnable, 10);
        }
    };
    @VisibleForTesting
    final Runnable mHideProgressBarRunnable = () -> {
        setProgressBarVisible(false);
    };

    protected WifiManager mWifiManager;
    private WifiManager.ActionListener mSaveListener;

    int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
    MobileDataEnabledListener mDataStateListener;

    protected InternetResetHelper mInternetResetHelper;

    /**
     * The state of {@link #isUiRestricted()} at {@link #onCreate(Bundle)}}. This is necessary to
     * ensure that behavior is consistent if {@link #isUiRestricted()} changes. It could be changed
     * by the Test DPC tool in AFW mode.
     */
    protected boolean mIsRestricted;
    @VisibleForTesting
    boolean mIsAdmin = true;
    @VisibleForTesting
    boolean mIsGuest = false;

    @VisibleForTesting
    AirplaneModeEnabler mAirplaneModeEnabler;
    @VisibleForTesting
    WifiPickerTracker mWifiPickerTracker;
    private WifiPickerTrackerHelper mWifiPickerTrackerHelper;
    @VisibleForTesting
    InternetUpdater mInternetUpdater;

    private WifiDialog2 mDialog;

    @VisibleForTesting
    PreferenceCategory mConnectedWifiEntryPreferenceCategory;
    @VisibleForTesting
    PreferenceCategory mFirstWifiEntryPreferenceCategory;
    @VisibleForTesting
    PreferenceCategory mWifiEntryPreferenceCategory;
    @VisibleForTesting
    AddWifiNetworkPreference mAddWifiNetworkPreference;
    private WifiSwitchPreferenceController mWifiSwitchPreferenceController;
    @VisibleForTesting
    Preference mConfigureWifiSettingsPreference;
    @VisibleForTesting
    Preference mSavedNetworksPreference;
    @VisibleForTesting
    DataUsagePreference mDataUsagePreference;
    @VisibleForTesting
    Preference mAirplaneModeMsgPreference;
    @VisibleForTesting
    LayoutPreference mResetInternetPreference;
    @VisibleForTesting
    ConnectedEthernetNetworkController mConnectedEthernetNetworkController;
    @VisibleForTesting
    FooterPreference mWifiStatusMessagePreference;
    @VisibleForTesting
    MenuProvider mMenuProvider;

    /**
     * Mobile data toggle
     */
    private static final String PREF_KEY_MOBILE_DATA_TOGGLE = "main_toggle_mobile_data";
    private MobileDataPreferenceController mMobileDataPreferenceController;

    /**
     * Mobile networks list for provider model
     */
    private static final String PREF_KEY_PROVIDER_MOBILE_NETWORK = "provider_model_mobile_network";
    private NetworkMobileProviderController mNetworkMobileProviderController;

    /**
     * Tracks whether the user initiated a connection via clicking in order to autoscroll to the
     * network once connected.
     */
    private boolean mClickedConnect;

    public NetworkProviderSettings() {
        super(DISALLOW_CONFIG_WIFI);
        mSubId = SubscriptionManager.getActiveDataSubscriptionId();
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Activity activity = getActivity();
        if (activity == null) {
            return;
        }

        if (mIsInSetupWizard) {
            GlifPreferenceLayout layout = (GlifPreferenceLayout) view;
            final Drawable icon = getContext().getDrawable(R.drawable.ic_network_setup);
            final String title = getContext().getString(R.string.provider_internet_settings);
            AccessibilitySetupWizardUtils.updateGlifPreferenceLayout(getContext(), layout,
                    title, "" /* description */, icon);
            FooterButtonStyleUtils.applyPrimaryButtonPartnerResource(activity, getNextButton(),
                    true);

            return;
        }

        setPinnedHeaderView(com.android.settingslib.widget.progressbar.R.layout.progress_header);
        setProgressBarVisible(false);

        if (hasWifiManager()) {
            setLoading(true, false);
            mIsViewLoading = true;
        }
    }

    private boolean hasWifiManager() {
        if (mWifiManager != null) return true;

        Context context = getContext();
        if (context == null) return false;

        mWifiManager = context.getSystemService(WifiManager.class);
        return (mWifiManager != null);
    }

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        final Context context = getContext();
        final Intent intent = this.getIntent();
        if (context != null && !context.getResources().getBoolean(
                R.bool.config_show_internet_settings)) {
            finish();
            return;
        }
        mAirplaneModeEnabler = new AirplaneModeEnabler(getContext(), this);
        mDataStateListener = new MobileDataEnabledListener(getContext(), this);
        mIsInSetupWizard = WizardManagerHelper.isAnySetupWizard(intent);

        // TODO(b/37429702): Add animations and preference comparator back after initial screen is
        // loaded (ODR).
        setAnimationAllowed(false);

        addPreferences();

        mIsRestricted = isUiRestricted();
        updateUserType();

        mMenuProvider = new MenuProvider() {
            @Override
            public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) {
                MenuItem fixConnectivityItem = menu.add(0, MENU_FIX_CONNECTIVITY, 0,
                        R.string.fix_connectivity);
                fixConnectivityItem.setIcon(R.drawable.ic_repair_24dp);
                fixConnectivityItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
            }

            @Override
            public boolean onMenuItemSelected(@NonNull MenuItem menuItem) {
                if (menuItem.getItemId() == MENU_FIX_CONNECTIVITY) {
                    if (isPhoneOnCall()) {
                        showResetInternetDialog();
                        return true;
                    }
                    fixConnectivity();
                    return true;
                }
                return false;
            }

            @Override
            public void onPrepareMenu(@NonNull Menu menu) {
                MenuProvider.super.onPrepareMenu(menu);

                boolean isWifiEnabled = mWifiPickerTracker != null
                        && mWifiPickerTracker.getWifiState() == WifiManager.WIFI_STATE_ENABLED;
                boolean isAirplaneModeOn =
                        mAirplaneModeEnabler != null && mAirplaneModeEnabler.isAirplaneModeOn();
                MenuItem fixConnectivityItem = menu.findItem(MENU_FIX_CONNECTIVITY);
                if (fixConnectivityItem == null) {
                    return;
                }
                fixConnectivityItem.setVisible(!mIsGuest && (!isAirplaneModeOn || isWifiEnabled));
            }
        };
    }

    private void updateUserType() {
        UserManager userManager = getSystemService(UserManager.class);
        if (userManager == null) return;
        mIsAdmin = userManager.isAdminUser();
        mIsGuest = userManager.isGuestUser();
    }

    @Override
    protected String getLogTag() {
        return TAG;
    }

    @Override
    public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent,
            Bundle savedInstanceState) {
        if (mIsInSetupWizard) {
            GlifPreferenceLayout layout = (GlifPreferenceLayout) parent;
            return layout.onCreateRecyclerView(inflater, parent, savedInstanceState);
        } else {
            return super.onCreateRecyclerView(inflater, parent, savedInstanceState);
        }
    }

    @Override
    protected int getPreferenceScreenResId() {
        return R.xml.network_provider_settings;
    }

    private void addPreferences() {
        mAirplaneModeMsgPreference = findPreference(PREF_KEY_AIRPLANE_MODE_MSG);
        updateAirplaneModeMsgPreference(mAirplaneModeEnabler.isAirplaneModeOn() /* visible */);
        mConnectedWifiEntryPreferenceCategory = findPreference(PREF_KEY_CONNECTED_ACCESS_POINTS);
        mFirstWifiEntryPreferenceCategory = findPreference(PREF_KEY_FIRST_ACCESS_POINTS);
        mWifiEntryPreferenceCategory = findPreference(PREF_KEY_ACCESS_POINTS);
        mConfigureWifiSettingsPreference = findPreference(PREF_KEY_CONFIGURE_NETWORK_SETTINGS);
        mSavedNetworksPreference = findPreference(PREF_KEY_SAVED_NETWORKS);
        mAddWifiNetworkPreference = findPreference(PREF_KEY_ADD_WIFI_NETWORK);
        // Hide mAddWifiNetworkPreference by default. updateWifiEntryPreferences() will add it back
        // later when appropriate.
        mWifiEntryPreferenceCategory.removePreference(mAddWifiNetworkPreference);
        mDataUsagePreference = findPreference(PREF_KEY_DATA_USAGE);
        mDataUsagePreference.setVisible(DataUsageUtils.hasWifiRadio(getContext()));
        mDataUsagePreference.setTemplate(new NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI)
                        .build(), SubscriptionManager.INVALID_SUBSCRIPTION_ID);
        mResetInternetPreference = findPreference(PREF_KEY_RESET_INTERNET);
        if (mResetInternetPreference != null) {
            mResetInternetPreference.setVisible(false);
        }
        addMobileDataPreferenceController();
        addNetworkMobileProviderController();
        addConnectedEthernetNetworkController();
        addWifiSwitchPreferenceController();
        mWifiStatusMessagePreference = findPreference(PREF_KEY_WIFI_STATUS_MESSAGE);

        checkConnectivityRecovering();
    }

    private void updateAirplaneModeMsgPreference(boolean visible) {
        if (mAirplaneModeMsgPreference != null) {
            mAirplaneModeMsgPreference.setVisible(visible);
        }
    }

    /**
     * Whether to show any UI which is SIM related.
     */
    @VisibleForTesting
    boolean showAnySubscriptionInfo(Context context) {
        return (context != null) && SubscriptionUtil.isSimHardwareVisible(context);
    }

    private void addMobileDataPreferenceController() {
        if (!showAnySubscriptionInfo(getContext())) {
            return;
        }
        if (mMobileDataPreferenceController == null) {
            mMobileDataPreferenceController = new MobileDataPreferenceController(
                    getContext(), PREF_KEY_MOBILE_DATA_TOGGLE, getSettingsLifecycle(),
                    mSubId, mIsInSetupWizard);
        }
        mMobileDataPreferenceController.displayPreference(getPreferenceScreen());
    }

    private void addNetworkMobileProviderController() {
        if (!showAnySubscriptionInfo(getContext())) {
            return;
        }
        if (mNetworkMobileProviderController == null) {
            mNetworkMobileProviderController = new NetworkMobileProviderController(
                    getContext(), PREF_KEY_PROVIDER_MOBILE_NETWORK);
        }
        mNetworkMobileProviderController.init(getSettingsLifecycle());
        mNetworkMobileProviderController.displayPreference(getPreferenceScreen());
    }

    private void addConnectedEthernetNetworkController() {
        if (mConnectedEthernetNetworkController == null) {
            mConnectedEthernetNetworkController =
                    new ConnectedEthernetNetworkController(getContext(), getSettingsLifecycle());
        }
        mConnectedEthernetNetworkController.displayPreference(getPreferenceScreen());
    }

    private void addWifiSwitchPreferenceController() {
        if (isCatalystEnabled()) {
            Log.i(TAG, "WifiSwitchPreferenceController bypassed since Catalyst is enabled!");
            return;
        }
        if (!hasWifiManager()) return;
        if (mWifiSwitchPreferenceController == null) {
            mWifiSwitchPreferenceController =
                    new WifiSwitchPreferenceController(getContext(), getSettingsLifecycle());
        }
        mWifiSwitchPreferenceController.displayPreference(getPreferenceScreen());
    }

    private void checkConnectivityRecovering() {
        mInternetResetHelper = new InternetResetHelper(getContext(), getLifecycle(),
                mNetworkMobileProviderController,
                findPreference(WifiSwitchPreferenceController.KEY),
                mConnectedWifiEntryPreferenceCategory,
                mFirstWifiEntryPreferenceCategory,
                mWifiEntryPreferenceCategory,
                mResetInternetPreference);
        mInternetResetHelper.checkRecovering();
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        if (hasWifiManager()) {
            mWifiPickerTrackerHelper =
                    new WifiPickerTrackerHelper(getSettingsLifecycle(), getContext(), this);
            mWifiPickerTracker = mWifiPickerTrackerHelper.getWifiPickerTracker();
        }
        mInternetUpdater = new InternetUpdater(getContext(), getSettingsLifecycle(), this);

        mSaveListener = new WifiManager.ActionListener() {
            @Override
            public void onSuccess() {
            }

            @Override
            public void onFailure(int reason) {
                Activity activity = getActivity();
                if (activity != null) {
                    Toast.makeText(activity,
                            R.string.wifi_failed_save_message,
                            Toast.LENGTH_SHORT).show();
                }
            }
        };

        if (mIsInSetupWizard) {
            mConfigureWifiSettingsPreference.setVisible(false);
            mDataUsagePreference.setVisible(false);
        }

        if (savedInstanceState != null) {
            mDialogMode = savedInstanceState.getInt(SAVE_DIALOG_MODE);
            mDialogWifiEntryKey = savedInstanceState.getString(SAVE_DIALOG_WIFIENTRY_KEY);
        }

        // If we're supposed to enable/disable the Next button based on our current connection
        // state, start it off in the right state.
        final Intent intent = getActivity().getIntent();
        mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false);

        if (intent.hasExtra(EXTRA_START_CONNECT_SSID)) {
            mOpenSsid = intent.getStringExtra(EXTRA_START_CONNECT_SSID);
        }

        requireActivity().addMenuProvider(mMenuProvider);
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
    }

    @Override
    public void onStart() {
        super.onStart();
        if (mIsViewLoading) {
            final long delayMillis = (hasWifiManager() && mWifiManager.isWifiEnabled())
                    ? 1000 : 100;
            getView().postDelayed(mRemoveLoadingRunnable, delayMillis);
        }
        if (mIsRestricted) {
            restrictUi();
            return;
        }
        mAirplaneModeEnabler.start();
        mDataStateListener.start(mSubId);
    }

    private void restrictUi() {
        if (!isUiRestrictedByOnlyAdmin()) {
            getEmptyTextView().setText(R.string.wifi_empty_list_user_restricted);
        }
        getPreferenceScreen().removeAll();
    }

    @Override
    public void onResume() {
        super.onResume();

        // Disable the animation of the preference list
        final RecyclerView prefListView = getListView();
        if (prefListView != null) {
            prefListView.setItemAnimator(null);
        }

        // Because RestrictedSettingsFragment's onResume potentially requests authorization,
        // which changes the restriction state, recalculate it.
        final boolean alreadyImmutablyRestricted = mIsRestricted;
        mIsRestricted = isUiRestricted();
        if (!alreadyImmutablyRestricted && mIsRestricted) {
            restrictUi();
        }

        changeNextButtonState(mWifiPickerTracker != null
                && mWifiPickerTracker.getConnectedWifiEntry() != null
                || getDataEnabled());
    }

    @Override
    public void onStop() {
        getView().removeCallbacks(mRemoveLoadingRunnable);
        getView().removeCallbacks(mUpdateWifiEntryPreferencesRunnable);
        getView().removeCallbacks(mHideProgressBarRunnable);
        mAirplaneModeEnabler.stop();
        mDataStateListener.stop();
        super.onStop();
    }

    @Override
    public void onDestroy() {
        if (mAirplaneModeEnabler != null) {
            mAirplaneModeEnabler.close();
        }
        super.onDestroy();
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (!hasWifiManager()) {
            // Do nothing
        } else if (requestCode == ADD_NETWORK_REQUEST) {
            handleAddNetworkRequest(resultCode, data);
            return;
        } else if (requestCode == REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER) {
            if (resultCode == Activity.RESULT_OK) {
                if (mDialog != null) {
                    mDialog.dismiss();
                }
            }
            return;
        } else if (requestCode == CONFIG_NETWORK_REQUEST) {
            if (resultCode == Activity.RESULT_OK) {
                final WifiConfiguration wifiConfiguration = data.getParcelableExtra(
                        ConfigureWifiEntryFragment.NETWORK_CONFIG_KEY);
                if (wifiConfiguration != null) {
                    mWifiManager.connect(wifiConfiguration,
                            new WifiConnectActionListener());
                }
            }
            return;
        } else if (requestCode == MANAGE_SUBSCRIPTION) {
            //Do nothing
            return;
        }

        final boolean formerlyRestricted = mIsRestricted;
        mIsRestricted = isUiRestricted();
        if (formerlyRestricted && !mIsRestricted
                && getPreferenceScreen().getPreferenceCount() == 0) {
            // De-restrict the ui
            addPreferences();
        }
    }

    @Override
    protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
        final RecyclerView.Adapter adapter = super.onCreateAdapter(preferenceScreen);
        adapter.setHasStableIds(true);
        return adapter;
    }

    @Override
    public int getMetricsCategory() {
        return SettingsEnums.WIFI;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        // If dialog has been shown, save its state.
        if (mDialog != null) {
            outState.putInt(SAVE_DIALOG_MODE, mDialogMode);
            outState.putString(SAVE_DIALOG_WIFIENTRY_KEY, mDialogWifiEntryKey);
        }
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
        Preference preference = (Preference) view.getTag();
        if (!(preference instanceof LongPressWifiEntryPreference)) {
            // Do nothing.
            return;
        }

        // Cache the WifiEntry for onContextItemSelected. Don't use it in other methods.
        mSelectedWifiEntry = ((LongPressWifiEntryPreference) preference).getWifiEntry();

        menu.setHeaderTitle(mSelectedWifiEntry.getTitle());
        if (mSelectedWifiEntry.canConnect()) {
            menu.add(Menu.NONE, MENU_ID_CONNECT, 0 /* order */, R.string.wifi_connect);
        }

        if (mSelectedWifiEntry.canDisconnect()) {
            if (mSelectedWifiEntry.canShare()) {
                addShareMenuIfSuitable(menu);
            }
            menu.add(Menu.NONE, MENU_ID_DISCONNECT, 1 /* order */,
                    R.string.wifi_disconnect_button_text);
        }

        // "forget" for normal saved network. And "disconnect" for ephemeral network because it
        // could only be disconnected and be put in blocklists so it won't be used again.
        if (canForgetNetwork()) {
            addForgetMenuIfSuitable(menu);
        }

        WifiConfiguration config = mSelectedWifiEntry.getWifiConfiguration();
        // Some configs are ineditable
        if (WifiUtils.isNetworkLockedDown(getActivity(), config)) {
            return;
        }

        addModifyMenuIfSuitable(menu, mSelectedWifiEntry);
    }

    @VisibleForTesting
    void addShareMenuIfSuitable(ContextMenu menu) {
        if (mIsAdmin) {
            menu.add(Menu.NONE, MENU_ID_SHARE, 0 /* order */, R.string.share);
            return;
        }
        Log.w(TAG, "Don't add the Wi-Fi share menu because the user is not an admin.");
        EventLog.writeEvent(0x534e4554, "206986392", -1 /* UID */, "User is not an admin");
    }

    @VisibleForTesting
    void addForgetMenuIfSuitable(ContextMenu menu) {
        if (mIsAdmin) {
            menu.add(Menu.NONE, MENU_ID_FORGET, 0 /* order */, R.string.forget);
        }
    }

    @VisibleForTesting
    void addModifyMenuIfSuitable(ContextMenu menu, WifiEntry wifiEntry) {
        if (mIsAdmin && wifiEntry.isSaved()
                && wifiEntry.getConnectedState() != CONNECTED_STATE_CONNECTED) {
            menu.add(Menu.NONE, MENU_ID_MODIFY, 0 /* order */, R.string.wifi_modify);
        }
    }

    private boolean canForgetNetwork() {
        return mSelectedWifiEntry.canForget() && !WifiUtils.isNetworkLockedDown(getActivity(),
                mSelectedWifiEntry.getWifiConfiguration());
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case MENU_ID_CONNECT:
                connect(mSelectedWifiEntry, true /* editIfNoConfig */, false /* fullScreenEdit */);
                return true;
            case MENU_ID_DISCONNECT:
                mSelectedWifiEntry.disconnect(null /* callback */);
                return true;
            case MENU_ID_FORGET:
                forget(mSelectedWifiEntry);
                return true;
            case MENU_ID_SHARE:
                WifiDppUtils.showLockScreen(getContext(),
                        () -> launchWifiDppConfiguratorActivity(mSelectedWifiEntry));
                return true;
            case MENU_ID_MODIFY:
                if (!mIsAdmin) {
                    Log.e(TAG, "Can't modify Wi-Fi because the user isn't admin.");
                    EventLog.writeEvent(0x534e4554, "237672190", UserHandle.myUserId(),
                            "User isn't admin");
                    return true;
                }
                showDialog(mSelectedWifiEntry, WifiConfigUiBase2.MODE_MODIFY);
                return true;
            default:
                return super.onContextItemSelected(item);
        }
    }

    @Override
    public boolean onPreferenceTreeClick(Preference preference) {
        // If the preference has a fragment set, open that
        if (preference.getFragment() != null) {
            preference.setOnPreferenceClickListener(null);
            return super.onPreferenceTreeClick(preference);
        }

        if (preference instanceof LongPressWifiEntryPreference) {
            onSelectedWifiPreferenceClick((LongPressWifiEntryPreference) preference);
        } else if (preference == mAddWifiNetworkPreference) {
            onAddNetworkPressed();
        } else {
            return super.onPreferenceTreeClick(preference);
        }
        return true;
    }

    @VisibleForTesting
    void onSelectedWifiPreferenceClick(LongPressWifiEntryPreference preference) {
        final WifiEntry selectedEntry = preference.getWifiEntry();

        if (selectedEntry.shouldEditBeforeConnect()) {
            launchConfigNewNetworkFragment(selectedEntry);
            return;
        }

        if (selectedEntry.canConnect()) {
            connect(selectedEntry, true /* editIfNoConfig */, true /* fullScreenEdit */);
            return;
        }

        if (selectedEntry.isSaved()) {
            launchNetworkDetailsFragment(preference);
        }
    }

    private void launchWifiDppConfiguratorActivity(WifiEntry wifiEntry) {
        final Intent intent = WifiDppUtils.getConfiguratorQrCodeGeneratorIntentOrNull(getContext(),
                mWifiManager, wifiEntry);

        if (intent == null) {
            Log.e(TAG, "Launch Wi-Fi DPP QR code generator with a wrong Wi-Fi network!");
        } else {
            mMetricsFeatureProvider.action(SettingsEnums.PAGE_UNKNOWN,
                    SettingsEnums.ACTION_SETTINGS_SHARE_WIFI_QR_CODE,
                    SettingsEnums.SETTINGS_WIFI_DPP_CONFIGURATOR,
                    /* key */ null,
                    /* value */ Integer.MIN_VALUE);

            startActivity(intent);
        }
    }

    private void showDialog(WifiEntry wifiEntry, int dialogMode) {
        if (WifiUtils.isNetworkLockedDown(getActivity(), wifiEntry.getWifiConfiguration())
                && wifiEntry.getConnectedState() == CONNECTED_STATE_CONNECTED) {
            RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(),
                    RestrictedLockUtilsInternal.getDeviceOwner(getActivity()));
            return;
        }

        if (mDialog != null) {
            removeDialog(WIFI_DIALOG_ID);
            mDialog = null;
        }

        // Save the access point and edit mode
        mDialogWifiEntry = wifiEntry;
        mDialogWifiEntryKey = wifiEntry.getKey();
        mDialogMode = dialogMode;

        showDialog(WIFI_DIALOG_ID);
    }

    @Override
    public Dialog onCreateDialog(int dialogId) {
        if (dialogId == WIFI_DIALOG_ID) {  // modify network
            mDialog = new WifiDialog2(requireContext(), this, mDialogWifiEntry, mDialogMode);
            return mDialog;
        }
        return super.onCreateDialog(dialogId);
    }

    @Override
    public void onDialogShowing() {
        super.onDialogShowing();
        setOnDismissListener(this);
    }

    @Override
    public void onDismiss(DialogInterface dialog) {
        // We don't keep any dialog object when dialog was dismissed.
        mDialog = null;
        mDialogWifiEntry = null;
        mDialogWifiEntryKey = null;
    }

    @Override
    public int getDialogMetricsCategory(int dialogId) {
        switch (dialogId) {
            case WIFI_DIALOG_ID:
                return SettingsEnums.DIALOG_WIFI_AP_EDIT;
            default:
                return 0;
        }
    }

    @Override
    public void onInternetTypeChanged(@InternetUpdater.InternetType int internetType) {
        ThreadUtils.postOnMainThread(() -> {
            onWifiStateChanged();
        });
    }

    /** Called when the state of Wifi has changed. */
    @Override
    public void onWifiStateChanged() {
        if (mIsRestricted || !hasWifiManager()) {
            return;
        }
        final int wifiState = mWifiPickerTracker.getWifiState();

        if (mWifiPickerTracker.isVerboseLoggingEnabled()) {
            Log.i(TAG, "onWifiStateChanged called with wifi state: " + wifiState);
        }

        if (isFinishingOrDestroyed()) {
            Log.w(TAG, "onWifiStateChanged shouldn't run when fragment is finishing or destroyed");
            return;
        }

        if (isAdded()) {
            // update the menu item
            requireActivity().invalidateMenu();
        }

        switch (wifiState) {
            case WifiManager.WIFI_STATE_ENABLED:
                setWifiScanMessage(/* isWifiEnabled */ true);
                updateWifiEntryPreferences();
                break;

            case WifiManager.WIFI_STATE_ENABLING:
                removeConnectedWifiEntryPreference();
                removeWifiEntryPreference();
                setProgressBarVisible(true);
                break;

            case WifiManager.WIFI_STATE_DISABLING:
                removeConnectedWifiEntryPreference();
                removeWifiEntryPreference();
                break;

            case WifiManager.WIFI_STATE_DISABLED:
                setWifiScanMessage(/* isWifiEnabled */ false);
                removeConnectedWifiEntryPreference();
                removeWifiEntryPreference();
                setAdditionalSettingsSummaries();
                setProgressBarVisible(false);
                mClickedConnect = false;
                break;
        }
    }

    @Override
    public void onScanRequested() {
        setProgressBarVisible(true);
    }

    @VisibleForTesting
    void setWifiScanMessage(boolean isWifiEnabled) {
        final Context context = getContext();
        if (context == null) {
            return;
        }

        final LocationManager locationManager = context.getSystemService(LocationManager.class);
        if (!hasWifiManager() || isWifiEnabled || !locationManager.isLocationEnabled()
                || !mWifiManager.isScanAlwaysAvailable()) {
            mWifiStatusMessagePreference.setVisible(false);
            return;
        }
        if (TextUtils.isEmpty(mWifiStatusMessagePreference.getTitle())) {
            mWifiStatusMessagePreference.setTitle(R.string.wifi_scan_notify_message);
            mWifiStatusMessagePreference.setLearnMoreText(
                    context.getString(R.string.wifi_scan_change));
            mWifiStatusMessagePreference.setLearnMoreAction(v -> launchWifiScanningFragment());
        }
        mWifiStatusMessagePreference.setVisible(true);
    }

    private void launchWifiScanningFragment() {
        new SubSettingLauncher(getContext())
            .setDestination(WifiScanningFragment.class.getName())
            .setSourceMetricsCategory(SettingsEnums.SETTINGS_NETWORK_CATEGORY)
            .launch();
    }

    @Override
    public void onWifiEntriesChanged(@WifiPickerTracker.WifiEntriesChangedReason int reason) {
        updateWifiEntryPreferences();
        if (reason == WifiPickerTracker.WIFI_ENTRIES_CHANGED_REASON_SCAN_RESULTS) {
            setProgressBarVisible(false);
        }
        changeNextButtonState(mWifiPickerTracker != null
                && mWifiPickerTracker.getConnectedWifiEntry() != null
                || getDataEnabled());

        // Edit the Wi-Fi network of specified SSID.
        if (mOpenSsid != null && mWifiPickerTracker != null) {
            Optional<WifiEntry> matchedWifiEntry = mWifiPickerTracker.getWifiEntries().stream()
                    .filter(wifiEntry -> TextUtils.equals(mOpenSsid, wifiEntry.getSsid()))
                    .filter(wifiEntry -> wifiEntry.getSecurity() != WifiEntry.SECURITY_NONE
                            && wifiEntry.getSecurity() != WifiEntry.SECURITY_OWE)
                    .filter(wifiEntry -> !wifiEntry.isSaved()
                            || isDisabledByWrongPassword(wifiEntry))
                    .findFirst();
            if (matchedWifiEntry.isPresent()) {
                mOpenSsid = null;
                launchConfigNewNetworkFragment(matchedWifiEntry.get());
            }
        }
    }

    @Override
    public void onNumSavedNetworksChanged() {
        if (isFinishingOrDestroyed()) {
            return;
        }
        setAdditionalSettingsSummaries();
    }

    @Override
    public void onNumSavedSubscriptionsChanged() {
        if (isFinishingOrDestroyed()) {
            return;
        }
        setAdditionalSettingsSummaries();
    }

    protected void updateWifiEntryPreferences() {
        // bypass the update if the activity and the view are not ready, or it's restricted UI.
        if (getActivity() == null || getView() == null || mIsRestricted) {
            return;
        }
        // in case state has changed
        if (mWifiPickerTracker == null
                || mWifiPickerTracker.getWifiState() != WifiManager.WIFI_STATE_ENABLED) {
            return;
        }

        boolean hasAvailableWifiEntries = false;
        mWifiEntryPreferenceCategory.setVisible(true);

        final WifiEntry connectedEntry = mWifiPickerTracker.getConnectedWifiEntry();
        PreferenceCategory connectedWifiPreferenceCategory = getConnectedWifiPreferenceCategory();
        connectedWifiPreferenceCategory.setVisible(connectedEntry != null);
        if (connectedEntry != null) {
            final LongPressWifiEntryPreference connectedPref =
                    connectedWifiPreferenceCategory.findPreference(connectedEntry.getKey());
            if (connectedPref == null || connectedPref.getWifiEntry() != connectedEntry) {
                connectedWifiPreferenceCategory.removeAll();
                final ConnectedWifiEntryPreference pref =
                        createConnectedWifiEntryPreference(connectedEntry);
                pref.setKey(connectedEntry.getKey());
                pref.refresh();
                connectedWifiPreferenceCategory.addPreference(pref);
                pref.setOnPreferenceClickListener(preference -> {
                    if (connectedEntry.canSignIn()) {
                        connectedEntry.signIn(null /* callback */);
                    } else {
                        launchNetworkDetailsFragment(pref);
                    }
                    return true;
                });
                pref.setOnGearClickListener(preference -> {
                    launchNetworkDetailsFragment(pref);
                });

                if (mClickedConnect) {
                    mClickedConnect = false;
                    if (!mIsInSetupWizard) {
                        scrollToPreference(connectedWifiPreferenceCategory);
                    }
                }
            }
        } else {
            connectedWifiPreferenceCategory.removeAll();
        }

        int index = 0;
        cacheRemoveAllPrefs(mWifiEntryPreferenceCategory);
        List<WifiEntry> wifiEntries = mWifiPickerTracker.getWifiEntries();
        for (WifiEntry wifiEntry : wifiEntries) {
            hasAvailableWifiEntries = true;

            String key = wifiEntry.getKey();
            LongPressWifiEntryPreference pref =
                    (LongPressWifiEntryPreference) getCachedPreference(key);
            if (pref != null) {
                if (pref.getWifiEntry() == wifiEntry) {
                    pref.setOrder(index++);
                    continue;
                } else {
                    // Create a new preference if the underlying WifiEntry object has changed
                    removePreference(key);
                }
            }

            pref = createLongPressWifiEntryPreference(wifiEntry);
            pref.setKey(wifiEntry.getKey());
            pref.setOrder(index++);
            pref.refresh();

            if (wifiEntry.getHelpUriString() != null) {
                pref.setOnButtonClickListener(preference -> {
                    openSubscriptionHelpPage(wifiEntry);
                });
            }
            mWifiEntryPreferenceCategory.addPreference(pref);
        }
        removeCachedPrefs(mWifiEntryPreferenceCategory);

        if (!hasAvailableWifiEntries) {
            Preference pref = new Preference(getPrefContext());
            pref.setSelectable(false);
            pref.setSummary(R.string.wifi_empty_list_wifi_on);
            pref.setOrder(index++);
            pref.setKey(PREF_KEY_EMPTY_WIFI_LIST);
            mWifiEntryPreferenceCategory.addPreference(pref);
        }

        mAddWifiNetworkPreference.setOrder(index++);
        mWifiEntryPreferenceCategory.addPreference(mAddWifiNetworkPreference);
        setAdditionalSettingsSummaries();
    }

    @VisibleForTesting
    PreferenceCategory getConnectedWifiPreferenceCategory() {
        if (mInternetUpdater.getInternetType() == InternetUpdater.INTERNET_WIFI) {
            mFirstWifiEntryPreferenceCategory.setVisible(false);
            mFirstWifiEntryPreferenceCategory.removeAll();
            return mConnectedWifiEntryPreferenceCategory;
        }

        mConnectedWifiEntryPreferenceCategory.setVisible(false);
        mConnectedWifiEntryPreferenceCategory.removeAll();
        return mFirstWifiEntryPreferenceCategory;
    }

    @VisibleForTesting
    ConnectedWifiEntryPreference createConnectedWifiEntryPreference(WifiEntry wifiEntry) {
        if (mInternetUpdater.getInternetType() == InternetUpdater.INTERNET_WIFI) {
            return new ConnectedWifiEntryPreference(getPrefContext(), wifiEntry, this);
        }
        return new FirstWifiEntryPreference(getPrefContext(), wifiEntry, this);
    }

    @VisibleForTesting
    void launchNetworkDetailsFragment(LongPressWifiEntryPreference pref) {
        final WifiEntry wifiEntry = pref.getWifiEntry();
        if (!wifiEntry.isSaved() && wifiEntry.getConnectedState() != CONNECTED_STATE_CONNECTED) {
            Log.w(TAG, "Don't launch Wi-Fi details because WifiEntry is not saved or connected!");
            return;
        }
        final Context context = requireContext();

        final Bundle bundle = new Bundle();
        bundle.putString(WifiNetworkDetailsFragment.KEY_CHOSEN_WIFIENTRY_KEY, wifiEntry.getKey());

        new SubSettingLauncher(context)
                .setTitleText(context.getText(R.string.pref_title_network_details))
                .setDestination(WifiNetworkDetailsFragment.class.getName())
                .setArguments(bundle)
                .setSourceMetricsCategory(getMetricsCategory())
                .launch();
    }

    @VisibleForTesting
    LongPressWifiEntryPreference createLongPressWifiEntryPreference(WifiEntry wifiEntry) {
        return new LongPressWifiEntryPreference(getPrefContext(), wifiEntry, this);
    }

    private void launchAddNetworkFragment() {
        new SubSettingLauncher(getContext())
                .setTitleRes(R.string.wifi_add_network)
                .setDestination(AddNetworkFragment.class.getName())
                .setSourceMetricsCategory(getMetricsCategory())
                .setResultListener(this, ADD_NETWORK_REQUEST)
                .launch();
    }

    /** Removes all preferences and hide the {@link #mConnectedWifiEntryPreferenceCategory} and
     *  {@link #mFirstWifiEntryPreferenceCategory}. */
    private void removeConnectedWifiEntryPreference() {
        mConnectedWifiEntryPreferenceCategory.removeAll();
        mConnectedWifiEntryPreferenceCategory.setVisible(false);
        mFirstWifiEntryPreferenceCategory.setVisible(false);
        mFirstWifiEntryPreferenceCategory.removeAll();
    }

    private void removeWifiEntryPreference() {
        mWifiEntryPreferenceCategory.removeAll();
        mWifiEntryPreferenceCategory.setVisible(false);
    }

    @VisibleForTesting
    void setAdditionalSettingsSummaries() {
        if (!mIsInSetupWizard) {
            mConfigureWifiSettingsPreference.setSummary(getString(
                    isWifiWakeupEnabled()
                            ? R.string.wifi_configure_settings_preference_summary_wakeup_on
                            : R.string.wifi_configure_settings_preference_summary_wakeup_off));
        }

        final int numSavedNetworks = mWifiPickerTracker == null ? 0 :
                mWifiPickerTracker.getNumSavedNetworks();
        final int numSavedSubscriptions = mWifiPickerTracker == null ? 0 :
                mWifiPickerTracker.getNumSavedSubscriptions();
        if (numSavedNetworks + numSavedSubscriptions > 0) {
            mSavedNetworksPreference.setVisible(true);
            mSavedNetworksPreference.setSummary(
                    getSavedNetworkSettingsSummaryText(numSavedNetworks, numSavedSubscriptions));
        } else {
            mSavedNetworksPreference.setVisible(false);
        }
    }

    private String getSavedNetworkSettingsSummaryText(
            int numSavedNetworks, int numSavedSubscriptions) {
        if (getContext() == null) {
            Log.w(TAG, "getSavedNetworkSettingsSummaryText shouldn't run if resource is not ready");
            return null;
        }

        if (numSavedSubscriptions == 0) {
            return StringUtil.getIcuPluralsString(getContext(), numSavedNetworks,
                    R.string.wifi_saved_access_points_summary);
        } else if (numSavedNetworks == 0) {
            return StringUtil.getIcuPluralsString(getContext(), numSavedSubscriptions,
                    R.string.wifi_saved_passpoint_access_points_summary);
        } else {
            final int numTotalEntries = numSavedNetworks + numSavedSubscriptions;
            return StringUtil.getIcuPluralsString(getContext(), numTotalEntries,
                    R.string.wifi_saved_all_access_points_summary);
        }
    }

    private boolean isWifiWakeupEnabled() {
        final Context context = getContext();
        final PowerManager powerManager = context.getSystemService(PowerManager.class);
        final ContentResolver contentResolver = context.getContentResolver();
        return hasWifiManager()
                && mWifiManager.isAutoWakeupEnabled()
                && mWifiManager.isScanAlwaysAvailable()
                && Settings.Global.getInt(contentResolver,
                Settings.Global.AIRPLANE_MODE_ON, 0) == 0
                && !powerManager.isPowerSaveMode();
    }

    protected void setProgressBarVisible(boolean visible) {
        if (!mIsInSetupWizard) {
            showPinnedHeader(visible);
        }
    }

    @VisibleForTesting
    void handleAddNetworkRequest(int result, Intent data) {
        if (result == Activity.RESULT_OK) {
            handleAddNetworkSubmitEvent(data);
        }
    }

    private void handleAddNetworkSubmitEvent(Intent data) {
        final WifiConfiguration wifiConfiguration = data.getParcelableExtra(
                AddNetworkFragment.WIFI_CONFIG_KEY);
        if (wifiConfiguration != null && hasWifiManager()) {
            mWifiManager.save(wifiConfiguration, mSaveListener);
        }
    }

    /**
     * Called when "add network" button is pressed.
     */
    private void onAddNetworkPressed() {
        launchAddNetworkFragment();
    }

    @Override
    public int getHelpResource() {
        return R.string.help_url_wifi;
    }

    /**
     * Renames/replaces "Next" button when appropriate. "Next" button usually exists in
     * Wi-Fi setup screens, not in usual wifi settings screen.
     *
     * @param enabled true when the device is connected to a mobile or wifi network.
     */
    @VisibleForTesting
    void changeNextButtonState(boolean enabled) {
        if (mEnableNextOnConnection && hasNextButton()) {
            getNextButton().setEnabled(enabled);
        }
    }

    @Override
    public void onForget(WifiDialog2 dialog) {
        forget(dialog.getWifiEntry());
    }

    @Override
    public void onSubmit(WifiDialog2 dialog) {
        if (!hasWifiManager()) return;

        final int dialogMode = dialog.getMode();
        final WifiConfiguration config = dialog.getController().getConfig();
        final WifiEntry wifiEntry = dialog.getWifiEntry();

        if (dialogMode == WifiConfigUiBase2.MODE_MODIFY) {
            if (config == null) {
                Toast.makeText(getContext(), R.string.wifi_failed_save_message,
                        Toast.LENGTH_SHORT).show();
            } else {
                mWifiManager.save(config, mSaveListener);
            }
        } else if (dialogMode == WifiConfigUiBase2.MODE_CONNECT
                || (dialogMode == WifiConfigUiBase2.MODE_VIEW && wifiEntry.canConnect())) {
            if (config == null) {
                connect(wifiEntry, false /* editIfNoConfig */,
                        false /* fullScreenEdit*/);
            } else {
                mWifiManager.connect(config, new WifiConnectActionListener());
            }
        }
    }

    @Override
    public void onScan(WifiDialog2 dialog, String ssid) {
        // Launch QR code scanner to join a network.
        startActivityForResult(
                WifiDppUtils.getEnrolleeQrCodeScannerIntent(dialog.getContext(), ssid),
                REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER);
    }

    private void forget(WifiEntry wifiEntry) {
        mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_WIFI_FORGET);
        wifiEntry.forget(null /* callback */);
    }

    @VisibleForTesting
    void connect(WifiEntry wifiEntry, boolean editIfNoConfig, boolean fullScreenEdit) {
        mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_WIFI_CONNECT,
                wifiEntry.isSaved());

        // If it's an unsaved secure WifiEntry, it will callback
        // ConnectCallback#onConnectResult with ConnectCallback#CONNECT_STATUS_FAILURE_NO_CONFIG
        WifiEntryConnectCallback callback =
                new WifiEntryConnectCallback(wifiEntry, editIfNoConfig, fullScreenEdit);

        if (Flags.androidVWifiApi() && wifiEntry.getSecurityTypes()
                .contains(WifiEntry.SECURITY_WEP)) {
            WifiUtils.checkWepAllowed(
                    getContext(), getViewLifecycleOwner(), wifiEntry.getSsid(), () -> {
                        wifiEntry.connect(callback);
                        return null;
                    });
            return;
        }

        wifiEntry.connect(callback);
    }

    private class WifiConnectActionListener implements WifiManager.ActionListener {
        @Override
        public void onSuccess() {
            mClickedConnect = true;
        }

        @Override
        public void onFailure(int reason) {
            if (isFinishingOrDestroyed()) {
                return;
            }
            Toast.makeText(getContext(), R.string.wifi_failed_connect_message, Toast.LENGTH_SHORT)
                    .show();
        }
    };

    public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
            new SearchIndexProvider(R.xml.network_provider_settings);

    @VisibleForTesting
    static class SearchIndexProvider extends BaseSearchIndexProvider {

        private final WifiRestriction mWifiRestriction;

        SearchIndexProvider(int xmlRes) {
            super(xmlRes);
            mWifiRestriction = new WifiRestriction();
        }

        @VisibleForTesting
        SearchIndexProvider(int xmlRes, WifiRestriction wifiRestriction) {
            super(xmlRes);
            mWifiRestriction = wifiRestriction;
        }

        @Override
        public List<String> getNonIndexableKeys(Context context) {
            final List<String> keys = super.getNonIndexableKeys(context);

            if (!mWifiRestriction.isChangeWifiStateAllowed(context)) {
                keys.add(PREF_KEY_WIFI_TOGGLE);
            }

            final WifiManager wifiManager = context.getSystemService(WifiManager.class);
            if (wifiManager == null) return keys;

            if (WifiSavedConfigUtils.getAllConfigsCount(context, wifiManager) == 0) {
                keys.add(PREF_KEY_SAVED_NETWORKS);
            }
            if (wifiManager.getWifiState() != WifiManager.WIFI_STATE_ENABLED) {
                keys.add(PREF_KEY_ADD_WIFI_NETWORK);
            }

            if (!DataUsageUtils.hasWifiRadio(context)) {
                keys.add(PREF_KEY_DATA_USAGE);
            }
            return keys;
        }
    }

    @VisibleForTesting
    static class WifiRestriction {
        public boolean isChangeWifiStateAllowed(@Nullable Context context) {
            if (context == null) return true;
            return WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(context);
        }
    }

    private class WifiEntryConnectCallback implements ConnectCallback {
        final WifiEntry mConnectWifiEntry;
        final boolean mEditIfNoConfig;
        final boolean mFullScreenEdit;

        WifiEntryConnectCallback(WifiEntry connectWifiEntry, boolean editIfNoConfig,
                boolean fullScreenEdit) {
            mConnectWifiEntry = connectWifiEntry;
            mEditIfNoConfig = editIfNoConfig;
            mFullScreenEdit = fullScreenEdit;
        }

        @Override
        public void onConnectResult(@ConnectStatus int status) {
            if (isFinishingOrDestroyed()) {
                return;
            }

            if (status == ConnectCallback.CONNECT_STATUS_SUCCESS) {
                mClickedConnect = true;
            } else if (status == ConnectCallback.CONNECT_STATUS_FAILURE_NO_CONFIG) {
                if (mEditIfNoConfig) {
                    // Edit an unsaved secure Wi-Fi network.
                    if (mFullScreenEdit) {
                        launchConfigNewNetworkFragment(mConnectWifiEntry);
                    } else {
                        showDialog(mConnectWifiEntry, WifiConfigUiBase2.MODE_CONNECT);
                    }
                }
            } else if (status == CONNECT_STATUS_FAILURE_UNKNOWN) {
                Toast.makeText(getContext(), R.string.wifi_failed_connect_message,
                        Toast.LENGTH_SHORT).show();
            }
        }
    }

    @VisibleForTesting
    void launchConfigNewNetworkFragment(WifiEntry wifiEntry) {
        if (mIsRestricted) {
            Log.e(TAG, "Can't configure Wi-Fi because NetworkProviderSettings is restricted.");
            EventLog.writeEvent(0x534e4554, "246301667", -1 /* UID */, "Fragment is restricted.");
            return;
        }

        final Bundle bundle = new Bundle();
        bundle.putString(WifiNetworkDetailsFragment.KEY_CHOSEN_WIFIENTRY_KEY,
                wifiEntry.getKey());
        new SubSettingLauncher(getContext())
                .setTitleText(wifiEntry.getTitle())
                .setDestination(ConfigureWifiEntryFragment.class.getName())
                .setArguments(bundle)
                .setSourceMetricsCategory(getMetricsCategory())
                .setResultListener(NetworkProviderSettings.this, CONFIG_NETWORK_REQUEST)
                .launch();
    }

    /** Helper method to return whether a WifiEntry is disabled due to a wrong password */
    private static boolean isDisabledByWrongPassword(WifiEntry wifiEntry) {
        WifiConfiguration config = wifiEntry.getWifiConfiguration();
        if (config == null) {
            return false;
        }
        WifiConfiguration.NetworkSelectionStatus networkStatus =
                config.getNetworkSelectionStatus();
        if (networkStatus == null
                || networkStatus.getNetworkSelectionStatus() == NETWORK_SELECTION_ENABLED) {
            return false;
        }
        int reason = networkStatus.getNetworkSelectionDisableReason();
        return WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD == reason;
    }

    @VisibleForTesting
    void openSubscriptionHelpPage(WifiEntry wifiEntry) {
        final Intent intent = getHelpIntent(getContext(), wifiEntry.getHelpUriString());
        if (intent != null) {
            try {
                startActivityForResult(intent, MANAGE_SUBSCRIPTION);
            } catch (ActivityNotFoundException e) {
                Log.e(TAG, "Activity was not found for intent, " + intent.toString());
            }
        }
    }

    @VisibleForTesting
    Intent getHelpIntent(Context context, String helpUrlString) {
        return HelpUtils.getHelpIntent(context, helpUrlString, context.getClass().getName());
    }

    @VisibleForTesting
    void showResetInternetDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
        DialogInterface.OnClickListener resetInternetClickListener =
                new Dialog.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        fixConnectivity();
                    }
                };
        builder.setTitle(R.string.reset_your_internet_title)
                .setMessage(R.string.reset_internet_text)
                .setPositiveButton(R.string.tts_reset, resetInternetClickListener)
                .setNegativeButton(android.R.string.cancel, null)
                .create()
                .show();
    }

    @VisibleForTesting
    boolean isPhoneOnCall() {
        TelephonyManager mTelephonyManager = getActivity().getSystemService(TelephonyManager.class);
        int state = mTelephonyManager.getCallState();
        return state != TelephonyManager.CALL_STATE_IDLE;
    }

    private void fixConnectivity() {
        if (mIsGuest) {
            Log.e(TAG, "Can't reset network because the user is a guest.");
            EventLog.writeEvent(0x534e4554, "252995826", UserHandle.myUserId(), "User is a guest");
            return;
        }
        mInternetResetHelper.restart();
    }

    /**
     * Called when airplane mode status is changed.
     *
     * @param isAirplaneModeOn The airplane mode is on
     */
    @Override
    public void onAirplaneModeChanged(boolean isAirplaneModeOn) {
        updateAirplaneModeMsgPreference(isAirplaneModeOn /* visible */);
        if (isAdded()) {
            // update the menu item
            requireActivity().invalidateMenu();
        }
    }

    /**
     * Implementation of {@code MobileDataEnabledListener.Client}
     */
    public void onMobileDataEnabledChange() {
        changeNextButtonState(getDataEnabled());
    }

    boolean getDataEnabled() {
        return getContext().getSystemService(TelephonyManager.class).getDataEnabled(mSubId);
    }

    /**
     * A Wi-Fi preference for the connected Wi-Fi network without internet access.
     *
     * Override the icon color attribute by {@link ConnectedWifiEntryPreference#getIconColorAttr()}
     * and show the icon color to android.R.attr.colorControlNormal for the preference.
     */
    public class FirstWifiEntryPreference extends ConnectedWifiEntryPreference {
        public FirstWifiEntryPreference(Context context, WifiEntry wifiEntry,
                Fragment fragment) {
            super(context, wifiEntry, fragment);
        }

        @Override
        protected int getIconColorAttr() {
            return android.R.attr.colorControlNormal;
        }
    }

    @Override
    public @Nullable String getPreferenceScreenBindingKey(@NonNull Context context) {
        return NetworkProviderScreen.KEY;
    }
}
