CaptivePortalLoginActivity.java revision 65636fb23c86e546dc4ce584481fa58bf72e4945
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;
24869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.net.ConnectivityManager;
258df099df1516d23c113be3121635dcd34984a4a0Paul Jensenimport android.net.ConnectivityManager.NetworkCallback;
26869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.net.Network;
278df099df1516d23c113be3121635dcd34984a4a0Paul Jensenimport android.net.NetworkCapabilities;
288df099df1516d23c113be3121635dcd34984a4a0Paul Jensenimport android.net.NetworkRequest;
2988eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport android.net.Proxy;
3088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport android.net.Uri;
31fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensenimport android.net.http.SslError;
32869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.os.Bundle;
33869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.provider.Settings;
3488eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport android.util.ArrayMap;
3588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport android.util.Log;
3665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensenimport android.util.TypedValue;
37869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.view.Menu;
38869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.view.MenuItem;
39fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensenimport android.webkit.SslErrorHandler;
40869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.webkit.WebChromeClient;
41869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.webkit.WebSettings;
42869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.webkit.WebView;
43869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport android.webkit.WebViewClient;
448f333f19222ac9415152e31f10e0df2b571b0b77Paul Jensenimport android.widget.ProgressBar;
455344a4abdf239a19485a9c858b6cc3be96002eacPaul Jensenimport android.widget.TextView;
46869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
47869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport java.io.IOException;
48869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport java.net.HttpURLConnection;
49869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport java.net.MalformedURLException;
50869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport java.net.URL;
51869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenimport java.lang.InterruptedException;
5288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport java.lang.reflect.Field;
5388eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensenimport java.lang.reflect.Method;
5465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensenimport java.util.Random;
55869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
56869868be653cb8eedd338e8347dfee1520d38cecPaul Jensenpublic class CaptivePortalLoginActivity extends Activity {
5788eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen    private static final String TAG = "CaptivePortalLogin";
58cd29cb66f92b008e8547f70b30223ce8dbc1fb86Lorenzo Colitti    private static final String DEFAULT_SERVER = "connectivitycheck.android.com";
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;
6571b645fe9cb8106dfcbf025a3fd7f58698c051bbPaul Jensen    private String mResponseToken;
668df099df1516d23c113be3121635dcd34984a4a0Paul Jensen    private NetworkCallback mNetworkCallback;
6725a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen    private ConnectivityManager mCm;
6865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen    private boolean mLaunchBrowser = false;
69869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
70869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    @Override
71869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    protected void onCreate(Bundle savedInstanceState) {
72869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        super.onCreate(savedInstanceState);
73869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
74869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        String server = Settings.Global.getString(getContentResolver(), "captive_portal_server");
75869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        if (server == null) server = DEFAULT_SERVER;
7625a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen        mCm = ConnectivityManager.from(this);
77869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        try {
7871b645fe9cb8106dfcbf025a3fd7f58698c051bbPaul Jensen            mURL = new URL("http", server, "/generate_204");
7925a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen        } catch (MalformedURLException e) {
8071b645fe9cb8106dfcbf025a3fd7f58698c051bbPaul Jensen            // System misconfigured, bail out in a way that at least provides network access.
8125a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen            Log.e(TAG, "Invalid captive portal URL, server=" + server);
8225a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen            done(Result.WANTED_AS_IS);
83869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        }
8425a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen        mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
8525a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen        mResponseToken = getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_TOKEN);
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) {
998df099df1516d23c113be3121635dcd34984a4a0Paul Jensen            finish();
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);
118869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        myWebView.setWebViewClient(new MyWebViewClient());
119869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        myWebView.setWebChromeClient(new MyWebChromeClient());
12088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen        // Start initial page load so WebView finishes loading proxy settings.
12188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen        // Actual load of mUrl is initiated by MyWebViewClient.
12288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen        myWebView.loadData("", "text/html", null);
12388eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen    }
12488eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen
12588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen    // Find WebView's proxy BroadcastReceiver and prompt it to read proxy system properties.
12688eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen    private void setWebViewProxy() {
12788eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen        LoadedApk loadedApk = getApplication().mLoadedApk;
12888eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen        try {
12988eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen            Field receiversField = LoadedApk.class.getDeclaredField("mReceivers");
13088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen            receiversField.setAccessible(true);
13188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen            ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk);
13288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen            for (Object receiverMap : receivers.values()) {
13388eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                for (Object rec : ((ArrayMap) receiverMap).keySet()) {
13488eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                    Class clazz = rec.getClass();
13588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                    if (clazz.getName().contains("ProxyChangeListener")) {
13688eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                        Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class,
13788eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                                Intent.class);
13888eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                        Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
13988eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                        onReceiveMethod.invoke(rec, getApplicationContext(), intent);
14088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                        Log.v(TAG, "Prompting WebView proxy reload.");
14188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                    }
14288eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                }
14388eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen            }
14488eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen        } catch (Exception e) {
14588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen            Log.e(TAG, "Exception while setting WebView proxy: " + e);
14688eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen        }
147869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    }
148869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
14925a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen    private void done(Result result) {
15071b645fe9cb8106dfcbf025a3fd7f58698c051bbPaul Jensen        if (mNetworkCallback != null) {
15125a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen            mCm.unregisterNetworkCallback(mNetworkCallback);
152868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen            mNetworkCallback = null;
15325a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen        }
15425a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen        switch (result) {
15525a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen            case DISMISSED:
15625a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen                mCm.reportCaptivePortalDismissed(mNetwork, mResponseToken);
15725a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen                break;
15825a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen            case UNWANTED:
15925a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen                mCm.ignoreNetworkWithCaptivePortal(mNetwork, mResponseToken);
16025a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen                break;
16125a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen            case WANTED_AS_IS:
16225a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen                mCm.useNetworkWithCaptivePortal(mNetwork, mResponseToken);
16325a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen                break;
16471b645fe9cb8106dfcbf025a3fd7f58698c051bbPaul Jensen        }
165869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        finish();
166869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    }
167869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
168869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    @Override
169869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    public boolean onCreateOptionsMenu(Menu menu) {
170869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        getMenuInflater().inflate(R.menu.captive_portal_login, menu);
171869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        return true;
172869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    }
173869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
174869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    @Override
175b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen    public void onBackPressed() {
176b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen        WebView myWebView = (WebView) findViewById(R.id.webview);
177b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen        if (myWebView.canGoBack()) {
178b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen            myWebView.goBack();
179b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen        } else {
180b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen            super.onBackPressed();
181b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen        }
182b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen    }
183b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen
184b6ea9ee6fe6fc205f4f8be593ca993d594e8d504Paul Jensen    @Override
185869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    public boolean onOptionsItemSelected(MenuItem item) {
186869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        int id = item.getItemId();
187869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        if (id == R.id.action_use_network) {
18825a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen            done(Result.WANTED_AS_IS);
189869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen            return true;
190869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        }
191869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        if (id == R.id.action_do_not_use_network) {
19225a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen            done(Result.UNWANTED);
193869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen            return true;
194869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        }
195869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        return super.onOptionsItemSelected(item);
196869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    }
197869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
198868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen    @Override
199868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen    public void onDestroy() {
200868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen        super.onDestroy();
201868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen
202868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen        if (mNetworkCallback != null) {
203868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen            mCm.unregisterNetworkCallback(mNetworkCallback);
204868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen            mNetworkCallback = null;
205868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen        }
20665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        if (mLaunchBrowser) {
20765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            // Give time for this network to become default. After 500ms just proceed.
20865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            for (int i = 0; i < 5; i++) {
20965636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                // TODO: This misses when mNetwork underlies a VPN.
21065636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                if (mNetwork.equals(mCm.getActiveNetwork())) break;
21165636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                try {
21265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                    Thread.sleep(100);
21365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                } catch (InterruptedException e) {
21465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                }
21565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            }
21665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(mURL.toString())));
21765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        }
218868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen    }
219868f6243bc6021465938a7b764bef8fd001cb39cPaul Jensen
220869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    private void testForCaptivePortal() {
221869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        new Thread(new Runnable() {
222869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen            public void run() {
223869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                // Give time for captive portal to open.
224869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                try {
225869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                    Thread.sleep(1000);
226869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                } catch (InterruptedException e) {
227869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                }
228869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                HttpURLConnection urlConnection = null;
229869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                int httpResponseCode = 500;
230869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                try {
231869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                    urlConnection = (HttpURLConnection) mURL.openConnection();
232869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                    urlConnection.setInstanceFollowRedirects(false);
233869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                    urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
234869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                    urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
235869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                    urlConnection.setUseCaches(false);
236869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                    urlConnection.getInputStream();
237869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                    httpResponseCode = urlConnection.getResponseCode();
238869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                } catch (IOException e) {
239869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                } finally {
240869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                    if (urlConnection != null) urlConnection.disconnect();
241869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                }
242869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                if (httpResponseCode == 204) {
24325a217c0fbda9bbaf58ec08b91115e99f73b727fPaul Jensen                    done(Result.DISMISSED);
244869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen                }
245869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen            }
246869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        }).start();
247869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    }
248869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
249869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    private class MyWebViewClient extends WebViewClient {
2505344a4abdf239a19485a9c858b6cc3be96002eacPaul Jensen        private static final String INTERNAL_ASSETS = "file:///android_asset/";
25165636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        private final String mBrowserBailOutToken = Long.toString(new Random().nextLong());
25265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        // How many Android device-independent-pixels per scaled-pixel
25365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        // dp/sp = (px/sp) / (px/dp) = (1/sp) / (1/dp)
25465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        private final float mDpPerSp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 1,
25565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                    getResources().getDisplayMetrics()) /
25665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                    TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
25765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                    getResources().getDisplayMetrics());
25865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        private boolean mFirstPageLoad = true;
25988eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen
260869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        @Override
261869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        public void onPageStarted(WebView view, String url, Bitmap favicon) {
26265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            if (url.contains(mBrowserBailOutToken)) {
26365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                mLaunchBrowser = true;
26465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                done(Result.WANTED_AS_IS);
26565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                return;
26665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            }
26765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            if (mFirstPageLoad) return;
268869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen            testForCaptivePortal();
269869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        }
270869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
271869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        @Override
272869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        public void onPageFinished(WebView view, String url) {
27365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            if (mFirstPageLoad) {
27465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                mFirstPageLoad = false;
27588eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                // Now that WebView has loaded at least one page we know it has read in the proxy
27688eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                // settings.  Now prompt the WebView read the Network-specific proxy settings.
27788eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                setWebViewProxy();
27888eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                // Load the real page.
27988eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                view.loadUrl(mURL.toString());
28088eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen                return;
28188eb0fa8eec7da1b7a3bd39f9d9844909911bc64Paul Jensen            }
2825344a4abdf239a19485a9c858b6cc3be96002eacPaul Jensen            // For internally generated pages, leave URL bar listing prior URL as this is the URL
2835344a4abdf239a19485a9c858b6cc3be96002eacPaul Jensen            // the page refers to.
2845344a4abdf239a19485a9c858b6cc3be96002eacPaul Jensen            if (!url.startsWith(INTERNAL_ASSETS)) {
2855344a4abdf239a19485a9c858b6cc3be96002eacPaul Jensen                final TextView myUrlBar = (TextView) findViewById(R.id.url_bar);
2865344a4abdf239a19485a9c858b6cc3be96002eacPaul Jensen                myUrlBar.setText(url);
2875344a4abdf239a19485a9c858b6cc3be96002eacPaul Jensen            }
288869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen            testForCaptivePortal();
289869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        }
290fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen
29165636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        // Convert Android device-independent-pixels (dp) to HTML size.
29265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        private String dp(int dp) {
29365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            // HTML px's are scaled just like dp's, so just add "px" suffix.
29465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            return Integer.toString(dp) + "px";
29565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        }
29665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen
29765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        // Convert Android scaled-pixels (sp) to HTML size.
29865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        private String sp(int sp) {
29965636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            // Convert sp to dp's.
30065636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            float dp = sp * mDpPerSp;
30165636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            // Apply a scale factor to make things look right.
30265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            dp *= 1.3;
30365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            // Convert dp's to HTML size.
30465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            return dp((int)dp);
30565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        }
30665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen
307fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen        // A web page consisting of a large broken lock icon to indicate SSL failure.
30865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen        private final String SSL_ERROR_HTML = "<html><head><style>" +
30965636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "body { margin-left:" + dp(48) + "; margin-right:" + dp(48) + "; " +
31065636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                        "margin-top:" + dp(96) + "; background-color:#fafafa; }" +
31165636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "img { width:" + dp(48) + "; height:" + dp(48) + "; }" +
31265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "div.warn { font-size:" + sp(16) + "; margin-top:" + dp(16) + "; " +
31365636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "           opacity:0.87; line-height:1.28; }" +
31465636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "div.example { font-size:" + sp(14) + "; margin-top:" + dp(16) + "; " +
31565636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "              opacity:0.54; line-height:1.21905; }" +
31665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "a { font-size:" + sp(14) + "; text-decoration:none; text-transform:uppercase; " +
31765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "    margin-top:" + dp(24) + "; display:inline-block; color:#4285F4; " +
31865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "    height:" + dp(48) + "; font-weight:bold; }" +
31965636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "</style></head><body><p><img src=quantum_ic_warning_amber_96.png><br>" +
32065636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "<div class=warn>%s</div>" +
32165636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "<div class=example>%s</div>" +
32265636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                "<a href=%s>%s</a></body></html>";
323fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen
324fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen        @Override
325fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
32665636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            Log.w(TAG, "SSL error; displaying SSL warning.");
32765636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            final String html = String.format(SSL_ERROR_HTML, getString(R.string.ssl_error_warning),
32865636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                    getString(R.string.ssl_error_example), mBrowserBailOutToken,
32965636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen                    getString(R.string.ssl_error_continue));
33065636fb23c86e546dc4ce584481fa58bf72e4945Paul Jensen            view.loadDataWithBaseURL(INTERNAL_ASSETS, html, "text/HTML", "UTF-8", null);
331fc8022f8cfffded3d94baef3ba5e5ce936799b06Paul Jensen        }
332869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    }
333869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen
334869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    private class MyWebChromeClient extends WebChromeClient {
335869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        @Override
336869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        public void onProgressChanged(WebView view, int newProgress) {
3375344a4abdf239a19485a9c858b6cc3be96002eacPaul Jensen            final ProgressBar myProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
3388f333f19222ac9415152e31f10e0df2b571b0b77Paul Jensen            myProgressBar.setProgress(newProgress);
339869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen        }
340869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen    }
341869868be653cb8eedd338e8347dfee1520d38cecPaul Jensen}
342