1539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott/*
2539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott * Copyright (C) 2010 The Android Open Source Project
3539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott *
4539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott * Licensed under the Apache License, Version 2.0 (the "License");
5539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott * you may not use this file except in compliance with the License.
6539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott * You may obtain a copy of the License at
7539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott *
8539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott *      http://www.apache.org/licenses/LICENSE-2.0
9539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott *
10539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott * Unless required by applicable law or agreed to in writing, software
11539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott * distributed under the License is distributed on an "AS IS" BASIS,
12539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott * See the License for the specific language governing permissions and
14539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott * limitations under the License.
15539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott */
16539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott
17539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scottpackage com.android.browser;
18539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott
19539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scottimport android.accounts.Account;
20539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scottimport android.accounts.AccountManager;
21539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scottimport android.accounts.AccountManagerCallback;
22539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scottimport android.accounts.AccountManagerFuture;
23539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scottimport android.app.Activity;
24cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scottimport android.app.ProgressDialog;
25539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scottimport android.content.Context;
26539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scottimport android.content.DialogInterface;
27539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scottimport android.content.DialogInterface.OnCancelListener;
28cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scottimport android.content.SharedPreferences.Editor;
29539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scottimport android.net.Uri;
30539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scottimport android.os.Bundle;
31a4af6dce2ebe6c4979524461047fcc0eb76fce84Patrick Scottimport android.util.Log;
32ef18de60e02b4b2a7227d9e9751487cc74baec36Patrick Scottimport android.webkit.CookieSyncManager;
33539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scottimport android.webkit.WebView;
34539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scottimport android.webkit.WebViewClient;
35539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott
363a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamathimport java.io.IOException;
373a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamathimport java.io.InputStream;
383a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamathimport java.net.HttpURLConnection;
393a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamathimport java.net.Proxy;
403a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamathimport java.net.URL;
413a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamathimport java.nio.charset.Charset;
423a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamathimport java.nio.charset.IllegalCharsetNameException;
433a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamathimport java.nio.charset.UnsupportedCharsetException;
443a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamathimport libcore.io.Streams;
453a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamathimport libcore.net.http.ResponseUtils;
46ba287c2673922ac51c4e4258224574411c0117e1John Reck
477d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scottpublic class GoogleAccountLogin implements Runnable,
48539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        AccountManagerCallback<Bundle>, OnCancelListener {
49539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott
50a4af6dce2ebe6c4979524461047fcc0eb76fce84Patrick Scott    private static final String LOGTAG = "BrowserLogin";
51a4af6dce2ebe6c4979524461047fcc0eb76fce84Patrick Scott
52539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    // Url for issuing the uber token.
53539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    private Uri ISSUE_AUTH_TOKEN_URL = Uri.parse(
54539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            "https://www.google.com/accounts/IssueAuthToken?service=gaia&Session=false");
55539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    // Url for signing into a particular service.
56a4af6dce2ebe6c4979524461047fcc0eb76fce84Patrick Scott    private static final Uri TOKEN_AUTH_URL = Uri.parse(
57539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            "https://www.google.com/accounts/TokenAuth");
58539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    // Google account type
59a4af6dce2ebe6c4979524461047fcc0eb76fce84Patrick Scott    private static final String GOOGLE = "com.google";
60cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott    // Last auto login time
61ba287c2673922ac51c4e4258224574411c0117e1John Reck    public static final String PREF_AUTOLOGIN_TIME = "last_autologin_time";
62539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott
63539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    private final Activity mActivity;
64539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    private final Account mAccount;
65539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    private final WebView mWebView;
66539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    private Runnable mRunnable;
67cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott    private ProgressDialog mProgressDialog;
68539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott
69539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    // SID and LSID retrieval process.
70539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    private String mSid;
71539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    private String mLsid;
72539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    private int mState;  // {NONE(0), SID(1), LSID(2)}
737d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott    private boolean mTokensInvalidated;
749bbd9314fee5f284ff254d7063c30d147fe47454Ben Murdoch    private String mUserAgent;
75539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott
76d43e75adea6f394730828cbf830438e2bddaed4bPatrick Scott    private GoogleAccountLogin(Activity activity, Account account,
77cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott            Runnable runnable) {
78539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        mActivity = activity;
79d43e75adea6f394730828cbf830438e2bddaed4bPatrick Scott        mAccount = account;
80539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        mWebView = new WebView(mActivity);
81cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott        mRunnable = runnable;
829bbd9314fee5f284ff254d7063c30d147fe47454Ben Murdoch        mUserAgent = mWebView.getSettings().getUserAgentString();
83cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott
84ef18de60e02b4b2a7227d9e9751487cc74baec36Patrick Scott        // XXX: Doing pre-login causes onResume to skip calling
85ef18de60e02b4b2a7227d9e9751487cc74baec36Patrick Scott        // resumeWebViewTimers. So to avoid problems with timers not running, we
86ef18de60e02b4b2a7227d9e9751487cc74baec36Patrick Scott        // duplicate the work here using the off-screen WebView.
87ef18de60e02b4b2a7227d9e9751487cc74baec36Patrick Scott        CookieSyncManager.getInstance().startSync();
88e1dbb956d762c3f07033f247c05270a9882a79a7Mathew Inwood        WebViewTimersControl.getInstance().onBrowserActivityResume(mWebView);
89ef18de60e02b4b2a7227d9e9751487cc74baec36Patrick Scott
90539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        mWebView.setWebViewClient(new WebViewClient() {
91539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            @Override
92539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            public boolean shouldOverrideUrlLoading(WebView view, String url) {
93539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                return false;
94539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            }
95539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            @Override
96539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            public void onPageFinished(WebView view, String url) {
97539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                done();
98539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            }
99539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        });
100539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    }
101539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott
102cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott    private void saveLoginTime() {
103ba287c2673922ac51c4e4258224574411c0117e1John Reck        Editor ed = BrowserSettings.getInstance().getPreferences().edit();
104cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott        ed.putLong(PREF_AUTOLOGIN_TIME, System.currentTimeMillis());
105cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott        ed.apply();
106cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott    }
107cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott
1087d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott    // Runnable
109539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    @Override
110539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    public void run() {
1113a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath        String urlString = ISSUE_AUTH_TOKEN_URL.buildUpon()
112539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                .appendQueryParameter("SID", mSid)
113539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                .appendQueryParameter("LSID", mLsid)
114539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                .build().toString();
115539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott
1163a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath        HttpURLConnection connection = null;
1173a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath        String authToken = null;
118539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        try {
1193a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath            URL url = new URL(urlString);
1203a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath            // Intentionally not using Proxy.
1213a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath            connection = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
1223a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath            connection.setRequestMethod("POST");
1233a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath            connection.setRequestProperty("User-Agent", mUserAgent);
1243a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath
1253a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath            int status = connection.getResponseCode();
1263a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath            if (status != HttpURLConnection.HTTP_OK) {
127a4af6dce2ebe6c4979524461047fcc0eb76fce84Patrick Scott                Log.d(LOGTAG, "LOGIN_FAIL: Bad status from auth url "
1283a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath                      + status + ": " + connection.getResponseMessage());
1297d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott                // Invalidate the tokens once just in case the 403 was for other
1307d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott                // reasons.
1313a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath                if (status == HttpURLConnection.HTTP_FORBIDDEN && !mTokensInvalidated) {
1327d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott                    Log.d(LOGTAG, "LOGIN_FAIL: Invalidating tokens...");
1337d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott                    // Need to regenerate the auth tokens and try again.
1347d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott                    invalidateTokens();
1357d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott                    // XXX: Do not touch any more member variables from this
1367d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott                    // thread as a second thread will handle the next login
1377d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott                    // attempt.
1387d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott                    return;
1397d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott                }
140539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                done();
141539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                return;
142539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            }
1433a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath
1443a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath            final Charset responseCharset = ResponseUtils.responseCharset(
1453a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath                    connection.getContentType());
1463a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath            byte[] responseBytes = Streams.readFully(connection.getInputStream());
1473a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath            authToken = new String(responseBytes, responseCharset);
148539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        } catch (Exception e) {
149a4af6dce2ebe6c4979524461047fcc0eb76fce84Patrick Scott            Log.d(LOGTAG, "LOGIN_FAIL: Exception acquiring uber token " + e);
150539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            done();
151539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            return;
152539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        } finally {
1533a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath            if (connection != null) {
1543a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath                connection.disconnect();
1553a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath            }
156539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        }
1573a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath
158539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        final String newUrl = TOKEN_AUTH_URL.buildUpon()
159539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                .appendQueryParameter("source", "android-browser")
1603a2cf8007db4f3258b94fdcb74c147220350aa36Narayan Kamath                .appendQueryParameter("auth", authToken)
161539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                .appendQueryParameter("continue",
162539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                        BrowserSettings.getFactoryResetHomeUrl(mActivity))
163539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                .build().toString();
164539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        mActivity.runOnUiThread(new Runnable() {
165539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            @Override public void run() {
166081caaa34ab5af0694087c3203e39be25797455aPatrick Scott                // Check mRunnable in case the request has been canceled.  This
167081caaa34ab5af0694087c3203e39be25797455aPatrick Scott                // is most likely not necessary as run() is the only non-UI
168081caaa34ab5af0694087c3203e39be25797455aPatrick Scott                // thread that calls done() but I am paranoid.
169081caaa34ab5af0694087c3203e39be25797455aPatrick Scott                synchronized (GoogleAccountLogin.this) {
170081caaa34ab5af0694087c3203e39be25797455aPatrick Scott                    if (mRunnable == null) {
171081caaa34ab5af0694087c3203e39be25797455aPatrick Scott                        return;
172081caaa34ab5af0694087c3203e39be25797455aPatrick Scott                    }
173081caaa34ab5af0694087c3203e39be25797455aPatrick Scott                    mWebView.loadUrl(newUrl);
174081caaa34ab5af0694087c3203e39be25797455aPatrick Scott                }
175539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            }
176539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        });
177539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    }
178539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott
1797d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott    private void invalidateTokens() {
1807d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott        AccountManager am = AccountManager.get(mActivity);
1817d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott        am.invalidateAuthToken(GOOGLE, mSid);
1827d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott        am.invalidateAuthToken(GOOGLE, mLsid);
1837d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott        mTokensInvalidated = true;
1847d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott        mState = 1;  // SID
1857d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott        am.getAuthToken(mAccount, "SID", null, mActivity, this, null);
1867d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott    }
1877d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott
188539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    // AccountManager callbacks.
189539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    @Override
190539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    public void run(AccountManagerFuture<Bundle> value) {
191539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        try {
192539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            String id = value.getResult().getString(
193539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                    AccountManager.KEY_AUTHTOKEN);
194539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            switch (mState) {
195539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                default:
196539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                case 0:
197539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                    throw new IllegalStateException(
198539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                            "Impossible to get into this state");
199539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                case 1:
200539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                    mSid = id;
201539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                    mState = 2;  // LSID
202539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                    AccountManager.get(mActivity).getAuthToken(
203539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                            mAccount, "LSID", null, mActivity, this, null);
204539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                    break;
205539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                case 2:
206539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                    mLsid = id;
2077d50a9364107c21e3358e1dbc51a06359a5287fbPatrick Scott                    new Thread(this).start();
208539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                    break;
209539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            }
210539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        } catch (Exception e) {
211a4af6dce2ebe6c4979524461047fcc0eb76fce84Patrick Scott            Log.d(LOGTAG, "LOGIN_FAIL: Exception in state " + mState + " " + e);
212539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            // For all exceptions load the original signin page.
213539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            // TODO: toast login failed?
214539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            done();
215539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        }
216539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    }
217539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott
218cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott    // Start the login process if auto-login is enabled and the user is not
219cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott    // already logged in.
220cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott    public static void startLoginIfNeeded(Activity activity,
221d43e75adea6f394730828cbf830438e2bddaed4bPatrick Scott            Runnable runnable) {
222d43e75adea6f394730828cbf830438e2bddaed4bPatrick Scott        // Already logged in?
223ba287c2673922ac51c4e4258224574411c0117e1John Reck        if (isLoggedIn()) {
224cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott            runnable.run();
225cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott            return;
226cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott        }
227cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott
228cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott        // No account found?
229d43e75adea6f394730828cbf830438e2bddaed4bPatrick Scott        Account[] accounts = getAccounts(activity);
230d43e75adea6f394730828cbf830438e2bddaed4bPatrick Scott        if (accounts == null || accounts.length == 0) {
231cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott            runnable.run();
232cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott            return;
233cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott        }
234cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott
235cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott        GoogleAccountLogin login =
236d43e75adea6f394730828cbf830438e2bddaed4bPatrick Scott                new GoogleAccountLogin(activity, accounts[0], runnable);
237cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott        login.startLogin();
238cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott    }
239cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott
240cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott    private void startLogin() {
241ba287c2673922ac51c4e4258224574411c0117e1John Reck        saveLoginTime();
242cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott        mProgressDialog = ProgressDialog.show(mActivity,
243cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott                mActivity.getString(R.string.pref_autologin_title),
244cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott                mActivity.getString(R.string.pref_autologin_progress,
245cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott                                    mAccount.name),
246cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott                true /* indeterminate */,
247cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott                true /* cancelable */,
248cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott                this);
249539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        mState = 1;  // SID
250539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        AccountManager.get(mActivity).getAuthToken(
251539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott                mAccount, "SID", null, mActivity, this, null);
252539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    }
253539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott
254d43e75adea6f394730828cbf830438e2bddaed4bPatrick Scott    private static Account[] getAccounts(Context ctx) {
255539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        return AccountManager.get(ctx).getAccountsByType(GOOGLE);
256539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    }
257539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott
258d43e75adea6f394730828cbf830438e2bddaed4bPatrick Scott    // Checks if we already did pre-login.
259ba287c2673922ac51c4e4258224574411c0117e1John Reck    private static boolean isLoggedIn() {
260cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott        // See if we last logged in less than a week ago.
261ba287c2673922ac51c4e4258224574411c0117e1John Reck        long lastLogin = BrowserSettings.getInstance().getPreferences()
262ba287c2673922ac51c4e4258224574411c0117e1John Reck                .getLong(PREF_AUTOLOGIN_TIME, -1);
263cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott        if (lastLogin == -1) {
264cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott            return false;
265cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott        }
266d43e75adea6f394730828cbf830438e2bddaed4bPatrick Scott        return true;
26797efb1e650b356d3fc81ff531544383362759d77Patrick Scott    }
26897efb1e650b356d3fc81ff531544383362759d77Patrick Scott
269539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    // Used to indicate that the Browser should continue loading the main page.
270539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    // This can happen on success, error, or timeout.
271539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    private synchronized void done() {
272539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        if (mRunnable != null) {
273a4af6dce2ebe6c4979524461047fcc0eb76fce84Patrick Scott            Log.d(LOGTAG, "Finished login attempt for " + mAccount.name);
274539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            mActivity.runOnUiThread(mRunnable);
275cd135089d54a8c003f7b0a35d88b3230f0a206b5Patrick Scott
276cf1df73719e171915eed887a5f06916158e42f04John Reck            try {
277cf1df73719e171915eed887a5f06916158e42f04John Reck                mProgressDialog.dismiss();
278cf1df73719e171915eed887a5f06916158e42f04John Reck            } catch (Exception e) {
279cf1df73719e171915eed887a5f06916158e42f04John Reck                // TODO: Switch to a managed dialog solution (DialogFragment?)
280cf1df73719e171915eed887a5f06916158e42f04John Reck                // Also refactor this class, it doesn't
281cf1df73719e171915eed887a5f06916158e42f04John Reck                // play nice with the activity lifecycle, leading to issues
282cf1df73719e171915eed887a5f06916158e42f04John Reck                // with the dialog it manages
283cf1df73719e171915eed887a5f06916158e42f04John Reck                Log.w(LOGTAG, "Failed to dismiss mProgressDialog: " + e.getMessage());
284cf1df73719e171915eed887a5f06916158e42f04John Reck            }
285539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott            mRunnable = null;
286474c927a76d4a6b12e942ba4bde3a7db18a1a332John Reck            mActivity.runOnUiThread(new Runnable() {
287474c927a76d4a6b12e942ba4bde3a7db18a1a332John Reck                @Override
288474c927a76d4a6b12e942ba4bde3a7db18a1a332John Reck                public void run() {
289474c927a76d4a6b12e942ba4bde3a7db18a1a332John Reck                    mWebView.destroy();
290474c927a76d4a6b12e942ba4bde3a7db18a1a332John Reck                }
291474c927a76d4a6b12e942ba4bde3a7db18a1a332John Reck            });
292539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        }
293539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    }
294539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott
295539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    // Called by the progress dialog on startup.
296539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    public void onCancel(DialogInterface unused) {
297539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott        done();
298539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott    }
2990fba5d9470f3b98d5aa61ae7392164450baf73deMichael Kolb
300539e2eced0f35144d7841477e5cdc2d8c521e82aPatrick Scott}
301