CaptivePortalLoginActivity.java revision 6a776c8317138cf4a3013addc1bd1a462f6dc1eb
1869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen/*
2869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * Copyright (C) 2014 The Android Open Source Project
3869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen *
4869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * Licensed under the Apache License, Version 2.0 (the "License");
5869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * you may not use this file except in compliance with the License.
6869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * You may obtain a copy of the License at
7869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen *
8869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen *      http://www.apache.org/licenses/LICENSE-2.0
9869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen *
10869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * Unless required by applicable law or agreed to in writing, software
11869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * distributed under the License is distributed on an "AS IS" BASIS,
12869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * See the License for the specific language governing permissions and
14869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen * limitations under the License.
15869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen */
16869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
17869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenpackage com.android.captiveportallogin;
18869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
19869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.app.Activity;
2088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport android.app.LoadedApk;
2188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport android.content.Context;
22869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.content.Intent;
23869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.graphics.Bitmap;
2449e3edff5156f471819e4ea2a88994bca70bd870Paul Jensenimport android.net.CaptivePortal;
25869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.net.ConnectivityManager;
268df099df1516d23c113be3121635dcd34984a4a0Paul Jensenimport android.net.ConnectivityManager.NetworkCallback;
27869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.net.Network;
288df099df1516d23c113be3121635dcd34984a4a0Paul Jensenimport android.net.NetworkCapabilities;
298df099df1516d23c113be3121635dcd34984a4a0Paul Jensenimport android.net.NetworkRequest;
3088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport android.net.Proxy;
3188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport android.net.Uri;
32fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensenimport android.net.http.SslError;
33869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.os.Bundle;
34869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.provider.Settings;
3588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport android.util.ArrayMap;
3688eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport android.util.Log;
3765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensenimport android.util.TypedValue;
38869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.view.Menu;
39869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.view.MenuItem;
40fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensenimport android.webkit.SslErrorHandler;
41869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.webkit.WebChromeClient;
42869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.webkit.WebSettings;
43869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.webkit.WebView;
44869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.webkit.WebViewClient;
458f333f19222ac9415152e31f10e0df2b571b0b77Paul Jensenimport android.widget.ProgressBar;
465344a4abdf239a19485a9c858b6cc3be96002eacPaul Jensenimport android.widget.TextView;
47869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
48869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport java.io.IOException;
49869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport java.net.HttpURLConnection;
50869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport java.net.MalformedURLException;
51869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport java.net.URL;
52869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport java.lang.InterruptedException;
5388eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport java.lang.reflect.Field;
5488eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport java.lang.reflect.Method;
5565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensenimport java.util.Random;
56869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
57869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenpublic class CaptivePortalLoginActivity extends Activity {
5888eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen    private static final String TAG = "CaptivePortalLogin";
59869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    private static final int SOCKET_TIMEOUT_MS = 10000;
60869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
6125a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen    private enum Result { DISMISSED, UNWANTED, WANTED_AS_IS };
62869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
63869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    private URL mURL;
6425a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen    private Network mNetwork;
6549e3edff5156f471819e4ea2a88994bca70bd870Paul Jensen    private CaptivePortal mCaptivePortal;
668df099df1516d23c113be3121635dcd34984a4a0Paul Jensen    private NetworkCallback mNetworkCallback;
6725a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen    private ConnectivityManager mCm;
6865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen    private boolean mLaunchBrowser = false;
69e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen    private MyWebViewClient mWebViewClient;
70869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
71869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    @Override
72869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    protected void onCreate(Bundle savedInstanceState) {
73869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        super.onCreate(savedInstanceState);
7425a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen        mCm = ConnectivityManager.from(this);
7552eb29f0822f129f2b14bacec23dd492f2260ac0Jan Nordqvist        String url = getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL);
76b7c2487c8b5fbd154643b8ddade8d88507cae137Udam Saini        if (url == null) url = mCm.getCaptivePortalServerUrl();
77869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        try {
78b7c2487c8b5fbd154643b8ddade8d88507cae137Udam Saini            mURL = new URL(url);
7925a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen        } catch (MalformedURLException e) {
8071b645fe9cb8106dfcbf025a3fd7f58698c051bbPaul Jensen            // System misconfigured, bail out in a way that at least provides network access.
81b7c2487c8b5fbd154643b8ddade8d88507cae137Udam Saini            Log.e(TAG, "Invalid captive portal URL, url=" + url);
8225a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen            done(Result.WANTED_AS_IS);
83869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        }
8425a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen        mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
8549e3edff5156f471819e4ea2a88994bca70bd870Paul Jensen        mCaptivePortal = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL);
86869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
87e0bef71662d81caaaa0d7214fb0bef5d39996a69Paul Jensen        // Also initializes proxy system properties.
8825a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen        mCm.bindProcessToNetwork(mNetwork);
8988eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen
9088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen        // Proxy system properties must be initialized before setContentView is called because
9188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen        // setContentView initializes the WebView logic which in turn reads the system properties.
9288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen        setContentView(R.layout.activity_captive_portal_login);
9388eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen
9488eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen        getActionBar().setDisplayShowHomeEnabled(false);
9588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen
968df099df1516d23c113be3121635dcd34984a4a0Paul Jensen        // Exit app if Network disappears.
9725a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen        final NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(mNetwork);
988df099df1516d23c113be3121635dcd34984a4a0Paul Jensen        if (networkCapabilities == null) {
996a776c8317138cf4a3013addc1bd1a462f6dc1ebPaul Jensen            finishAndRemoveTask();
1008df099df1516d23c113be3121635dcd34984a4a0Paul Jensen            return;
1018df099df1516d23c113be3121635dcd34984a4a0Paul Jensen        }
1028df099df1516d23c113be3121635dcd34984a4a0Paul Jensen        mNetworkCallback = new NetworkCallback() {
1038df099df1516d23c113be3121635dcd34984a4a0Paul Jensen            @Override
1048df099df1516d23c113be3121635dcd34984a4a0Paul Jensen            public void onLost(Network lostNetwork) {
10525a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen                if (mNetwork.equals(lostNetwork)) done(Result.UNWANTED);
1068df099df1516d23c113be3121635dcd34984a4a0Paul Jensen            }
1078df099df1516d23c113be3121635dcd34984a4a0Paul Jensen        };
1088df099df1516d23c113be3121635dcd34984a4a0Paul Jensen        final NetworkRequest.Builder builder = new NetworkRequest.Builder();
1098df099df1516d23c113be3121635dcd34984a4a0Paul Jensen        for (int transportType : networkCapabilities.getTransportTypes()) {
1108df099df1516d23c113be3121635dcd34984a4a0Paul Jensen            builder.addTransportType(transportType);
1118df099df1516d23c113be3121635dcd34984a4a0Paul Jensen        }
11225a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen        mCm.registerNetworkCallback(builder.build(), mNetworkCallback);
113869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
11488eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen        final WebView myWebView = (WebView) findViewById(R.id.webview);
11588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen        myWebView.clearCache(true);
116869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        WebSettings webSettings = myWebView.getSettings();
117869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        webSettings.setJavaScriptEnabled(true);
118e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen        mWebViewClient = new MyWebViewClient();
119e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen        myWebView.setWebViewClient(mWebViewClient);
120869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        myWebView.setWebChromeClient(new MyWebChromeClient());
12188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen        // Start initial page load so WebView finishes loading proxy settings.
12288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen        // Actual load of mUrl is initiated by MyWebViewClient.
12388eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen        myWebView.loadData("", "text/html", null);
12488eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen    }
12588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen
12688eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen    // Find WebView's proxy BroadcastReceiver and prompt it to read proxy system properties.
12788eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen    private void setWebViewProxy() {
12888eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen        LoadedApk loadedApk = getApplication().mLoadedApk;
12988eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen        try {
13088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen            Field receiversField = LoadedApk.class.getDeclaredField("mReceivers");
13188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen            receiversField.setAccessible(true);
13288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen            ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk);
13388eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen            for (Object receiverMap : receivers.values()) {
13488eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                for (Object rec : ((ArrayMap) receiverMap).keySet()) {
13588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                    Class clazz = rec.getClass();
13688eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                    if (clazz.getName().contains("ProxyChangeListener")) {
13788eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                        Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class,
13888eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                                Intent.class);
13988eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                        Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
14088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                        onReceiveMethod.invoke(rec, getApplicationContext(), intent);
14188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                        Log.v(TAG, "Prompting WebView proxy reload.");
14288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                    }
14388eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                }
14488eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen            }
14588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen        } catch (Exception e) {
14688eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen            Log.e(TAG, "Exception while setting WebView proxy: " + e);
14788eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen        }
148869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    }
149869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
15025a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen    private void done(Result result) {
15171b645fe9cb8106dfcbf025a3fd7f58698c051bbPaul Jensen        if (mNetworkCallback != null) {
15225a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen            mCm.unregisterNetworkCallback(mNetworkCallback);
153868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen            mNetworkCallback = null;
15425a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen        }
15525a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen        switch (result) {
15625a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen            case DISMISSED:
15749e3edff5156f471819e4ea2a88994bca70bd870Paul Jensen                mCaptivePortal.reportCaptivePortalDismissed();
15825a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen                break;
15925a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen            case UNWANTED:
16049e3edff5156f471819e4ea2a88994bca70bd870Paul Jensen                mCaptivePortal.ignoreNetwork();
16125a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen                break;
16225a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen            case WANTED_AS_IS:
16349e3edff5156f471819e4ea2a88994bca70bd870Paul Jensen                mCaptivePortal.useNetwork();
16425a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen                break;
16571b645fe9cb8106dfcbf025a3fd7f58698c051bbPaul Jensen        }
1666a776c8317138cf4a3013addc1bd1a462f6dc1ebPaul Jensen        finishAndRemoveTask();
167869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    }
168869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
169869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    @Override
170869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    public boolean onCreateOptionsMenu(Menu menu) {
171869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        getMenuInflater().inflate(R.menu.captive_portal_login, menu);
172869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        return true;
173869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    }
174869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
175869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    @Override
176b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen    public void onBackPressed() {
177b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen        WebView myWebView = (WebView) findViewById(R.id.webview);
178e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen        if (myWebView.canGoBack() && mWebViewClient.allowBack()) {
179b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen            myWebView.goBack();
180b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen        } else {
181b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen            super.onBackPressed();
182b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen        }
183b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen    }
184b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen
185b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen    @Override
186869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    public boolean onOptionsItemSelected(MenuItem item) {
187869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        int id = item.getItemId();
188869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        if (id == R.id.action_use_network) {
18925a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen            done(Result.WANTED_AS_IS);
190869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen            return true;
191869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        }
192869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        if (id == R.id.action_do_not_use_network) {
19325a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen            done(Result.UNWANTED);
194869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen            return true;
195869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        }
196869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        return super.onOptionsItemSelected(item);
197869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    }
198869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
199868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen    @Override
200868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen    public void onDestroy() {
201868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen        super.onDestroy();
202868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen
203868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen        if (mNetworkCallback != null) {
204868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen            mCm.unregisterNetworkCallback(mNetworkCallback);
205868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen            mNetworkCallback = null;
206868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen        }
20765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        if (mLaunchBrowser) {
20865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            // Give time for this network to become default. After 500ms just proceed.
20965636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            for (int i = 0; i < 5; i++) {
21065636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                // TODO: This misses when mNetwork underlies a VPN.
21165636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                if (mNetwork.equals(mCm.getActiveNetwork())) break;
21265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                try {
21365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                    Thread.sleep(100);
21465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                } catch (InterruptedException e) {
21565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                }
21665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            }
21765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(mURL.toString())));
21865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        }
219868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen    }
220868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen
221869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    private void testForCaptivePortal() {
222869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        new Thread(new Runnable() {
223869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen            public void run() {
224869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                // Give time for captive portal to open.
225869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                try {
226869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                    Thread.sleep(1000);
227869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                } catch (InterruptedException e) {
228869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                }
229869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                HttpURLConnection urlConnection = null;
230869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                int httpResponseCode = 500;
231869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                try {
232869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                    urlConnection = (HttpURLConnection) mURL.openConnection();
233869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                    urlConnection.setInstanceFollowRedirects(false);
234869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                    urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
235869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                    urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
236869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                    urlConnection.setUseCaches(false);
237869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                    urlConnection.getInputStream();
238869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                    httpResponseCode = urlConnection.getResponseCode();
239869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                } catch (IOException e) {
240869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                } finally {
241869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                    if (urlConnection != null) urlConnection.disconnect();
242869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                }
243869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                if (httpResponseCode == 204) {
24425a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen                    done(Result.DISMISSED);
245869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                }
246869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen            }
247869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        }).start();
248869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    }
249869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
250869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    private class MyWebViewClient extends WebViewClient {
2515344a4abdf239a19485a9c858b6cc3be96002eacPaul Jensen        private static final String INTERNAL_ASSETS = "file:///android_asset/";
25265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        private final String mBrowserBailOutToken = Long.toString(new Random().nextLong());
25365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        // How many Android device-independent-pixels per scaled-pixel
25465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        // dp/sp = (px/sp) / (px/dp) = (1/sp) / (1/dp)
25565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        private final float mDpPerSp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 1,
25665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                    getResources().getDisplayMetrics()) /
25765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                    TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
25865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                    getResources().getDisplayMetrics());
259e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen        private int mPagesLoaded;
260e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen
261e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen        // If we haven't finished cleaning up the history, don't allow going back.
262e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen        public boolean allowBack() {
263e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen            return mPagesLoaded > 1;
264e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen        }
26588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen
266869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        @Override
267869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        public void onPageStarted(WebView view, String url, Bitmap favicon) {
26865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            if (url.contains(mBrowserBailOutToken)) {
26965636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                mLaunchBrowser = true;
27065636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                done(Result.WANTED_AS_IS);
27165636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                return;
27265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            }
273e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen            // The first page load is used only to cause the WebView to
274e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen            // fetch the proxy settings.  Don't update the URL bar, and
275e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen            // don't check if the captive portal is still there.
276e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen            if (mPagesLoaded == 0) return;
277e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen            // For internally generated pages, leave URL bar listing prior URL as this is the URL
278e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen            // the page refers to.
279e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen            if (!url.startsWith(INTERNAL_ASSETS)) {
280e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen                final TextView myUrlBar = (TextView) findViewById(R.id.url_bar);
281e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen                myUrlBar.setText(url);
282e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen            }
283869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen            testForCaptivePortal();
284869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        }
285869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
286869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        @Override
287869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        public void onPageFinished(WebView view, String url) {
288e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen            mPagesLoaded++;
289e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen            if (mPagesLoaded == 1) {
29088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                // Now that WebView has loaded at least one page we know it has read in the proxy
29188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                // settings.  Now prompt the WebView read the Network-specific proxy settings.
29288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                setWebViewProxy();
29388eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                // Load the real page.
29488eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                view.loadUrl(mURL.toString());
29588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                return;
296e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen            } else if (mPagesLoaded == 2) {
297e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen                // Prevent going back to empty first page.
298e836b6847af968460f36a4e6649b8cb6f6da18dbPaul Jensen                view.clearHistory();
2995344a4abdf239a19485a9c858b6cc3be96002eacPaul Jensen            }
300869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen            testForCaptivePortal();
301869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        }
302fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen
30365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        // Convert Android device-independent-pixels (dp) to HTML size.
30465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        private String dp(int dp) {
30565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            // HTML px's are scaled just like dp's, so just add "px" suffix.
30665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            return Integer.toString(dp) + "px";
30765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        }
30865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen
30965636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        // Convert Android scaled-pixels (sp) to HTML size.
31065636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        private String sp(int sp) {
31165636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            // Convert sp to dp's.
31265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            float dp = sp * mDpPerSp;
31365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            // Apply a scale factor to make things look right.
31465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            dp *= 1.3;
31565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            // Convert dp's to HTML size.
31665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            return dp((int)dp);
31765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        }
31865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen
319fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen        // A web page consisting of a large broken lock icon to indicate SSL failure.
32065636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        private final String SSL_ERROR_HTML = "<html><head><style>" +
32165636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "body { margin-left:" + dp(48) + "; margin-right:" + dp(48) + "; " +
32265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                        "margin-top:" + dp(96) + "; background-color:#fafafa; }" +
32365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "img { width:" + dp(48) + "; height:" + dp(48) + "; }" +
32465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "div.warn { font-size:" + sp(16) + "; margin-top:" + dp(16) + "; " +
32565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "           opacity:0.87; line-height:1.28; }" +
32665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "div.example { font-size:" + sp(14) + "; margin-top:" + dp(16) + "; " +
32765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "              opacity:0.54; line-height:1.21905; }" +
32865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "a { font-size:" + sp(14) + "; text-decoration:none; text-transform:uppercase; " +
32965636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "    margin-top:" + dp(24) + "; display:inline-block; color:#4285F4; " +
33065636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "    height:" + dp(48) + "; font-weight:bold; }" +
33165636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "</style></head><body><p><img src=quantum_ic_warning_amber_96.png><br>" +
33265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "<div class=warn>%s</div>" +
33365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "<div class=example>%s</div>" +
33465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "<a href=%s>%s</a></body></html>";
335fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen
336fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen        @Override
337fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
33841ff524a6325feb8dc9d3d64545c5f2aeb964f34Paul Jensen            Log.w(TAG, "SSL error (error: " + error.getPrimaryError() + " host: " +
33941ff524a6325feb8dc9d3d64545c5f2aeb964f34Paul Jensen                    // Only show host to avoid leaking private info.
34041ff524a6325feb8dc9d3d64545c5f2aeb964f34Paul Jensen                    Uri.parse(error.getUrl()).getHost() + " certificate: " +
34141ff524a6325feb8dc9d3d64545c5f2aeb964f34Paul Jensen                    error.getCertificate() + "); displaying SSL warning.");
34265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            final String html = String.format(SSL_ERROR_HTML, getString(R.string.ssl_error_warning),
34365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                    getString(R.string.ssl_error_example), mBrowserBailOutToken,
34465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                    getString(R.string.ssl_error_continue));
34565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            view.loadDataWithBaseURL(INTERNAL_ASSETS, html, "text/HTML", "UTF-8", null);
346fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen        }
347fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen
348fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen        @Override
349fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen        public boolean shouldOverrideUrlLoading (WebView view, String url) {
350fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen            if (url.startsWith("tel:")) {
351fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen                startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(url)));
352fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen                return true;
353fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen            }
354fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen            return false;
355fd54da9b7ccf8218bb99cbbe540fd71ea411508bPaul Jensen        }
356869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    }
357869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
358869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    private class MyWebChromeClient extends WebChromeClient {
359869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        @Override
360869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        public void onProgressChanged(WebView view, int newProgress) {
3615344a4abdf239a19485a9c858b6cc3be96002eacPaul Jensen            final ProgressBar myProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
3628f333f19222ac9415152e31f10e0df2b571b0b77Paul Jensen            myProgressBar.setProgress(newProgress);
363869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        }
364869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    }
365869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen}
366