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 
17 package com.android.settings.wifi;
18 
19 import android.app.Activity;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.pm.ActivityInfo;
23 import android.net.NetworkInfo.DetailedState;
24 import android.net.wifi.WifiConfiguration;
25 import android.net.wifi.WifiManager;
26 import android.os.Bundle;
27 import android.os.Handler;
28 import android.os.Message;
29 import android.preference.PreferenceScreen;
30 import android.text.TextUtils;
31 import android.util.Log;
32 import android.view.View;
33 import android.view.View.OnClickListener;
34 import android.view.ViewGroup;
35 import android.view.Window;
36 import android.view.inputmethod.InputMethodManager;
37 import android.widget.Button;
38 import android.widget.ProgressBar;
39 import android.widget.TextView;
40 
41 import com.android.settings.R;
42 
43 import java.util.Collection;
44 import java.util.EnumMap;
45 import java.util.List;
46 
47 /**
48  * WifiSetings Activity specific for SetupWizard with X-Large screen size.
49  */
50 public 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 {
sNetworkStateMap.put(DetailedState.IDLE, DetailedState.DISCONNECTED)62         sNetworkStateMap.put(DetailedState.IDLE, DetailedState.DISCONNECTED);
sNetworkStateMap.put(DetailedState.SCANNING, DetailedState.SCANNING)63         sNetworkStateMap.put(DetailedState.SCANNING, DetailedState.SCANNING);
sNetworkStateMap.put(DetailedState.CONNECTING, DetailedState.CONNECTING)64         sNetworkStateMap.put(DetailedState.CONNECTING, DetailedState.CONNECTING);
sNetworkStateMap.put(DetailedState.AUTHENTICATING, DetailedState.CONNECTING)65         sNetworkStateMap.put(DetailedState.AUTHENTICATING, DetailedState.CONNECTING);
sNetworkStateMap.put(DetailedState.OBTAINING_IPADDR, DetailedState.CONNECTING)66         sNetworkStateMap.put(DetailedState.OBTAINING_IPADDR, DetailedState.CONNECTING);
sNetworkStateMap.put(DetailedState.CONNECTED, DetailedState.CONNECTED)67         sNetworkStateMap.put(DetailedState.CONNECTED, DetailedState.CONNECTED);
sNetworkStateMap.put(DetailedState.SUSPENDED, DetailedState.SUSPENDED)68         sNetworkStateMap.put(DetailedState.SUSPENDED, DetailedState.SUSPENDED);  // ?
sNetworkStateMap.put(DetailedState.DISCONNECTING, DetailedState.DISCONNECTED)69         sNetworkStateMap.put(DetailedState.DISCONNECTING, DetailedState.DISCONNECTED);
sNetworkStateMap.put(DetailedState.DISCONNECTED, DetailedState.DISCONNECTED)70         sNetworkStateMap.put(DetailedState.DISCONNECTED, DetailedState.DISCONNECTED);
sNetworkStateMap.put(DetailedState.FAILED, DetailedState.FAILED)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
onCreate(Bundle savedInstanceState)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 
initViews()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 
restoreFirstVisibilityState()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
onClick(View view)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 
hideSoftwareKeyboard()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
updateConnectionState(DetailedState originalState)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, false /* isEphemeral */));
289             }
290             break;
291         }
292         mPreviousNetworkState = state;
293     }
294 
showDisconnectedState(String stateString)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 
showConnectingState()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 
showConnectedState()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 
showDefaultTitle()343     private void showDefaultTitle() {
344         mTitleView.setText(getString(R.string.wifi_setup_title));
345     }
346 
showAddNetworkTitle()347     private void showAddNetworkTitle() {
348         mNetworkName = "";
349         mTitleView.setText(R.string.wifi_setup_title_add_network);
350     }
351 
showEditingTitle()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 
showConnectingTitle()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 
showConnectedTitle()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      */
showTopDividerWithProgressBar()398     private void showTopDividerWithProgressBar() {
399         mProgressBar.setVisibility(View.VISIBLE);
400         mTopDividerNoProgress.setVisibility(View.GONE);
401         mBottomPadding.setVisibility(View.GONE);
402     }
403 
showScanningState()404     private void showScanningState() {
405         setPaddingVisibility(View.VISIBLE);
406         mWifiSettingsFragmentLayout.setVisibility(View.GONE);
407         showScanningProgressBar();
408     }
409 
onAddNetworkButtonPressed()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      */
showConfigUi(AccessPoint selectedAccessPoint, boolean edit)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      */
initSecurityFields(View view, int accessPointSecurity)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 
onEapNetworkSelected()529     private void onEapNetworkSelected() {
530         mConnectButton.setVisibility(View.GONE);
531         mBackButton.setText(R.string.wifi_setup_back);
532     }
533 
showEditingButtonState()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
onConnectButtonPressed()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 
onBackButtonPressed()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      */
changeNextButtonState(boolean connected)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      */
onAccessPointsUpdated( PreferenceScreen preferenceScreen, Collection<AccessPoint> accessPoints)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 
refreshAccessPoints(boolean disconnectNetwork)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      */
onSupplicantStateChanged(Intent intent)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      */
onAuthenticationFailure()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
setPaddingVisibility(int visibility)713     /* package */ void setPaddingVisibility(int visibility) {
714         mTopPadding.setVisibility(visibility);
715         mContentPadding.setVisibility(visibility);
716     }
717 
showDisconnectedProgressBar()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      */
showScanningProgressBar()736     private void showScanningProgressBar() {
737         showTopDividerWithProgressBar();
738         mProgressBar.setIndeterminate(true);
739     }
740 
741     /**
742      * Shows top divider with ProgressBar, showing "connecting" state.
743      */
showConnectingProgressBar()744     private void showConnectingProgressBar() {
745         showTopDividerWithProgressBar();
746         mProgressBar.setIndeterminate(false);
747         mProgressBar.setProgress(1);
748     }
749 
showConnectedProgressBar()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      */
onSaveNetwork(WifiConfiguration config)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