1/*
2 * Copyright (C) 2010 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.settings.wifi;
18
19import android.app.Activity;
20import android.content.Context;
21import android.content.Intent;
22import android.content.pm.ActivityInfo;
23import android.net.NetworkInfo.DetailedState;
24import android.net.wifi.WifiConfiguration;
25import android.net.wifi.WifiManager;
26import android.os.Bundle;
27import android.os.Handler;
28import android.os.Message;
29import android.preference.PreferenceScreen;
30import android.text.TextUtils;
31import android.util.Log;
32import android.view.View;
33import android.view.View.OnClickListener;
34import android.view.ViewGroup;
35import android.view.Window;
36import android.view.inputmethod.InputMethodManager;
37import android.widget.Button;
38import android.widget.ProgressBar;
39import android.widget.TextView;
40
41import com.android.settings.R;
42
43import java.util.Collection;
44import java.util.EnumMap;
45import java.util.List;
46
47/**
48 * WifiSetings Activity specific for SetupWizard with X-Large screen size.
49 */
50public class WifiSettingsForSetupWizardXL extends Activity implements OnClickListener {
51    private static final String TAG = "SetupWizard";
52    private static final boolean DEBUG = true;
53
54    // lock orientation into landscape or portrait
55    private static final String EXTRA_PREFS_LANDSCAPE_LOCK = "extra_prefs_landscape_lock";
56    private static final String EXTRA_PREFS_PORTRAIT_LOCK = "extra_prefs_portrait_lock";
57
58    private static final EnumMap<DetailedState, DetailedState> sNetworkStateMap =
59            new EnumMap<DetailedState, DetailedState>(DetailedState.class);
60
61    static {
62        sNetworkStateMap.put(DetailedState.IDLE, DetailedState.DISCONNECTED);
63        sNetworkStateMap.put(DetailedState.SCANNING, DetailedState.SCANNING);
64        sNetworkStateMap.put(DetailedState.CONNECTING, DetailedState.CONNECTING);
65        sNetworkStateMap.put(DetailedState.AUTHENTICATING, DetailedState.CONNECTING);
66        sNetworkStateMap.put(DetailedState.OBTAINING_IPADDR, DetailedState.CONNECTING);
67        sNetworkStateMap.put(DetailedState.CONNECTED, DetailedState.CONNECTED);
68        sNetworkStateMap.put(DetailedState.SUSPENDED, DetailedState.SUSPENDED);  // ?
69        sNetworkStateMap.put(DetailedState.DISCONNECTING, DetailedState.DISCONNECTED);
70        sNetworkStateMap.put(DetailedState.DISCONNECTED, DetailedState.DISCONNECTED);
71        sNetworkStateMap.put(DetailedState.FAILED, DetailedState.FAILED);
72    }
73
74    private WifiSettings mWifiSettings;
75    private WifiManager mWifiManager;
76
77    /** Used for resizing a padding above title. Hiden when software keyboard is shown. */
78    private View mTopPadding;
79
80    /** Used for resizing a padding of main content. Hiden when software keyboard is shown. */
81    private View mContentPadding;
82
83    private TextView mTitleView;
84    /**
85     * The name of a network currently connecting, or trying to connect.
86     * This may be empty ("") at first, and updated when configuration is changed.
87     */
88    private CharSequence mNetworkName = "";
89    private CharSequence mEditingTitle;
90
91    private ProgressBar mProgressBar;
92    private View mTopDividerNoProgress;
93    /**
94     * Used for resizing a padding between WifiSettings preference and bottom bar when
95     * ProgressBar is visible as a top divider.
96     */
97    private View mBottomPadding;
98
99    private Button mAddNetworkButton;
100    private Button mRefreshButton;
101    private Button mSkipOrNextButton;
102    private Button mBackButton;
103
104    private Button mConnectButton;
105
106    /**
107     * View enclosing {@link WifiSettings}.
108     */
109    private View mWifiSettingsFragmentLayout;
110    private View mConnectingStatusLayout;
111    private TextView mConnectingStatusView;
112
113    /*
114     * States of current screen, which should be saved and restored when Activity is relaunched
115     * with orientation change, etc.
116     */
117    private static final int SCREEN_STATE_DISCONNECTED = 0;
118    private static final int SCREEN_STATE_EDITING = 1;
119    private static final int SCREEN_STATE_CONNECTING = 2;
120    private static final int SCREEN_STATE_CONNECTED = 3;
121
122    /** Current screen state. */
123    private int mScreenState = SCREEN_STATE_DISCONNECTED;
124
125    private WifiConfigUiForSetupWizardXL mWifiConfig;
126
127    private InputMethodManager mInputMethodManager;
128
129    /**
130     * Previous network connection state reported by main Wifi module.
131     *
132     * Note that we don't use original {@link DetailedState} object but simplified one translated
133     * using sNetworkStateMap.
134     */
135    private DetailedState mPreviousNetworkState = DetailedState.DISCONNECTED;
136
137    @Override
138    public void onCreate(Bundle savedInstanceState) {
139        super.onCreate(savedInstanceState);
140        requestWindowFeature(Window.FEATURE_NO_TITLE);
141        setContentView(R.layout.wifi_settings_for_setup_wizard_xl);
142
143        mWifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
144        // There's no button here enabling wifi network, so we need to enable it without
145        // users' request.
146        mWifiManager.setWifiEnabled(true);
147
148        mWifiSettings =
149                (WifiSettings)getFragmentManager().findFragmentById(R.id.wifi_setup_fragment);
150        mInputMethodManager = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
151
152        initViews();
153
154        // At first, Wifi module doesn't return SCANNING state (it's too early), so we manually
155        // show it.
156        showScanningState();
157    }
158
159    private void initViews() {
160        Intent intent = getIntent();
161
162        if (intent.getBooleanExtra("firstRun", false)) {
163            final View layoutRoot = findViewById(R.id.layout_root);
164            layoutRoot.setSystemUiVisibility(View.STATUS_BAR_DISABLE_BACK);
165        }
166        if (intent.getBooleanExtra(EXTRA_PREFS_LANDSCAPE_LOCK, false)) {
167            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
168        }
169        if (intent.getBooleanExtra(EXTRA_PREFS_PORTRAIT_LOCK, false)) {
170            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
171        }
172
173        mTitleView = (TextView)findViewById(R.id.wifi_setup_title);
174        mProgressBar = (ProgressBar)findViewById(R.id.scanning_progress_bar);
175        mProgressBar.setMax(2);
176        mTopDividerNoProgress = findViewById(R.id.top_divider_no_progress);
177        mBottomPadding = findViewById(R.id.bottom_padding);
178
179        mProgressBar.setVisibility(View.VISIBLE);
180        mProgressBar.setIndeterminate(true);
181        mTopDividerNoProgress.setVisibility(View.GONE);
182
183        mAddNetworkButton = (Button)findViewById(R.id.wifi_setup_add_network);
184        mAddNetworkButton.setOnClickListener(this);
185        mRefreshButton = (Button)findViewById(R.id.wifi_setup_refresh_list);
186        mRefreshButton.setOnClickListener(this);
187        mSkipOrNextButton = (Button)findViewById(R.id.wifi_setup_skip_or_next);
188        mSkipOrNextButton.setOnClickListener(this);
189        mConnectButton = (Button)findViewById(R.id.wifi_setup_connect);
190        mConnectButton.setOnClickListener(this);
191        mBackButton = (Button)findViewById(R.id.wifi_setup_cancel);
192        mBackButton.setOnClickListener(this);
193
194        mTopPadding = findViewById(R.id.top_padding);
195        mContentPadding = findViewById(R.id.content_padding);
196
197        mWifiSettingsFragmentLayout = findViewById(R.id.wifi_settings_fragment_layout);
198        mConnectingStatusLayout = findViewById(R.id.connecting_status_layout);
199        mConnectingStatusView = (TextView) findViewById(R.id.connecting_status);
200    }
201
202    private void restoreFirstVisibilityState() {
203        showDefaultTitle();
204        mAddNetworkButton.setVisibility(View.VISIBLE);
205        mRefreshButton.setVisibility(View.VISIBLE);
206        mSkipOrNextButton.setVisibility(View.VISIBLE);
207        mConnectButton.setVisibility(View.GONE);
208        mBackButton.setVisibility(View.GONE);
209        setPaddingVisibility(View.VISIBLE);
210    }
211
212    @Override
213    public void onClick(View view) {
214        hideSoftwareKeyboard();
215        if (view == mAddNetworkButton) {
216            if (DEBUG) Log.d(TAG, "AddNetwork button pressed");
217            onAddNetworkButtonPressed();
218        } else if (view == mRefreshButton) {
219            if (DEBUG) Log.d(TAG, "Refresh button pressed");
220            refreshAccessPoints(true);
221        } else if (view == mSkipOrNextButton) {
222            if (DEBUG) Log.d(TAG, "Skip/Next button pressed");
223            if (TextUtils.equals(getString(R.string.wifi_setup_skip), ((Button)view).getText())) {
224                // We don't want to let Wifi enabled when a user press skip without choosing
225                // any access point.
226                mWifiManager.setWifiEnabled(false);
227                // Notify "skip"
228                setResult(RESULT_FIRST_USER);
229            } else {
230                setResult(RESULT_OK);
231            }
232            finish();
233        } else if (view == mConnectButton) {
234            if (DEBUG) Log.d(TAG, "Connect button pressed");
235            onConnectButtonPressed();
236        } else if (view == mBackButton) {
237            if (DEBUG) Log.d(TAG, "Back button pressed");
238            onBackButtonPressed();
239        }
240    }
241
242    private void hideSoftwareKeyboard() {
243        if (DEBUG) Log.i(TAG, "Hiding software keyboard.");
244        final View focusedView = getCurrentFocus();
245        if (focusedView != null) {
246            mInputMethodManager.hideSoftInputFromWindow(focusedView.getWindowToken(), 0);
247        }
248    }
249
250    // Called from WifiSettings
251    /* package */ void updateConnectionState(DetailedState originalState) {
252        final DetailedState state = sNetworkStateMap.get(originalState);
253
254        if (originalState == DetailedState.FAILED) {
255            // We clean up the current connectivity status and let users select another network
256            // if they want.
257            refreshAccessPoints(true);
258        }
259
260        switch (state) {
261        case SCANNING: {
262            if (mScreenState == SCREEN_STATE_DISCONNECTED) {
263                if (mWifiSettings.getAccessPointsCount() == 0) {
264                    showScanningState();
265                } else {
266                    showDisconnectedProgressBar();
267                    mWifiSettingsFragmentLayout.setVisibility(View.VISIBLE);
268                    mBottomPadding.setVisibility(View.GONE);
269                }
270            } else {
271                showDisconnectedProgressBar();
272            }
273            break;
274        }
275        case CONNECTING: {
276            if (mScreenState == SCREEN_STATE_CONNECTING) {
277                showConnectingState();
278            }
279            break;
280        }
281        case CONNECTED: {
282            showConnectedState();
283            break;
284        }
285        default:  // DISCONNECTED, FAILED
286            if (mScreenState != SCREEN_STATE_CONNECTED &&
287                    mWifiSettings.getAccessPointsCount() > 0) {
288                showDisconnectedState(Summary.get(this, state));
289            }
290            break;
291        }
292        mPreviousNetworkState = state;
293    }
294
295    private void showDisconnectedState(String stateString) {
296        showDisconnectedProgressBar();
297        if (mScreenState == SCREEN_STATE_DISCONNECTED &&
298                mWifiSettings.getAccessPointsCount() > 0) {
299            mWifiSettingsFragmentLayout.setVisibility(View.VISIBLE);
300            mBottomPadding.setVisibility(View.GONE);
301        }
302        mAddNetworkButton.setEnabled(true);
303        mRefreshButton.setEnabled(true);
304    }
305
306    private void showConnectingState() {
307        mScreenState = SCREEN_STATE_CONNECTING;
308
309        mBackButton.setVisibility(View.VISIBLE);
310        // We save this title and show it when authentication failed.
311        mEditingTitle = mTitleView.getText();
312        showConnectingTitle();
313        showConnectingProgressBar();
314
315        setPaddingVisibility(View.VISIBLE);
316    }
317
318    private void showConnectedState() {
319        // Once we show "connected" screen, we won't change it even when the device becomes
320        // disconnected afterwards. We keep the state unless a user explicitly cancel it
321        // (by pressing "back" button).
322        mScreenState = SCREEN_STATE_CONNECTED;
323
324        hideSoftwareKeyboard();
325        setPaddingVisibility(View.VISIBLE);
326
327        showConnectedTitle();
328        showConnectedProgressBar();
329
330        mWifiSettingsFragmentLayout.setVisibility(View.GONE);
331        mConnectingStatusLayout.setVisibility(View.VISIBLE);
332
333        mConnectingStatusView.setText(R.string.wifi_setup_description_connected);
334        mConnectButton.setVisibility(View.GONE);
335        mAddNetworkButton.setVisibility(View.GONE);
336        mRefreshButton.setVisibility(View.GONE);
337        mBackButton.setVisibility(View.VISIBLE);
338        mBackButton.setText(R.string.wifi_setup_back);
339        mSkipOrNextButton.setVisibility(View.VISIBLE);
340        mSkipOrNextButton.setEnabled(true);
341    }
342
343    private void showDefaultTitle() {
344        mTitleView.setText(getString(R.string.wifi_setup_title));
345    }
346
347    private void showAddNetworkTitle() {
348        mNetworkName = "";
349        mTitleView.setText(R.string.wifi_setup_title_add_network);
350    }
351
352    private void showEditingTitle() {
353        if (TextUtils.isEmpty(mNetworkName) && mWifiConfig != null) {
354            if (mWifiConfig.getController() != null &&
355                mWifiConfig.getController().getConfig() != null) {
356                mNetworkName = mWifiConfig.getController().getConfig().SSID;
357            } else {
358                Log.w(TAG, "Unexpected null found (WifiController or WifiConfig is null). " +
359                        "Ignore them.");
360            }
361        }
362        mTitleView.setText(getString(R.string.wifi_setup_title_editing_network, mNetworkName));
363    }
364
365    private void showConnectingTitle() {
366        if (TextUtils.isEmpty(mNetworkName) && mWifiConfig != null) {
367            if (mWifiConfig.getController() != null &&
368                    mWifiConfig.getController().getConfig() != null) {
369                mNetworkName = mWifiConfig.getController().getConfig().SSID;
370            } else {
371                Log.w(TAG, "Unexpected null found (WifiController or WifiConfig is null). " +
372                        "Ignore them.");
373            }
374        }
375        mTitleView.setText(getString(R.string.wifi_setup_title_connecting_network, mNetworkName));
376    }
377
378    private void showConnectedTitle() {
379        if (TextUtils.isEmpty(mNetworkName) && mWifiConfig != null) {
380            if (mWifiConfig.getController() != null &&
381                    mWifiConfig.getController().getConfig() != null) {
382                mNetworkName = mWifiConfig.getController().getConfig().SSID;
383            } else {
384                Log.w(TAG, "Unexpected null found (WifiController or WifiConfig is null). " +
385                        "Ignore them.");
386            }
387        }
388        mTitleView.setText(getString(R.string.wifi_setup_title_connected_network, mNetworkName));
389    }
390
391    /**
392     * Shows top divider with ProgressBar without defining the state of the ProgressBar.
393     *
394     * @see #showScanningProgressBar()
395     * @see #showConnectedProgressBar()
396     * @see #showConnectingProgressBar()
397     */
398    private void showTopDividerWithProgressBar() {
399        mProgressBar.setVisibility(View.VISIBLE);
400        mTopDividerNoProgress.setVisibility(View.GONE);
401        mBottomPadding.setVisibility(View.GONE);
402    }
403
404    private void showScanningState() {
405        setPaddingVisibility(View.VISIBLE);
406        mWifiSettingsFragmentLayout.setVisibility(View.GONE);
407        showScanningProgressBar();
408    }
409
410    private void onAddNetworkButtonPressed() {
411        mWifiSettings.onAddNetworkPressed();
412    }
413
414    /**
415     * Called when the screen enters wifi configuration UI. UI widget for configuring network
416     * (a.k.a. ConfigPreference) should be taken care of by caller side.
417     * This method should handle buttons' visibility/enabled.
418     * @param selectedAccessPoint AccessPoint object being selected. null when a user pressed
419     * "Add network" button, meaning there's no selected access point.
420     */
421    /* package */ void showConfigUi(AccessPoint selectedAccessPoint, boolean edit) {
422        mScreenState = SCREEN_STATE_EDITING;
423
424        if (selectedAccessPoint != null &&
425                (selectedAccessPoint.security == AccessPoint.SECURITY_WEP ||
426                        selectedAccessPoint.security == AccessPoint.SECURITY_PSK)) {
427            // We forcibly set edit as true so that users can modify every field if they want,
428            // while config UI doesn't allow them to edit some of them when edit is false
429            // (e.g. password field is hiden when edit==false).
430            edit = true;
431        }
432
433        // We don't want to keep scanning Wifi networks during users' configuring a network.
434        mWifiSettings.pauseWifiScan();
435
436        mWifiSettingsFragmentLayout.setVisibility(View.GONE);
437        mConnectingStatusLayout.setVisibility(View.GONE);
438        final ViewGroup parent = (ViewGroup)findViewById(R.id.wifi_config_ui);
439        parent.setVisibility(View.VISIBLE);
440        parent.removeAllViews();
441        mWifiConfig = new WifiConfigUiForSetupWizardXL(this, parent, selectedAccessPoint, edit);
442
443        if (selectedAccessPoint == null) {  // "Add network" flow
444            showAddNetworkTitle();
445            mConnectButton.setVisibility(View.VISIBLE);
446
447            showDisconnectedProgressBar();
448            showEditingButtonState();
449        } else if (selectedAccessPoint.security == AccessPoint.SECURITY_NONE) {
450            mNetworkName = selectedAccessPoint.getTitle().toString();
451
452            // onConnectButtonPressed() will change visibility status.
453            mConnectButton.performClick();
454        } else {
455            mNetworkName = selectedAccessPoint.getTitle().toString();
456            showEditingTitle();
457            showDisconnectedProgressBar();
458            showEditingButtonState();
459            if (selectedAccessPoint.security == AccessPoint.SECURITY_EAP) {
460                onEapNetworkSelected();
461            } else {
462                mConnectButton.setVisibility(View.VISIBLE);
463
464                // WifiConfigController shows Connect button as "Save" when edit==true and a user
465                // tried to connect the network.
466                // In SetupWizard, we just show the button as "Connect" instead.
467                mConnectButton.setText(R.string.wifi_connect);
468                mBackButton.setText(R.string.wifi_setup_cancel);
469            }
470        }
471    }
472
473    /**
474     * Called before security fields are correctly set by {@link WifiConfigController}.
475     *
476     * @param view security field view
477     * @param accessPointSecurity type of security. e.g. AccessPoint.SECURITY_NONE
478     * @return true when it is ok for the caller to init security fields. false when
479     * all security fields are managed by this method, and thus the caller shouldn't touch them.
480     */
481    /* package */ boolean initSecurityFields(View view, int accessPointSecurity) {
482        // Reset all states tweaked below.
483        view.findViewById(R.id.eap_not_supported).setVisibility(View.GONE);
484        view.findViewById(R.id.eap_not_supported_for_add_network).setVisibility(View.GONE);
485        view.findViewById(R.id.ssid_text).setVisibility(View.VISIBLE);
486        view.findViewById(R.id.ssid_layout).setVisibility(View.VISIBLE);
487
488        if (accessPointSecurity == AccessPoint.SECURITY_EAP) {
489            setPaddingVisibility(View.VISIBLE);
490            hideSoftwareKeyboard();
491
492            // In SetupWizard for XLarge screen, we don't have enough space for showing
493            // configurations needed for EAP. We instead disable the whole feature there and let
494            // users configure those networks after the setup.
495            if (view.findViewById(R.id.type_ssid).getVisibility() == View.VISIBLE) {
496                view.findViewById(R.id.eap_not_supported_for_add_network)
497                        .setVisibility(View.VISIBLE);
498            } else {
499                view.findViewById(R.id.eap_not_supported).setVisibility(View.VISIBLE);
500            }
501            view.findViewById(R.id.security_fields).setVisibility(View.GONE);
502            view.findViewById(R.id.ssid_text).setVisibility(View.GONE);
503            view.findViewById(R.id.ssid_layout).setVisibility(View.GONE);
504            onEapNetworkSelected();
505
506            // This method did init security fields by itself. The caller must not do it.
507            return false;
508        }
509
510        mConnectButton.setVisibility(View.VISIBLE);
511        setPaddingVisibility(View.GONE);
512
513        // In "add network" flow, we'll see multiple initSecurityFields() calls with different
514        // accessPointSecurity variable. We want to show software keyboard conditionally everytime
515        // when this method is called.
516        if (mWifiConfig != null) {
517            if (accessPointSecurity == AccessPoint.SECURITY_PSK ||
518                    accessPointSecurity == AccessPoint.SECURITY_WEP) {
519                mWifiConfig.requestFocusAndShowKeyboard(R.id.password);
520            } else {
521                mWifiConfig.requestFocusAndShowKeyboard(R.id.ssid);
522            }
523        }
524
525        // Let the caller init security fields.
526        return true;
527    }
528
529    private void onEapNetworkSelected() {
530        mConnectButton.setVisibility(View.GONE);
531        mBackButton.setText(R.string.wifi_setup_back);
532    }
533
534    private void showEditingButtonState() {
535        mSkipOrNextButton.setVisibility(View.GONE);
536        mAddNetworkButton.setVisibility(View.GONE);
537        mRefreshButton.setVisibility(View.GONE);
538        mBackButton.setVisibility(View.VISIBLE);
539    }
540
541    // May be called when user press "connect" button in WifiDialog
542    /* package */ void onConnectButtonPressed() {
543        mScreenState = SCREEN_STATE_CONNECTING;
544
545        mWifiSettings.submit(mWifiConfig.getController());
546
547        // updateConnectionState() isn't called soon by the main Wifi module after the user's
548        // "connect" request, and the user still sees "not connected" message for a while, which
549        // looks strange for users though legitimate from the view of the module.
550        //
551        // We instead manually show "connecting" message before the system gets actual
552        // "connecting" message from Wifi module.
553        showConnectingState();
554
555        // Might be better to delay showing this button.
556        mBackButton.setVisibility(View.VISIBLE);
557        mBackButton.setText(R.string.wifi_setup_back);
558
559        final ViewGroup parent = (ViewGroup)findViewById(R.id.wifi_config_ui);
560        parent.setVisibility(View.GONE);
561        mConnectingStatusLayout.setVisibility(View.VISIBLE);
562        mConnectingStatusView.setText(R.string.wifi_setup_description_connecting);
563
564        mSkipOrNextButton.setVisibility(View.VISIBLE);
565        mSkipOrNextButton.setEnabled(false);
566        mConnectButton.setVisibility(View.GONE);
567        mAddNetworkButton.setVisibility(View.GONE);
568        mRefreshButton.setVisibility(View.GONE);
569    }
570
571    private void onBackButtonPressed() {
572
573        if (mScreenState == SCREEN_STATE_CONNECTING || mScreenState == SCREEN_STATE_CONNECTED) {
574            if (DEBUG) Log.d(TAG, "Back button pressed after connect action.");
575            mScreenState = SCREEN_STATE_DISCONNECTED;
576
577            // When a user press "Back" button after pressing "Connect" button, we want to cancel
578            // the "Connect" request and refresh the whole Wifi status.
579            restoreFirstVisibilityState();
580
581            mSkipOrNextButton.setEnabled(true);
582            changeNextButtonState(false);  // Skip
583
584            // Wifi list becomes empty for a moment. We show "scanning" effect to a user so that
585            // he/she won't be astonished there. This stops once the scan finishes.
586            showScanningState();
587
588            // Remembered networks may be re-used during SetupWizard, which confuse users.
589            // We force the module to forget them to reduce UX complexity
590            final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
591            for (WifiConfiguration config : configs) {
592                if (DEBUG) {
593                    Log.d(TAG, String.format("forgeting Wi-Fi network \"%s\" (id: %d)",
594                            config.SSID, config.networkId));
595                }
596                mWifiManager.forget(config.networkId, new WifiManager.ActionListener() {
597                        public void onSuccess() {
598                        }
599                        public void onFailure(int reason) {
600                            //TODO: Add failure UI
601                        }
602                        });
603            }
604
605            mWifiSettingsFragmentLayout.setVisibility(View.GONE);
606            refreshAccessPoints(true);
607        } else { // During user's Wifi configuration.
608            mScreenState = SCREEN_STATE_DISCONNECTED;
609            mWifiSettings.resumeWifiScan();
610
611            restoreFirstVisibilityState();
612
613            mAddNetworkButton.setEnabled(true);
614            mRefreshButton.setEnabled(true);
615            mSkipOrNextButton.setEnabled(true);
616            showDisconnectedProgressBar();
617            mWifiSettingsFragmentLayout.setVisibility(View.VISIBLE);
618            mBottomPadding.setVisibility(View.GONE);
619        }
620
621        setPaddingVisibility(View.VISIBLE);
622        mConnectingStatusLayout.setVisibility(View.GONE);
623        final ViewGroup parent = (ViewGroup)findViewById(R.id.wifi_config_ui);
624        parent.removeAllViews();
625        parent.setVisibility(View.GONE);
626        mWifiConfig = null;
627    }
628
629    /**
630     * @param connected true when the device is connected to a specific network.
631     */
632    /* package */ void changeNextButtonState(boolean connected) {
633        if (connected) {
634            mSkipOrNextButton.setText(R.string.wifi_setup_next);
635        } else {
636            mSkipOrNextButton.setText(R.string.wifi_setup_skip);
637        }
638    }
639
640    /**
641     * Called when the list of AccessPoints are modified and this Activity needs to refresh
642     * the list.
643     * @param preferenceScreen
644     */
645    /* package */ void onAccessPointsUpdated(
646            PreferenceScreen preferenceScreen, Collection<AccessPoint> accessPoints) {
647        // If we already show some of access points but the bar still shows "scanning" state, it
648        // should be stopped.
649        if (mProgressBar.isIndeterminate() && accessPoints.size() > 0) {
650            showDisconnectedProgressBar();
651            if (mScreenState == SCREEN_STATE_DISCONNECTED) {
652                mWifiSettingsFragmentLayout.setVisibility(View.VISIBLE);
653                mBottomPadding.setVisibility(View.GONE);
654            }
655            mAddNetworkButton.setEnabled(true);
656            mRefreshButton.setEnabled(true);
657        }
658
659        for (AccessPoint accessPoint : accessPoints) {
660            accessPoint.setLayoutResource(R.layout.custom_preference);
661            preferenceScreen.addPreference(accessPoint);
662        }
663    }
664
665    private void refreshAccessPoints(boolean disconnectNetwork) {
666        showScanningState();
667
668        if (disconnectNetwork) {
669            mWifiManager.disconnect();
670        }
671
672        mWifiSettings.refreshAccessPoints();
673    }
674
675    /**
676     * Called when {@link WifiSettings} received
677     * {@link WifiManager#SUPPLICANT_STATE_CHANGED_ACTION}.
678     */
679    /* package */ void onSupplicantStateChanged(Intent intent) {
680        final int errorCode = intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, -1);
681        if (errorCode == WifiManager.ERROR_AUTHENTICATING) {
682            Log.i(TAG, "Received authentication error event.");
683            onAuthenticationFailure();
684        }
685    }
686
687    /**
688     * Called once when Authentication failed.
689     */
690    private void onAuthenticationFailure() {
691        mScreenState = SCREEN_STATE_EDITING;
692
693        mSkipOrNextButton.setVisibility(View.GONE);
694        mConnectButton.setVisibility(View.VISIBLE);
695        mConnectButton.setEnabled(true);
696
697        if (!TextUtils.isEmpty(mEditingTitle)) {
698            mTitleView.setText(mEditingTitle);
699        } else {
700            Log.w(TAG, "Title during editing/adding a network was empty.");
701            showEditingTitle();
702        }
703
704        final ViewGroup parent = (ViewGroup)findViewById(R.id.wifi_config_ui);
705        parent.setVisibility(View.VISIBLE);
706        mConnectingStatusLayout.setVisibility(View.GONE);
707
708        showDisconnectedProgressBar();
709        setPaddingVisibility(View.GONE);
710    }
711
712    // Used by WifiConfigUiForSetupWizardXL
713    /* package */ void setPaddingVisibility(int visibility) {
714        mTopPadding.setVisibility(visibility);
715        mContentPadding.setVisibility(visibility);
716    }
717
718    private void showDisconnectedProgressBar() {
719        // The device may report DISCONNECTED during connecting to a network, at which we don't
720        // want to lose bottom padding of top divider implicitly added by ProgressBar.
721        if (mScreenState == SCREEN_STATE_DISCONNECTED) {
722            mProgressBar.setVisibility(View.GONE);
723            mProgressBar.setIndeterminate(false);
724            mTopDividerNoProgress.setVisibility(View.VISIBLE);
725        } else {
726            mProgressBar.setVisibility(View.VISIBLE);
727            mProgressBar.setIndeterminate(false);
728            mProgressBar.setProgress(0);
729            mTopDividerNoProgress.setVisibility(View.GONE);
730        }
731    }
732
733    /**
734     * Shows top divider with ProgressBar, whose state is intermediate.
735     */
736    private void showScanningProgressBar() {
737        showTopDividerWithProgressBar();
738        mProgressBar.setIndeterminate(true);
739    }
740
741    /**
742     * Shows top divider with ProgressBar, showing "connecting" state.
743     */
744    private void showConnectingProgressBar() {
745        showTopDividerWithProgressBar();
746        mProgressBar.setIndeterminate(false);
747        mProgressBar.setProgress(1);
748    }
749
750    private void showConnectedProgressBar() {
751        showTopDividerWithProgressBar();
752        mProgressBar.setIndeterminate(false);
753        mProgressBar.setProgress(2);
754    }
755
756    /**
757     * Called when WifiManager is requested to save a network.
758     */
759    /* package */ void onSaveNetwork(WifiConfiguration config) {
760        // We want to both save and connect a network. connectNetwork() does both.
761        mWifiManager.connect(config, new WifiManager.ActionListener() {
762                public void onSuccess() {
763                }
764                public void onFailure(int reason) {
765                //TODO: Add failure UI
766                }
767                });
768    }
769}
770