/*
 * Copyright (C) 2016 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.tv.settings.connectivity;

import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_CLASSIC;
import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_TWO_PANEL;
import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_VENDOR;
import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_X;
import static com.android.tv.settings.util.InstrumentationUtils.logEntrySelected;
import static com.android.tv.settings.util.InstrumentationUtils.logToggleInteracted;

import android.app.Activity;
import android.app.tvsettings.TvSettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Icon;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserManager;
import android.provider.Settings;
import android.util.ArrayMap;
import android.view.View;

import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.Keep;
import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceManager;
import androidx.preference.SwitchPreference;
import androidx.preference.TwoStatePreference;

import com.android.settingslib.RestrictedPreference;
import com.android.tv.settings.FullScreenConfirmationActivity;
import com.android.tv.settings.MainFragment;
import com.android.tv.settings.R;
import com.android.tv.settings.RestrictedPreferenceAdapter;
import com.android.tv.settings.SettingsPreferenceFragment;
import com.android.tv.settings.basic.BasicModeFeatureProvider;
import com.android.tv.settings.connectivity.thread.ShareThreadNetworkActivity;
import com.android.tv.settings.connectivity.util.ThreadNetworkHelper;
import com.android.tv.settings.device.eco.EnergyModeConfirmationActivity;
import com.android.tv.settings.device.eco.EnergyModesHelper;
import com.android.tv.settings.device.eco.EnergyModesHelper.EnergyMode;
import com.android.tv.settings.library.network.AccessPoint;
import com.android.tv.settings.overlay.FlavorUtils;
import com.android.tv.settings.util.SliceUtils;
import com.android.tv.settings.widget.AccessPointPreference;
import com.android.tv.settings.widget.CustomContentDescriptionSwitchPreference;
import com.android.tv.settings.widget.TvAccessPointPreference;
import com.android.tv.twopanelsettings.FullScreenDialogFragment;
import com.android.tv.twopanelsettings.slices.SliceShard;
import com.android.tv.twopanelsettings.slices.compat.Slice;
import com.android.wifitrackerlib.WifiEntry;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
/**
 * Fragment for controlling network connectivity
 */
