1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.tv.settings.connectivity;
18
19import android.content.Context;
20import android.net.wifi.WifiConfiguration;
21import android.os.Bundle;
22import android.os.Handler;
23import android.os.SystemClock;
24import android.provider.Settings;
25import android.support.v7.preference.Preference;
26import android.support.v7.preference.PreferenceCategory;
27import android.support.v7.preference.PreferenceManager;
28import android.support.v7.preference.TwoStatePreference;
29
30import com.android.internal.logging.nano.MetricsProto;
31import com.android.settingslib.wifi.AccessPoint;
32import com.android.settingslib.wifi.AccessPointPreference;
33import com.android.tv.settings.R;
34import com.android.tv.settings.SettingsPreferenceFragment;
35
36import java.util.Collection;
37import java.util.HashSet;
38import java.util.Set;
39
40/**
41 * Fragment for controlling network connectivity
42 */
43public class NetworkFragment extends SettingsPreferenceFragment implements
44        ConnectivityListener.Listener, ConnectivityListener.WifiNetworkListener,
45        AccessPoint.AccessPointListener {
46
47    private static final String KEY_WIFI_ENABLE = "wifi_enable";
48    private static final String KEY_WIFI_LIST = "wifi_list";
49    private static final String KEY_WIFI_COLLAPSE = "wifi_collapse";
50    private static final String KEY_WIFI_OTHER = "wifi_other";
51    private static final String KEY_WIFI_ADD = "wifi_add";
52    private static final String KEY_WIFI_ALWAYS_SCAN = "wifi_always_scan";
53    private static final String KEY_ETHERNET = "ethernet";
54    private static final String KEY_ETHERNET_STATUS = "ethernet_status";
55    private static final String KEY_ETHERNET_PROXY = "ethernet_proxy";
56    private static final String KEY_ETHERNET_DHCP = "ethernet_dhcp";
57
58    private static final int INITIAL_UPDATE_DELAY = 500;
59
60    private ConnectivityListener mConnectivityListener;
61    private AccessPointPreference.UserBadgeCache mUserBadgeCache;
62
63    private TwoStatePreference mEnableWifiPref;
64    private CollapsibleCategory mWifiNetworksCategory;
65    private Preference mCollapsePref;
66    private Preference mAddPref;
67    private TwoStatePreference mAlwaysScan;
68    private PreferenceCategory mEthernetCategory;
69    private Preference mEthernetStatusPref;
70    private Preference mEthernetProxyPref;
71    private Preference mEthernetDhcpPref;
72
73    private final Handler mHandler = new Handler();
74    private long mNoWifiUpdateBeforeMillis;
75    private Runnable mInitialUpdateWifiListRunnable = new Runnable() {
76        @Override
77        public void run() {
78            mNoWifiUpdateBeforeMillis = 0;
79            updateWifiList();
80        }
81    };
82
83    public static NetworkFragment newInstance() {
84        return new NetworkFragment();
85    }
86
87    @Override
88    public void onCreate(Bundle savedInstanceState) {
89        mConnectivityListener = new ConnectivityListener(getContext(), this, getLifecycle());
90        mUserBadgeCache =
91                new AccessPointPreference.UserBadgeCache(getContext().getPackageManager());
92        super.onCreate(savedInstanceState);
93    }
94
95    @Override
96    public void onStart() {
97        super.onStart();
98        mConnectivityListener.setWifiListener(this);
99        mNoWifiUpdateBeforeMillis = SystemClock.elapsedRealtime() + INITIAL_UPDATE_DELAY;
100        updateWifiList();
101    }
102
103    @Override
104    public void onResume() {
105        super.onResume();
106        // There doesn't seem to be an API to listen to everything this could cover, so
107        // tickle it here and hope for the best.
108        updateConnectivity();
109    }
110
111    @Override
112    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
113        getPreferenceManager().setPreferenceComparisonCallback(
114                new PreferenceManager.SimplePreferenceComparisonCallback());
115        setPreferencesFromResource(R.xml.network, null);
116
117        mEnableWifiPref = (TwoStatePreference) findPreference(KEY_WIFI_ENABLE);
118        mWifiNetworksCategory = (CollapsibleCategory) findPreference(KEY_WIFI_LIST);
119        mCollapsePref = findPreference(KEY_WIFI_COLLAPSE);
120        mAddPref = findPreference(KEY_WIFI_ADD);
121        mAlwaysScan = (TwoStatePreference) findPreference(KEY_WIFI_ALWAYS_SCAN);
122
123        mEthernetCategory = (PreferenceCategory) findPreference(KEY_ETHERNET);
124        mEthernetStatusPref = findPreference(KEY_ETHERNET_STATUS);
125        mEthernetProxyPref = findPreference(KEY_ETHERNET_PROXY);
126        mEthernetProxyPref.setIntent(EditProxySettingsActivity.createIntent(getContext(),
127                WifiConfiguration.INVALID_NETWORK_ID));
128        mEthernetDhcpPref = findPreference(KEY_ETHERNET_DHCP);
129        mEthernetDhcpPref.setIntent(EditIpSettingsActivity.createIntent(getContext(),
130                WifiConfiguration.INVALID_NETWORK_ID));
131    }
132
133    @Override
134    public boolean onPreferenceTreeClick(Preference preference) {
135        if (preference.getKey() == null) {
136            return super.onPreferenceTreeClick(preference);
137        }
138        switch (preference.getKey()) {
139            case KEY_WIFI_ENABLE:
140                mConnectivityListener.setWifiEnabled(mEnableWifiPref.isChecked());
141                if (mMetricsFeatureProvider != null) {
142                    if (mEnableWifiPref.isChecked()) {
143                        mMetricsFeatureProvider.action(getContext(),
144                                MetricsProto.MetricsEvent.ACTION_WIFI_ON);
145                    } else {
146                        // Log if user was connected at the time of switching off.
147                        mMetricsFeatureProvider.action(getContext(),
148                                MetricsProto.MetricsEvent.ACTION_WIFI_OFF,
149                                mConnectivityListener.isWifiConnected());
150                    }
151                }
152                return true;
153            case KEY_WIFI_COLLAPSE:
154                final boolean collapse = !mWifiNetworksCategory.isCollapsed();
155                mCollapsePref.setTitle(collapse
156                        ? R.string.wifi_setting_see_all : R.string.wifi_setting_see_fewer);
157                mWifiNetworksCategory.setCollapsed(collapse);
158                return true;
159            case KEY_WIFI_ALWAYS_SCAN:
160                Settings.Global.putInt(getActivity().getContentResolver(),
161                        Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE,
162                        mAlwaysScan.isChecked() ? 1 : 0);
163                return true;
164            case KEY_ETHERNET_STATUS:
165                return true;
166            case KEY_WIFI_ADD:
167                mMetricsFeatureProvider.action(getActivity(),
168                        MetricsProto.MetricsEvent.ACTION_WIFI_ADD_NETWORK);
169                break;
170        }
171        return super.onPreferenceTreeClick(preference);
172    }
173
174    private void updateConnectivity() {
175        if (!isAdded()) {
176            return;
177        }
178
179        final boolean wifiEnabled = mConnectivityListener.isWifiEnabledOrEnabling();
180        mEnableWifiPref.setChecked(wifiEnabled);
181
182        mWifiNetworksCategory.setVisible(wifiEnabled);
183        mCollapsePref.setVisible(wifiEnabled && mWifiNetworksCategory.shouldShowCollapsePref());
184        mAddPref.setVisible(wifiEnabled);
185
186        if (!wifiEnabled) {
187            updateWifiList();
188        }
189
190        int scanAlwaysAvailable = 0;
191        try {
192            scanAlwaysAvailable = Settings.Global.getInt(getContext().getContentResolver(),
193                    Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE);
194        } catch (Settings.SettingNotFoundException e) {
195            // Ignore
196        }
197        mAlwaysScan.setChecked(scanAlwaysAvailable == 1);
198
199        final boolean ethernetAvailable = mConnectivityListener.isEthernetAvailable();
200        mEthernetCategory.setVisible(ethernetAvailable);
201        mEthernetStatusPref.setVisible(ethernetAvailable);
202        mEthernetProxyPref.setVisible(ethernetAvailable);
203        mEthernetDhcpPref.setVisible(ethernetAvailable);
204
205        if (ethernetAvailable) {
206            final boolean ethernetConnected =
207                    mConnectivityListener.isEthernetConnected();
208            mEthernetStatusPref.setTitle(ethernetConnected
209                    ? R.string.connected : R.string.not_connected);
210            mEthernetStatusPref.setSummary(mConnectivityListener.getEthernetIpAddress());
211        }
212    }
213
214    private void updateWifiList() {
215        if (!isAdded()) {
216            return;
217        }
218
219        if (!mConnectivityListener.isWifiEnabledOrEnabling()) {
220            mWifiNetworksCategory.removeAll();
221            mNoWifiUpdateBeforeMillis = 0;
222            return;
223        }
224
225        final long now = SystemClock.elapsedRealtime();
226        if (mNoWifiUpdateBeforeMillis > now) {
227            mHandler.removeCallbacks(mInitialUpdateWifiListRunnable);
228            mHandler.postDelayed(mInitialUpdateWifiListRunnable,
229                    mNoWifiUpdateBeforeMillis - now);
230            return;
231        }
232
233        final int existingCount = mWifiNetworksCategory.getRealPreferenceCount();
234        final Set<Preference> toRemove = new HashSet<>(existingCount);
235        for (int i = 0; i < existingCount; i++) {
236            toRemove.add(mWifiNetworksCategory.getPreference(i));
237        }
238
239        final Context themedContext = getPreferenceManager().getContext();
240        final Collection<AccessPoint> accessPoints = mConnectivityListener.getAvailableNetworks();
241        int index = 0;
242        for (final AccessPoint accessPoint : accessPoints) {
243            accessPoint.setListener(this);
244            AccessPointPreference pref = (AccessPointPreference) accessPoint.getTag();
245            if (pref == null) {
246                pref = new AccessPointPreference(accessPoint, themedContext, mUserBadgeCache,
247                        false);
248                accessPoint.setTag(pref);
249            } else {
250                toRemove.remove(pref);
251            }
252            if (accessPoint.isActive()) {
253                pref.setFragment(WifiDetailsFragment.class.getName());
254                WifiDetailsFragment.prepareArgs(pref.getExtras(), accessPoint);
255                pref.setIntent(null);
256            } else {
257                pref.setFragment(null);
258                pref.setIntent(WifiConnectionActivity.createIntent(getContext(), accessPoint));
259            }
260            pref.setOrder(index++);
261            // Double-adding is harmless
262            mWifiNetworksCategory.addPreference(pref);
263        }
264
265        for (final Preference preference : toRemove) {
266            mWifiNetworksCategory.removePreference(preference);
267        }
268
269        mCollapsePref.setVisible(mWifiNetworksCategory.shouldShowCollapsePref());
270    }
271
272    @Override
273    public void onConnectivityChange() {
274        updateConnectivity();
275    }
276
277    @Override
278    public void onWifiListChanged() {
279        updateWifiList();
280    }
281
282    @Override
283    public void onAccessPointChanged(AccessPoint accessPoint) {
284        ((AccessPointPreference) accessPoint.getTag()).refresh();
285    }
286
287    @Override
288    public void onLevelChanged(AccessPoint accessPoint) {
289        ((AccessPointPreference) accessPoint.getTag()).onLevelChanged();
290    }
291
292    @Override
293    public int getMetricsCategory() {
294        return MetricsProto.MetricsEvent.SETTINGS_NETWORK_CATEGORY;
295    }
296}
297