1/*
2 * Copyright (C) 2014 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.setup;
18
19import android.animation.Animator;
20import android.animation.ObjectAnimator;
21import android.app.Fragment;
22import android.app.FragmentManager;
23import android.app.FragmentTransaction;
24import android.content.Context;
25import android.content.Intent;
26import android.net.ConnectivityManager;
27import android.net.NetworkInfo;
28import android.net.wifi.ScanResult;
29import android.net.wifi.WifiConfiguration;
30import android.net.wifi.WifiInfo;
31import android.net.wifi.WifiManager;
32import android.os.Bundle;
33import android.os.Handler;
34import android.os.Message;
35import android.text.TextUtils;
36import android.util.Pair;
37
38import com.android.tv.settings.R;
39import com.android.tv.settings.connectivity.AdvancedWifiOptionsFlow;
40import com.android.tv.settings.connectivity.ConnectToWifiFragment;
41import com.android.tv.settings.connectivity.FormPageDisplayer;
42import com.android.tv.settings.connectivity.TimedMessageWizardFragment;
43import com.android.tv.settings.connectivity.WifiConfigHelper;
44import com.android.tv.settings.connectivity.WifiFormPageType;
45import com.android.tv.settings.connectivity.WifiMultiPagedFormActivity;
46import com.android.tv.settings.connectivity.WifiSecurity;
47import com.android.tv.settings.connectivity.WpsConnectionActivity;
48import com.android.tv.settings.connectivity.setup.SelectFromListWizardFragment.ListItem;
49import com.android.tv.settings.form.FormPage;
50import com.android.tv.settings.form.FormPageResultListener;
51import com.android.tv.settings.util.ThemeHelper;
52import com.android.tv.settings.util.TransitionUtils;
53
54import java.util.ArrayList;
55import java.util.Collections;
56import java.util.HashMap;
57import java.util.List;
58
59/**
60 * Wi-Fi settings during initial setup for a large no-touch device
61 */
62public class WifiSetupActivity extends WifiMultiPagedFormActivity
63        implements ConnectToWifiFragment.Listener, TimedMessageWizardFragment.Listener {
64
65    private static final String TAG = "WifiSetupActivity";
66    private static final int MSG_NETWORK_REFRESH = 1;
67    private static final int NETWORK_REFRESH_TIMEOUT = 5000;
68    private static final String EXTRA_SHOW_SUMMARY = "extra_show_summary";
69    private static final String EXTRA_SHOW_SKIP_NETWORK = "extra_show_skip_network";
70    private static final String EXTRA_SHOW_WPS_AT_TOP = "extra_show_wps_at_top";
71    // If you change this constant, make sure to change the constant in setup wizard
72    private static final int RESULT_NETWORK_SKIPPED = 3;
73
74    private boolean mShowSkipNetwork;
75    private boolean mShowWpsAtTop;
76    private AdvancedWifiOptionsFlow mAdvancedWifiOptionsFlow;
77    private WifiManager mWifiManager;
78    private WifiConfiguration mConfiguration;
79    private String mConnectedNetwork;
80    private WifiSecurity mWifiSecurity;
81    private Handler mHandler;
82    private FormPageDisplayer.UserActivityListener mUserActivityListener;
83    private FormPage mChooseNetworkPage;
84    private FormPage mSsidPage;
85    private FormPage mSecurityPage;
86    private FormPage mPasswordPage;
87    private SelectFromListWizardFragment mNetworkListFragment;
88    private FormPage mConnectPage;
89    private FormPage mSuccessPage;
90
91    @Override
92    protected void onCreate(Bundle savedInstanceState) {
93        setTheme(ThemeHelper.getThemeResource(getIntent()));
94
95        mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
96        mConfiguration = new WifiConfiguration();
97
98        mHandler = new Handler() {
99            @Override
100            public void handleMessage(Message msg) {
101                if (mNetworkListFragment != null) {
102                    mNetworkListFragment.update(WifiFormPageType.CHOOSE_NETWORK.getChoices(
103                            WifiSetupActivity.this, getNetworks()));
104                    mHandler.sendEmptyMessageDelayed(MSG_NETWORK_REFRESH, NETWORK_REFRESH_TIMEOUT);
105                }
106            }
107        };
108        mUserActivityListener = new FormPageDisplayer.UserActivityListener() {
109            @Override
110            public void onUserActivity() {
111                mHandler.removeMessages(MSG_NETWORK_REFRESH);
112                mHandler.sendEmptyMessageDelayed(MSG_NETWORK_REFRESH, NETWORK_REFRESH_TIMEOUT);
113            }
114        };
115
116        boolean showSummary = getIntent().getBooleanExtra(EXTRA_SHOW_SUMMARY, false);
117        mShowSkipNetwork = getIntent().getBooleanExtra(EXTRA_SHOW_SKIP_NETWORK, false);
118        mShowWpsAtTop = getIntent().getBooleanExtra(EXTRA_SHOW_WPS_AT_TOP, false);
119
120        if (showSummary) {
121            addSummaryPage();
122        } else {
123            addPage(WifiFormPageType.CHOOSE_NETWORK);
124        }
125
126        super.onCreate(savedInstanceState);
127
128        // fade in
129        ObjectAnimator animator = TransitionUtils.createActivityFadeInAnimator(getResources(),
130                true);
131        animator.setTarget(getContentView());
132        animator.start();
133    }
134
135    @Override
136    public void finish() {
137        // fade out and really finish when we're done
138        ObjectAnimator animator = TransitionUtils.createActivityFadeOutAnimator(getResources(),
139                true);
140        animator.setTarget(getContentView());
141        animator.addListener(new Animator.AnimatorListener() {
142
143            @Override
144            public void onAnimationStart(Animator animation) {
145            }
146
147            @Override
148            public void onAnimationRepeat(Animator animation) {
149            }
150
151            @Override
152            public void onAnimationEnd(Animator animation) {
153                doFinish();
154            }
155
156            @Override
157            public void onAnimationCancel(Animator animation) {
158            }
159        });
160        animator.start();
161    }
162
163    @Override
164    public void onConnectToWifiCompleted(int reason) {
165        Bundle result = new Bundle();
166        result.putString(FormPage.DATA_KEY_SUMMARY_STRING, Integer.toString(reason));
167        onBundlePageResult(mConnectPage, result);
168    }
169
170    @Override
171    public void onTimedMessageCompleted() {
172        Bundle result = new Bundle();
173        result.putString(FormPage.DATA_KEY_SUMMARY_STRING, "");
174        onBundlePageResult(mSuccessPage, result);
175    }
176
177    @Override
178    public void addPage(WifiFormPageType formPageType) {
179        for (int i = mFormPages.size() - 1; i >= 0; i--) {
180            if (getFormPageType(mFormPages.get (i)) == formPageType) {
181                for (int j = mFormPages.size() - 1; j >= i; j--) {
182                    mFormPages.remove(j);
183                }
184                break;
185            }
186        }
187        addPage(formPageType.create());
188    }
189
190    @Override
191    protected boolean onPageComplete(WifiFormPageType formPageType, FormPage formPage) {
192
193        switch (formPageType) {
194            case KNOWN_NETWORK:
195                if (choiceChosen(formPage, R.string.wifi_connect)) {
196                    addStartPage();
197                } else if (choiceChosen(formPage, R.string.wifi_forget_network)) {
198                    ((WifiManager) getSystemService(Context.WIFI_SERVICE)).forget(
199                            mConfiguration.networkId, null);
200                    addPage(WifiFormPageType.CHOOSE_NETWORK);
201                }
202                break;
203            case CHOOSE_NETWORK:
204                if (choiceChosen(formPage, R.string.skip_network)) {
205                    WifiConfigHelper.forgetWifiNetwork(this);
206                    setResult(RESULT_NETWORK_SKIPPED);
207                } else {
208                    mHandler.removeMessages(MSG_NETWORK_REFRESH);
209                    mNetworkListFragment = null;
210                    mChooseNetworkPage = formPage;
211                    addPageBasedOnNetworkChoice(mChooseNetworkPage);
212                }
213                break;
214            case ENTER_SSID:
215                mSsidPage = formPage;
216                String ssid = formPage.getDataSummary();
217                WifiConfigHelper.setConfigSsid(mConfiguration, ssid);
218                addPage(WifiFormPageType.CHOOSE_SECURITY);
219                break;
220            case CHOOSE_SECURITY:
221                mSecurityPage = formPage;
222                if (choiceChosen(formPage, R.string.wifi_security_type_none)) {
223                    mWifiSecurity = WifiSecurity.NONE;
224                } else if (choiceChosen(formPage, R.string.wifi_security_type_wep)) {
225                    mWifiSecurity = WifiSecurity.WEP;
226                } else if (choiceChosen(formPage, R.string.wifi_security_type_wpa)) {
227                    mWifiSecurity = WifiSecurity.PSK;
228                } else if (choiceChosen(formPage, R.string.wifi_security_type_eap)) {
229                    mWifiSecurity = WifiSecurity.EAP;
230                }
231                WifiConfigHelper.setConfigKeyManagementBySecurity(mConfiguration, mWifiSecurity);
232                if (mWifiSecurity == WifiSecurity.NONE) {
233                    optionsOrConnect();
234                } else {
235                    addPage(WifiFormPageType.ENTER_PASSWORD);
236                }
237                break;
238            case ENTER_PASSWORD:
239                mPasswordPage = formPage;
240                String password = formPage.getDataSummary();
241                setWifiConfigurationPassword(mConfiguration, mWifiSecurity, password);
242                optionsOrConnect();
243                break;
244            case CONNECT:
245                switch (Integer.valueOf(formPage.getDataSummary())) {
246                    case ConnectToWifiFragment.RESULT_REJECTED_BY_AP:
247                        addPage(WifiFormPageType.CONNECT_REJECTED_BY_AP);
248                        break;
249                    case ConnectToWifiFragment.RESULT_UNKNOWN_ERROR:
250                        addPage(WifiFormPageType.CONNECT_FAILED);
251                        break;
252                    case ConnectToWifiFragment.RESULT_TIMEOUT:
253                        addPage(WifiFormPageType.CONNECT_TIMEOUT);
254                        break;
255                    case ConnectToWifiFragment.RESULT_BAD_AUTHENTICATION:
256                        WifiConfigHelper.forgetConfiguration(this, mConfiguration);
257                        addPage(WifiFormPageType.CONNECT_AUTHENTICATION_FAILURE);
258                        break;
259                    case ConnectToWifiFragment.RESULT_SUCCESS:
260                        WifiConfigHelper.saveConfiguration(this, mConfiguration);
261                        addPage(WifiFormPageType.SUCCESS);
262                        break;
263                    default:
264                        break;
265                }
266                break;
267            case CONNECT_TIMEOUT:
268                mAdvancedWifiOptionsFlow = new AdvancedWifiOptionsFlow(this, this, true, null);
269                // fall through
270            case CONNECT_REJECTED_BY_AP:
271                if (choiceChosen(formPage, R.string.wifi_action_try_again)) {
272                    optionsOrConnect();
273                } else {
274                    mSsidPage = null;
275                    mSecurityPage = null;
276                    mPasswordPage = null;
277                    addPage(WifiFormPageType.CHOOSE_NETWORK);
278                }
279                break;
280            case CONNECT_FAILED:
281                // fall through
282            case CONNECT_AUTHENTICATION_FAILURE:
283                if (choiceChosen(formPage, R.string.wifi_action_try_again)) {
284                    addPageBasedOnNetworkChoice(mChooseNetworkPage);
285                } else {
286                    mSsidPage = null;
287                    mSecurityPage = null;
288                    mPasswordPage = null;
289                    addPage(WifiFormPageType.CHOOSE_NETWORK);
290                }
291                break;
292            case SUMMARY_CONNECTED_WIFI:
293                if (choiceChosen(formPage, R.string.wifi_action_dont_change_network)) {
294                    setResult(RESULT_OK);
295                    finish();
296                } else if (choiceChosen(formPage, R.string.wifi_action_change_network)) {
297                    addPage(WifiFormPageType.CHOOSE_NETWORK);
298                }
299                break;
300            case SUMMARY_CONNECTED_NON_WIFI:
301                setResult(RESULT_OK);
302                finish();
303                break;
304            case SUMMARY_NOT_CONNECTED:
305                addPage(WifiFormPageType.CHOOSE_NETWORK);
306                break;
307            case SUCCESS:
308                setResult(RESULT_OK);
309                break;
310            case WPS:
311                setResult(RESULT_OK);
312                break;
313            default:
314                if (mAdvancedWifiOptionsFlow != null) {
315                    switch (mAdvancedWifiOptionsFlow.handlePageComplete(formPageType, formPage)) {
316                        case AdvancedWifiOptionsFlow.RESULT_ALL_PAGES_COMPLETE:
317                            connect();
318                            break;
319                        case AdvancedWifiOptionsFlow.RESULT_UNKNOWN_PAGE:
320                        case AdvancedWifiOptionsFlow.RESULT_PAGE_HANDLED:
321                        default:
322                            break;
323                    }
324                }
325                break;
326        }
327        return true;
328    }
329
330    @Override
331    protected void displayPage(FormPage formPage, FormPageResultListener listener,
332            boolean forward) {
333        WifiFormPageType formPageType = getFormPageType(formPage);
334
335        if (formPageType == WifiFormPageType.CONNECT) {
336            mConnectPage = formPage;
337            Fragment fragment = ConnectToWifiFragment.newInstance(
338                    getString(formPageType.getTitleResourceId(), mConfiguration.getPrintableSsid()),
339                    true, mConfiguration);
340            displayFragment(fragment, forward);
341        } else if (formPageType == WifiFormPageType.SUCCESS) {
342            mSuccessPage = formPage;
343            Fragment fragment = TimedMessageWizardFragment.newInstance(
344                    getString(formPageType.getTitleResourceId()));
345            displayFragment(fragment, forward);
346        } else if (formPageType == WifiFormPageType.WPS) {
347            displayFragment(MessageWizardFragment.newInstance("", true), forward);
348        } else {
349            Fragment fragment = displayPage(formPageType, mConfiguration.getPrintableSsid(),
350                    formPageType == WifiFormPageType.SUMMARY_CONNECTED_WIFI ? mConnectedNetwork
351                            : null,
352                    formPageType == WifiFormPageType.CHOOSE_NETWORK ? getNetworks() : null,
353                    getLastPage(formPageType),
354                    formPageType == WifiFormPageType.CHOOSE_NETWORK ? mUserActivityListener : null,
355                    formPageType != WifiFormPageType.SUCCESS, formPage, listener, forward,
356                            (mAdvancedWifiOptionsFlow != null) ? mAdvancedWifiOptionsFlow
357                            .isEmptyTextAllowed(formPageType) : false);
358            if (formPageType == WifiFormPageType.CHOOSE_NETWORK) {
359                mNetworkListFragment = (SelectFromListWizardFragment) fragment;
360                mHandler.sendEmptyMessageDelayed(MSG_NETWORK_REFRESH, NETWORK_REFRESH_TIMEOUT);
361            }
362        }
363    }
364
365    @Override
366    protected void undisplayCurrentPage() {
367        FragmentManager fragMan = getFragmentManager();
368        Fragment target = fragMan.findFragmentById(R.id.content);
369        FragmentTransaction transaction = fragMan.beginTransaction();
370        transaction.remove(target);
371        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE);
372        transaction.commit();
373    }
374
375    private void doFinish() {
376        super.finish();
377    }
378
379    private void connect() {
380        if (!WifiConfigHelper.isNetworkSaved(mConfiguration) &&
381            mAdvancedWifiOptionsFlow != null) {
382            mAdvancedWifiOptionsFlow.updateConfiguration(mConfiguration);
383        }
384        addPage(WifiFormPageType.CONNECT);
385    }
386
387    private void optionsOrConnect() {
388        if (mAdvancedWifiOptionsFlow != null) {
389            addPage(mAdvancedWifiOptionsFlow.getInitialPage());
390        } else {
391            connect();
392        }
393    }
394
395    private FormPage getLastPage(WifiFormPageType formPageType) {
396        switch (formPageType) {
397            case CHOOSE_NETWORK:
398                return mChooseNetworkPage;
399            case CHOOSE_SECURITY:
400                return mSecurityPage;
401            case ENTER_PASSWORD:
402                return mPasswordPage;
403            case ENTER_SSID:
404                return mSsidPage;
405            default:
406                return (mAdvancedWifiOptionsFlow != null) ? mAdvancedWifiOptionsFlow
407                        .getPreviousPage(formPageType)
408                        : null;
409        }
410    }
411
412    private ArrayList<ListItem> getNetworks() {
413        ArrayList<SelectFromListWizardFragment.ListItem> listItems = new ArrayList<
414                SelectFromListWizardFragment.ListItem>();
415        final List<ScanResult> results = mWifiManager.getScanResults();
416        final HashMap<Pair<String, WifiSecurity>, ScanResult> consolidatedScanResults = new HashMap<
417                Pair<String, WifiSecurity>, ScanResult>();
418        for (ScanResult result : results) {
419            if (TextUtils.isEmpty(result.SSID)) {
420                continue;
421            }
422
423            Pair<String, WifiSecurity> key = new Pair<String, WifiSecurity>(
424                    result.SSID, WifiSecurity.getSecurity(result));
425            ScanResult existing = consolidatedScanResults.get(key);
426            if (existing == null || existing.level < result.level) {
427                consolidatedScanResults.put(key, result);
428            }
429        }
430        for (ScanResult result : consolidatedScanResults.values()) {
431            listItems.add(new SelectFromListWizardFragment.ListItem(result));
432        }
433        Collections.sort(listItems, new SelectFromListWizardFragment.ListItemComparator());
434
435        SelectFromListWizardFragment.ListItem wpsItem = new SelectFromListWizardFragment.ListItem(
436                getString(R.string.wps_network).toUpperCase(), R.drawable.ic_setup_wps);
437        if (mShowWpsAtTop) {
438            listItems.add(0, wpsItem);
439        } else {
440            listItems.add(wpsItem);
441        }
442
443        if (mShowSkipNetwork) {
444            listItems.add(new SelectFromListWizardFragment.ListItem(
445                   getString(R.string.skip_network).toUpperCase(), 0));
446        }
447
448        return listItems;
449    }
450
451    private void addPageBasedOnNetworkChoice(FormPage chooseNetworkPage) {
452        if (choiceChosen(chooseNetworkPage, R.string.other_network)) {
453            mConfiguration.hiddenSSID = true;
454            addPage(WifiFormPageType.ENTER_SSID);
455        } else if (choiceChosen(chooseNetworkPage, R.string.wps_network)) {
456            addPage(WifiFormPageType.WPS, new Intent(this, WpsConnectionActivity.class)
457                    .putExtras(getIntent().getExtras()));
458        } else {
459            ScanResult scanResult = getListItem(chooseNetworkPage).getScanResult();
460            mConfiguration = WifiConfigHelper.getConfigurationForNetwork(this, scanResult);
461            mWifiSecurity = WifiSecurity.getSecurity(scanResult);
462
463            if (WifiConfigHelper.isNetworkSaved(mConfiguration)) {
464                addPage(WifiFormPageType.KNOWN_NETWORK);
465            } else {
466                addStartPage();
467            }
468        }
469    }
470
471    private void addStartPage() {
472        if ((mWifiSecurity == WifiSecurity.WEP && TextUtils.isEmpty(mConfiguration.wepKeys[0]))
473                || (!mWifiSecurity.isOpen() && TextUtils.isEmpty(mConfiguration.preSharedKey))) {
474            addPage(WifiFormPageType.ENTER_PASSWORD);
475        } else {
476            connect();
477        }
478    }
479
480    private void addSummaryPage() {
481        ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(
482                Context.CONNECTIVITY_SERVICE);
483        NetworkInfo currentConnection = connectivityManager.getActiveNetworkInfo();
484        boolean isConnected = (currentConnection != null) && currentConnection.isConnected();
485        if (isConnected) {
486            if (currentConnection.getType() == ConnectivityManager.TYPE_WIFI) {
487                WifiInfo currentWifiConnection = mWifiManager.getConnectionInfo();
488                mConnectedNetwork = WifiInfo.removeDoubleQuotes(
489                        currentWifiConnection.getSSID());
490                if (mConnectedNetwork == null) {
491                    mConnectedNetwork = getString(R.string.wifi_summary_unknown_network);
492                }
493                addPage(WifiFormPageType.SUMMARY_CONNECTED_WIFI);
494            } else {
495                addPage(WifiFormPageType.SUMMARY_CONNECTED_NON_WIFI);
496            }
497        } else {
498            addPage(WifiFormPageType.SUMMARY_NOT_CONNECTED);
499        }
500    }
501}
502