@Keep
public class NetworkFragment extends SettingsPreferenceFragment implements
        ConnectivityListener.Listener, ConnectivityListener.WifiNetworkListener,
        AccessPoint.AccessPointListener, SliceShard.Callbacks {

    private static final String TAG = "NetworkFragment";
    private static final String KEY_WIFI_ENABLE = "wifi_enable";
    private static final String KEY_WIFI_LIST = "wifi_list";
    private static final String KEY_WIFI_COLLAPSE = "wifi_collapse";
    private static final String KEY_WIFI_OTHER = "wifi_other";
    private static final String KEY_WIFI_ADD = "wifi_add";
    private static final String KEY_WIFI_ADD_EASYCONNECT = "wifi_add_easyconnect";
    private static final String KEY_WIFI_ALWAYS_SCAN = "wifi_always_scan";
    private static final String KEY_ETHERNET = "ethernet";
    private static final String KEY_WIFI_THREAD_NETWORK = "wifi_thread_network";
    private static final String KEY_SHARE_THREAD_NETWORK = "share_thread_network";
    private static final String KEY_ETHERNET_STATUS = "ethernet_status";
    private static final String KEY_ETHERNET_PROXY = "ethernet_proxy";
    private static final String KEY_ETHERNET_DHCP = "ethernet_dhcp";
    private static final String KEY_NETWORK_DIAGNOSTICS = "network_diagnostics";

    private static final int INITIAL_UPDATE_DELAY = 500;

    private static final String NETWORK_DIAGNOSTICS_ACTION =
            "com.android.tv.settings.network.NETWORK_DIAGNOSTICS";
    private static final String NETWORK_HOOK_RESTRICT_WIFI_ACTION =
            "com.android.tv.settings.HOOK_RESTRICT_WIFI_FOR_ERP";

    private final Preference.OnPreferenceChangeListener mThreadNetworkPreferenceChangeListener =
            new Preference.OnPreferenceChangeListener() {
                @Override
                public boolean onPreferenceChange(Preference preference, Object newValue) {
                    Boolean isChecked = (Boolean) newValue;
                    EnergyModesHelper energyModesHelper = new EnergyModesHelper(getContext());

                    if (isChecked) {
                        EnergyMode currentEnergyMode = energyModesHelper.updateEnergyMode();
                        if (currentEnergyMode == null) {
                            Optional.ofNullable(ThreadNetworkHelper.getInstance(getContext()))
                                    .ifPresent(helper -> helper.setEnabled(true));
                            logToggleInteracted(
                                    TvSettingsEnums.NETWORK_T_N, true);
                        } else {
                            if (energyModesHelper.getEnergyMode(currentEnergyMode.identifierRes)
                                    != EnergyModesHelper.MODE_HIGH_ENERGY) {
                                enableThreadNetworkIntentLauncher
                                        .launch(getEnableThreadNetworkConfirmationIntent());
                            } else {
                                Optional.ofNullable(ThreadNetworkHelper.getInstance(getContext()))
                                        .ifPresent(helper -> helper.setEnabled(true));
                            }
                        }
                    } else {
                        disableThreadNetworkIntentLauncher
                                .launch(getDisableThreadNetworkConfirmationIntent());
                    }
                    return false;
                }
            };
    private final ThreadNetworkHelper.OnStateChangeListener mOnThreadNetworkStateChange =
            new ThreadNetworkHelper.OnStateChangeListener() {

                @Override
                public void isEnabled(boolean enabled) {
                    mThreadNetworkPref.setChecked(enabled);
                    updateThreadShareVisibility(enabled);
                }
            };
    private final ActivityResultLauncher<Intent> enableThreadNetworkIntentLauncher =
            registerForActivityResult(
                    new ActivityResultContracts.StartActivityForResult(),
                    new ActivityResultCallback<ActivityResult>() {
                        @Override
                        public void onActivityResult(ActivityResult result) {
                            if (result.getResultCode() == Activity.RESULT_OK) {
                                Intent intent = new Intent(
                                        getContext(), EnergyModeConfirmationActivity.class
                                );
                                intent.putExtra(EnergyModeConfirmationActivity.EXTRA_ENERGY_MODE_ID,
                                        getContext().getString(
                                                R.string.energy_mode_high_identifier));
                                getContext().startActivity(intent);
                            }
                        }
                    });
    private final ActivityResultLauncher<Intent> disableThreadNetworkIntentLauncher =
            registerForActivityResult(
                    new ActivityResultContracts.StartActivityForResult(),
                    new ActivityResultCallback<ActivityResult>() {
                        @Override
                        public void onActivityResult(ActivityResult result) {
                            if (result.getResultCode() == Activity.RESULT_OK) {
                                Optional.ofNullable(ThreadNetworkHelper.getInstance(getContext()))
                                    .ifPresent(helper -> helper.setEnabled(false));
                                logToggleInteracted(
                                        TvSettingsEnums.NETWORK_T_N, false);
                            }
                        }
                    });

    private ConnectivityListener mConnectivityListener;
    private WifiManager mWifiManager;
    private ConnectivityManager mConnectivityManager;
    private Optional<ThreadNetworkHelper> mThreadNetworkHelperOptional;
    private TvAccessPointPreference.UserBadgeCache mUserBadgeCache;

    private TwoStatePreference mEnableWifiPref;
    private CollapsibleCategory mWifiNetworksCategory;
    private Preference mCollapsePref;
    private RestrictedPreference mAddPref;
    private RestrictedPreference mAddEasyConnectPref;
    private TwoStatePreference mAlwaysScan;
    private PreferenceCategory mEthernetCategory;
    private TwoStatePreference mThreadNetworkPref;
    private Preference mShareThreadNetworkPref;
    private Preference mEthernetStatusPref;
    private Preference mEthernetProxyPref;
    private Preference mEthernetDhcpPref;
    private Map<WifiEntry, RestrictedPreferenceAdapter<TvAccessPointPreference>> mPrefMap =
            Collections.emptyMap();

    private final Handler mHandler = new Handler();
    private long mNoWifiUpdateBeforeMillis;
    private Runnable mInitialUpdateWifiListRunnable = new Runnable() {
        @Override
        public void run() {
            mNoWifiUpdateBeforeMillis = 0;
            updateWifiList();
        }
    };
    private boolean mIsWifiHardwarePresent;
    private SliceShard mSliceShard;

    public static NetworkFragment newInstance() {
        return new NetworkFragment();
    }

    private final List<AccessPoint> mCurrentAccessPoints = new ArrayList<>();

    private ActivityResultLauncher<Intent> mRestrictWifiHookLauncher;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        mIsWifiHardwarePresent = getContext().getPackageManager()
                .hasSystemFeature(PackageManager.FEATURE_WIFI);
        mConnectivityListener = new ConnectivityListener(
                getContext(), this, getSettingsLifecycle());
        mWifiManager = getContext().getSystemService(WifiManager.class);
        mConnectivityManager = getContext().getSystemService(ConnectivityManager.class);
        mUserBadgeCache =
                new TvAccessPointPreference.UserBadgeCache(getContext().getPackageManager());
        mThreadNetworkHelperOptional = Optional.ofNullable(
                ThreadNetworkHelper.getInstance(getContext()));
        mRestrictWifiHookLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
            if (result.getResultCode() == Activity.RESULT_OK) {
                // OK to restrict Wifi
                if(mEnableWifiPref != null && mEnableWifiPref.isChecked()){
                    mEnableWifiPref.setChecked(false);
                }
            }else if(result.getResultCode() == Activity.RESULT_CANCELED){
                // we should let user connect Wifi in default/fallback case
                updateWifiEnable();
            }
        });
        super.onCreate(savedInstanceState);
    }

    @Override
    public void onStart() {
        super.onStart();
        mConnectivityListener.setWifiListener(this);
        mNoWifiUpdateBeforeMillis = SystemClock.elapsedRealtime() + INITIAL_UPDATE_DELAY;
        updateWifiList();
    }

    @Override
    public void onResume() {
        super.onResume();
        // There doesn't seem to be an API to listen to everything this could cover, so
        // tickle it here and hope for the best.
        updateConnectivity();
        mThreadNetworkHelperOptional.ifPresent(threadNetworkHelper ->  {
            threadNetworkHelper.registerStateCallback();
            if (mThreadNetworkPref != null) {
                threadNetworkHelper.setOnStateChangeListener(mOnThreadNetworkStateChange);
                mThreadNetworkPref
                        .setOnPreferenceChangeListener(mThreadNetworkPreferenceChangeListener);
            }
        });
    }

    @Override
    public void onPause() {
        super.onPause();
        mThreadNetworkHelperOptional.ifPresent(threadNetworkHelper ->  {
            if (mThreadNetworkPref != null) {
                mThreadNetworkPref
                        .setOnPreferenceChangeListener(null);
            }
            threadNetworkHelper.unregisterStateCallback();
            threadNetworkHelper.setOnStateChangeListener(null);
        });
    }


    @Override
    public void onStop() {
        mConnectivityListener.setListener(null);
        clearCurrentAccessPoints();
        super.onStop();
    }

    private int getPreferenceScreenResId() {
        switch (FlavorUtils.getFlavor(getContext())) {
            case FLAVOR_CLASSIC:
            case FLAVOR_TWO_PANEL:
                return R.xml.network;
            case FLAVOR_X:
            case FLAVOR_VENDOR:
                return R.xml.network_x;
            default:
                return R.xml.network;
        }
    }

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        getPreferenceManager().setPreferenceComparisonCallback(
                new PreferenceManager.SimplePreferenceComparisonCallback());
        var sliceUri = getString(R.string.network_fragment_slice_uri);
        if (!SliceUtils.isSliceProviderValid(requireContext(), sliceUri)) {
            setPreferencesFromResource(getPreferenceScreenResId(), null);
            maybeConfigurePreferences();
            return;
        }

        setPreferencesFromResource(R.xml.settings_loading, null);
        mSliceShard = new SliceShard(this, sliceUri, this,
                getString(R.string.connectivity_network_category_title),
                SliceShard.Companion.getPrefContext(requireContext()), true);
    }

    @Override
    public void onSlice(@Nullable Slice slice) {
        mSliceShard = null;
        if (slice == null) {
            setPreferencesFromResource(getPreferenceScreenResId(), null);
        }
        maybeConfigurePreferences();
    }

    private boolean isSliceLoading() {
        return mSliceShard != null;
    }

    private void maybeConfigurePreferences() {
        if (!isAdded() || isSliceLoading()) {
            return;
        }
        mEnableWifiPref = (TwoStatePreference) findPreference(KEY_WIFI_ENABLE);
        mWifiNetworksCategory = (CollapsibleCategory) findPreference(KEY_WIFI_LIST);
        mCollapsePref = findPreference(KEY_WIFI_COLLAPSE);
        mAddPref = (RestrictedPreference) findPreference(KEY_WIFI_ADD);
        mAddEasyConnectPref = (RestrictedPreference) findPreference(KEY_WIFI_ADD_EASYCONNECT);
        mAlwaysScan = (TwoStatePreference) findPreference(KEY_WIFI_ALWAYS_SCAN);
        mThreadNetworkPref = (SwitchPreference) findPreference(KEY_WIFI_THREAD_NETWORK);
        mShareThreadNetworkPref = findPreference(KEY_SHARE_THREAD_NETWORK);

        mEthernetCategory = (PreferenceCategory) findPreference(KEY_ETHERNET);
        mEthernetStatusPref = findPreference(KEY_ETHERNET_STATUS);
        mEthernetProxyPref = findPreference(KEY_ETHERNET_PROXY);
        mEthernetDhcpPref = findPreference(KEY_ETHERNET_DHCP);

        if (!mIsWifiHardwarePresent) {
            mEnableWifiPref.setVisible(false);
        }
        mThreadNetworkPref.setVisible(mThreadNetworkHelperOptional.isPresent());
        updateThreadShareVisibility(mThreadNetworkPref.isChecked());
        Preference networkDiagnosticsPref = findPreference(KEY_NETWORK_DIAGNOSTICS);
        Intent networkDiagnosticsIntent = makeNetworkDiagnosticsIntent();
        if (networkDiagnosticsIntent != null) {
            networkDiagnosticsPref.setVisible(true);
            networkDiagnosticsPref.setIntent(networkDiagnosticsIntent);
        } else {
            networkDiagnosticsPref.setVisible(false);
        }

        final UserManager userManager = UserManager.get(requireContext());

        mAddPref.checkRestrictionAndSetDisabled(UserManager.DISALLOW_CONFIG_WIFI);
        mAddEasyConnectPref.checkRestrictionAndSetDisabled(UserManager.DISALLOW_CONFIG_WIFI);

        if (!mAddPref.isDisabledByAdmin()) {
            mAddPref.checkRestrictionAndSetDisabled(UserManager.DISALLOW_ADD_WIFI_CONFIG);
            mAddEasyConnectPref.checkRestrictionAndSetDisabled(
                    UserManager.DISALLOW_ADD_WIFI_CONFIG);
        }

        if (userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI)
                || userManager.hasUserRestriction(UserManager.DISALLOW_ADD_WIFI_CONFIG)) {
            mAddPref.setFragment(null);
            mAddEasyConnectPref.setFragment(null);

            if (!mAddPref.isDisabledByAdmin()) {
                mAddPref.setEnabled(false);
            }
            if (!mAddEasyConnectPref.isDisabledByAdmin()) {
                mAddEasyConnectPref.setEnabled(false);
            }
        }

        if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED) &&
                mThreadNetworkHelperOptional.isPresent()) {
            mThreadNetworkHelperOptional.get()
                    .setOnStateChangeListener(mOnThreadNetworkStateChange);
            mThreadNetworkPref
                    .setOnPreferenceChangeListener(mThreadNetworkPreferenceChangeListener);
        }

        updateConnectivity();
    }

    private void updateThreadShareVisibility(boolean isThreadsEnabled) {
        mShareThreadNetworkPref.setVisible(
                getContext().getResources().getBoolean(
                        R.bool.config_share_thread_network_enabled) && isThreadsEnabled);
    }

    private Intent getRestrictWifiHookIntent(){
        Intent intent = new Intent();
        intent.setAction(NETWORK_HOOK_RESTRICT_WIFI_ACTION);

        ResolveInfo resolveInfo = MainFragment.systemIntentIsHandled(getContext(), intent);
        if (resolveInfo == null || resolveInfo.activityInfo == null) {
            return null;
        }

        intent.setPackage(resolveInfo.activityInfo.packageName);
        return intent;
    }

    private void updateWifiEnable(){
        BasicModeFeatureProvider provider = FlavorUtils.getFeatureFactory(
                getContext()).getBasicModeFeatureProvider();
        if (mEnableWifiPref.isChecked() &&
                Settings.Global.getInt(
                        getContext().getContentResolver(), Settings.Global.ADB_ENABLED, 0)
                        != 1 && SystemProperties.getInt(
                "ro.product.first_api_level", Build.VERSION.SDK_INT)
                >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && provider.isBasicMode(
                getContext())
                && !provider.isStoreDemoMode(getContext())) {
            // WiFi turned ON + not developer + launched on U+ + basic mode.
            // Prevent WiFi connection by launching dialog instead.
            provider.startBasicModeInternetBlock(getActivity());
        } else {
            mConnectivityListener.setWifiEnabled(mEnableWifiPref.isChecked());
        }
    }

    @Override
    public boolean onPreferenceTreeClick(Preference preference) {
        if (preference.getKey() == null) {
            return super.onPreferenceTreeClick(preference);
        }
        switch (preference.getKey()) {
            case KEY_WIFI_ENABLE:
                Intent intent = getRestrictWifiHookIntent();
                if (mEnableWifiPref.isChecked() && intent != null) {
                    mRestrictWifiHookLauncher.launch(intent);
                } else {
                    updateWifiEnable();
                }
                logToggleInteracted(
                        TvSettingsEnums.NETWORK_WIFI_ON_OFF, mEnableWifiPref.isChecked());
                return true;
            case KEY_WIFI_COLLAPSE:
                final boolean collapse = !mWifiNetworksCategory.isCollapsed();
                View collapsePrefView = getListView().getChildAt(mCollapsePref.getOrder());
                String wifiCollapseTitle = getContext().getString(collapse
                        ? R.string.wifi_setting_see_all : R.string.wifi_setting_see_fewer);
                mCollapsePref.setTitle(wifiCollapseTitle);
                if (collapsePrefView != null) {
                    collapsePrefView.setAccessibilityPaneTitle(wifiCollapseTitle);
                }
                mWifiNetworksCategory.setCollapsed(collapse);
                logEntrySelected(
                        collapse
                                ? TvSettingsEnums.NETWORK_SEE_FEWER
                                : TvSettingsEnums.NETWORK_SEE_ALL);
                return true;
            case KEY_WIFI_ALWAYS_SCAN:
                Settings.Global.putInt(getActivity().getContentResolver(),
                        Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE,
                        mAlwaysScan.isChecked() ? 1 : 0);
                logToggleInteracted(
                        TvSettingsEnums.NETWORK_ALWAYS_SCANNING_NETWORKS, mAlwaysScan.isChecked());
                return true;
            case KEY_WIFI_THREAD_NETWORK, KEY_ETHERNET_STATUS:
                return true;
            case KEY_WIFI_ADD:
                logEntrySelected(TvSettingsEnums.NETWORK_ADD_NEW_NETWORK);
                break;
            case KEY_WIFI_ADD_EASYCONNECT:
                startActivity(AddWifiNetworkActivity.createEasyConnectIntent(getContext()));
                break;
            case KEY_SHARE_THREAD_NETWORK:
                startActivity(new Intent(getContext(), ShareThreadNetworkActivity.class));
                break;
        }
        return super.onPreferenceTreeClick(preference);
    }

    private void updateConnectivity() {
        if (!isAdded() || isSliceLoading()) {
            return;
        }

        final boolean wifiEnabled = mIsWifiHardwarePresent
                && mConnectivityListener.isWifiEnabledOrEnabling();
        final boolean ethernetConnected = mConnectivityListener.isEthernetConnected();
        mEnableWifiPref.setChecked(wifiEnabled);

        mWifiNetworksCategory.setVisible(wifiEnabled && !ethernetConnected);
        mCollapsePref.setVisible(wifiEnabled && mWifiNetworksCategory.shouldShowCollapsePref());
        mAddPref.setVisible(wifiEnabled && !ethernetConnected);
        if (mAddEasyConnectPref != null) {
            mAddEasyConnectPref.setVisible(isEasyConnectEnabled() && !ethernetConnected);
        }

        if (!wifiEnabled) {
            updateWifiList();
        }

        int scanAlwaysAvailable = 0;
        try {
            scanAlwaysAvailable = Settings.Global.getInt(getContext().getContentResolver(),
                    Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE);
        } catch (Settings.SettingNotFoundException e) {
            // Ignore
        }
        mAlwaysScan.setChecked(scanAlwaysAvailable == 1);
        if (mAlwaysScan instanceof CustomContentDescriptionSwitchPreference) {
            ((CustomContentDescriptionSwitchPreference) mAlwaysScan).setContentDescription(
                    getResources()
                            .getString(
                                    R.string.wifi_setting_always_scan_content_description));
        }

        final boolean ethernetAvailable = mConnectivityListener.isEthernetAvailable();
        mEthernetCategory.setVisible(ethernetAvailable);
        mEthernetStatusPref.setVisible(ethernetAvailable);
        mEthernetProxyPref.setVisible(ethernetAvailable);
        if (ethernetAvailable) {
            mEthernetProxyPref.setIntent(EditProxySettingsActivity.createEthernetIntent(
                    getContext(),
                    mConnectivityListener.getEthernetInterfaceName(),
                    mConnectivityListener.getEthernetIpConfiguration()));
        }
        mEthernetProxyPref.setOnPreferenceClickListener(
                preference -> {
                    logEntrySelected(TvSettingsEnums.NETWORK_ETHERNET_PROXY_SETTINGS);
                    return false;
                });

        mEthernetDhcpPref.setVisible(ethernetAvailable);
        if (ethernetAvailable) {
            mEthernetDhcpPref.setIntent(EditIpSettingsActivity.createEthernetIntent(getContext(),
                    mConnectivityListener.getEthernetInterfaceName(),
                    mConnectivityListener.getEthernetIpConfiguration()));
        }
        mEthernetDhcpPref.setOnPreferenceClickListener(
                preference -> {
                    logEntrySelected(TvSettingsEnums.NETWORK_ETHERNET_IP_SETTINGS);
                    return false;
                });

        if (ethernetAvailable) {
            mEthernetStatusPref.setTitle(ethernetConnected
                    ? R.string.connected : R.string.not_connected);
            mEthernetStatusPref.setSummary(mConnectivityListener.getEthernetIpAddress());
        }

        mEnableWifiPref.setSummary(ethernetConnected ?
                getString(R.string.unplug_ethernet_to_use_wifi) : null);
    }

    private void updateWifiList() {
        if (!isAdded() || isSliceLoading()) {
            return;
        }

        if (!mIsWifiHardwarePresent || !mConnectivityListener.isWifiEnabledOrEnabling()
            || mConnectivityListener.isEthernetConnected()) {
            mWifiNetworksCategory.removeAll();
            mNoWifiUpdateBeforeMillis = 0;
            return;
        }

        final long now = SystemClock.elapsedRealtime();
        if (mNoWifiUpdateBeforeMillis > now) {
            mHandler.removeCallbacks(mInitialUpdateWifiListRunnable);
            mHandler.postDelayed(mInitialUpdateWifiListRunnable,
                    mNoWifiUpdateBeforeMillis - now);
            return;
        }

        final int existingCount = mWifiNetworksCategory.getRealPreferenceCount();
        final Set<Preference> toRemove = new HashSet<>(existingCount);
        for (int i = 0; i < existingCount; i++) {
            toRemove.add(mWifiNetworksCategory.getPreference(i));
        }

        final Context themedContext = getPreferenceManager().getContext();
        final Collection<AccessPoint> newAccessPoints =
                mConnectivityListener.getAvailableNetworks();
        int index = 0;

        final Map<WifiEntry, RestrictedPreferenceAdapter<TvAccessPointPreference>> newPrefMap =
                new ArrayMap<>();
        for (final AccessPoint accessPoint : newAccessPoints) {
            accessPoint.setListener(this);
            RestrictedPreferenceAdapter<TvAccessPointPreference> restrictedPref =
                    mPrefMap.get(accessPoint.getWifiEntry());
            Preference pref;
            if (restrictedPref == null) {
                pref = new TvAccessPointPreference(accessPoint, themedContext, mUserBadgeCache,
                        false);
                restrictedPref = new RestrictedPreferenceAdapter(themedContext, pref,
                        UserManager.DISALLOW_CONFIG_WIFI);
            } else {
                toRemove.remove(restrictedPref.getPreference());
                pref = restrictedPref.getOriginalPreference();
            }
            newPrefMap.put(accessPoint.getWifiEntry(), restrictedPref);

            if (isCaptivePortal(accessPoint)) {
                pref.setFragment(null);
                pref.setIntent(null);
                pref.setOnPreferenceClickListener(preference -> {
                    accessPoint.getWifiEntry().signIn(null);
                    return true;
                });
            } else if (accessPoint.isActive()) {
                pref.setFragment(WifiDetailsFragment.class.getName());
                // No need to track entry selection as new page will be focused
                pref.setOnPreferenceClickListener(preference -> false);
                WifiDetailsFragment.prepareArgs(pref.getExtras(), accessPoint);
                pref.setIntent(null);
            } else {
                pref.setFragment(null);
                pref.setIntent(WifiConnectionActivity.createIntent(getContext(), accessPoint));
                pref.setOnPreferenceClickListener(
                        preference -> {
                            logEntrySelected(TvSettingsEnums.NETWORK_NOT_CONNECTED_AP);
                            return false;
                        });
            }
            pref.setVisible(!restrictedPref.isRestricted() || accessPoint.isSaved());
            pref.setOrder(index++);
            pref.setSummary(WifiUtils.getConnectionStatus(accessPoint.getWifiEntry()));
            restrictedPref.updatePreference();

            Preference restrictedChild = restrictedPref.getPreference();
            if (restrictedChild.getParent() != null &&
                    restrictedChild.getParent() != mWifiNetworksCategory) {
                // Remove first if added to parent from old fragment.
                restrictedChild.getParent().removePreference(restrictedChild);
            }
            mWifiNetworksCategory.addPreference(restrictedChild);
        }

        for (final Preference preference : toRemove) {
            mWifiNetworksCategory.removePreference(preference);
        }

        mCollapsePref.setVisible(mWifiNetworksCategory.shouldShowCollapsePref());
        mPrefMap = newPrefMap;

        clearCurrentAccessPoints();
        mCurrentAccessPoints.addAll(newAccessPoints);
    }

    private void clearCurrentAccessPoints() {
        for (AccessPoint accessPoint : mCurrentAccessPoints) {
            accessPoint.setListener(null);
        }
        mCurrentAccessPoints.clear();
    }

    private boolean isCaptivePortal(AccessPoint accessPoint) {
        return accessPoint.getWifiEntry().canSignIn();
    }

    private Intent makeNetworkDiagnosticsIntent() {
        Intent intent = new Intent();
        intent.setAction(NETWORK_DIAGNOSTICS_ACTION);

        ResolveInfo resolveInfo = MainFragment.systemIntentIsHandled(getContext(), intent);
        if (resolveInfo == null || resolveInfo.activityInfo == null) {
            return null;
        }

        intent.setPackage(resolveInfo.activityInfo.packageName);

        return intent;
    }

    @Override
    public void onConnectivityChange() {
        updateConnectivity();
    }

    @Override
    public void onWifiListChanged() {
        updateWifiList();
    }

    @Override
    public void onAccessPointChanged(AccessPoint accessPoint) {
        RestrictedPreferenceAdapter<TvAccessPointPreference> restrictedPref =
                mPrefMap.get(accessPoint.getWifiEntry());
        if (restrictedPref != null) {
            restrictedPref.updatePreference(AccessPointPreference::refresh);
        }
    }

    @Override
    public void onLevelChanged(AccessPoint accessPoint) {
        RestrictedPreferenceAdapter<TvAccessPointPreference> restrictedPref =
                mPrefMap.get(accessPoint.getWifiEntry());
        if (restrictedPref != null) {
            restrictedPref.updatePreference(AccessPointPreference::onLevelChanged);
        }
    }

    @Override
    protected int getPageId() {
        return TvSettingsEnums.NETWORK;
    }

    private boolean isEasyConnectEnabled() {
        final boolean wifiEnabled = mIsWifiHardwarePresent
                && mConnectivityListener.isWifiEnabledOrEnabling();

        if (!wifiEnabled || !mWifiManager.isEasyConnectSupported()) {
            return false;
        }

        return getContext().getResources().getBoolean(R.bool.config_easyconnect_enabled);
    }

    private Intent getDisableThreadNetworkConfirmationIntent() {
        Bundle args = new FullScreenDialogFragment.DialogBuilder()
                .setIcon(Icon.createWithResource(getContext(), R.drawable.ic_info_outline))
                .setTitle(getContext()
                        .getString(R.string.wifi_settings_thread_network_confirmation_title))
                .setMessage(getContext()
                        .getString(R.string.wifi_settings_thread_network_confirmation_message))
                .setPositiveButton(getContext()
                        .getString(
                                R.string.wifi_settings_thread_network_confirmation_button_confirm))
                .setNegativeButton(getContext().getString(R.string.settings_cancel))
                .build();
        return FullScreenConfirmationActivity.getIntent(getContext(), args);
    }

    private Intent getEnableThreadNetworkConfirmationIntent() {
        Bundle args = new FullScreenDialogFragment.DialogBuilder()
                .setIcon(Icon.createWithResource(getContext(), R.drawable.ic_info_outline))
                .setTitle(getContext()
                        .getString(R.string.wifi_settings_thread_network_energy_title))
                .setMessage(getContext()
                        .getString(R.string.wifi_settings_thread_network_energy_message))
                .setPositiveButton(getContext()
                        .getString(R.string.wifi_settings_thread_network_button_change_energy_mode))
                .setNegativeButton(getContext().getString(R.string.settings_cancel))
                .build();
        return FullScreenConfirmationActivity.getIntent(getContext(), args);
    }
}
