Merge "change app tab behavior" into honeycomb-mr1
diff --git a/res/anim/autologin_enter.xml b/res/anim/autologin_enter.xml
new file mode 100644
index 0000000..45e5204
--- /dev/null
+++ b/res/anim/autologin_enter.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:fromYDelta="-100%" android:toYDelta="0"
+    android:duration="@android:integer/config_longAnimTime"/>
diff --git a/res/anim/autologin_exit.xml b/res/anim/autologin_exit.xml
new file mode 100644
index 0000000..6faa715
--- /dev/null
+++ b/res/anim/autologin_exit.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:fromYDelta="0" android:toYDelta="-100%"
+    android:duration="@android:integer/config_longAnimTime"/>
diff --git a/res/layout/url_bar.xml b/res/layout/url_bar.xml
index f2b32c4..bf184a8 100644
--- a/res/layout/url_bar.xml
+++ b/res/layout/url_bar.xml
@@ -10,11 +10,68 @@
         the specific language governing permissions and limitations under the
         License.
     -->
-<LinearLayout
+<RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical">
+    >
+    <LinearLayout
+        android:id="@+id/autologin"
+        android:background="#FBF0A0"
+        android:gravity="center_vertical"
+        android:paddingTop="3dip"
+        android:visibility="gone"
+        android:layout_below="@+id/taburlbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/autologin_bar_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textColor="@android:color/primary_text_light"
+            android:paddingLeft="15dip"
+            android:paddingRight="15dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"/>
+        <Button
+            android:id="@+id/autologin_account"
+            android:background="@android:drawable/btn_dropdown"
+            android:textColor="@android:color/primary_text_light"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"/>
+        <Button
+            android:id="@+id/autologin_login"
+            android:text="@string/autologin_bar_login_text"
+            android:background="@android:drawable/btn_default"
+            android:textColor="@android:color/primary_text_light"
+            android:layout_marginRight="15dip"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+        <ProgressBar
+            android:id="@+id/autologin_progress"
+            android:indeterminateOnly="true"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:visibility="gone" />
+        <TextView
+            android:id="@+id/autologin_error"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:textColor="#dd6826"
+            android:text="@string/autologin_bar_error"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:visibility="gone" />
+        <View
+            android:layout_width="2dip"
+            android:layout_height="match_parent"
+            android:layout_weight="1"/>
+        <ImageButton
+            android:id="@+id/autologin_close"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingRight="15dip"
+            android:background="@null"
+            android:src="@*android:drawable/btn_close"/>
+    </LinearLayout>
     <LinearLayout
         android:id="@+id/taburlbar"
         android:layout_width="match_parent"
@@ -128,7 +185,8 @@
         android:layout_width="match_parent"
         android:layout_height="22dip"
         android:background="@null"
+        android:layout_below="@+id/taburlbar"
         android:src="@drawable/progress"
         android:layout_marginTop="-11dip"
         android:visibility="gone" />
-</LinearLayout>
+</RelativeLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5a8a110..9f339f5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -381,14 +381,8 @@
 
     <!-- Auto login preference title [CHAR-LIMIT=32] -->
     <string name="pref_autologin_title">Automatic Google sign-in</string>
-    <!-- Summary when no accounts are found [CHAR-LIMIT=32] -->
-    <string name="pref_autologin_no_account">No accounts found</string>
-    <!-- Summary when there is an account available [CHAR-LIMIT=none] -->
-    <string name="pref_autologin_summary">Sign into Google sites automatically using <xliff:g>%s</xliff:g></string>
     <!-- Message shown during auto login [CHAR-LIMIT=none] -->
     <string name="pref_autologin_progress">Signing into Google sites using <xliff:g>%s</xliff:g>\nYour privacy &amp; security settings control automatic Google sign-in</string>
-    <!-- Option in account list to disable autologin [CHAR-LIMIT=50] -->
-    <string name="pref_autologin_disable">Don\'t sign in automatically</string>
     <!-- Auto-login bar description [CHAR-LIMIT=40] -->
     <string name="autologin_bar_text">Automatic sign-in is available.</string>
     <!-- Login button [CHAR-LIMIT=10] -->
diff --git a/res/xml/privacy_security_preferences.xml b/res/xml/privacy_security_preferences.xml
index 9bff4b8..50802ca 100644
--- a/res/xml/privacy_security_preferences.xml
+++ b/res/xml/privacy_security_preferences.xml
@@ -39,10 +39,6 @@
             android:title="@string/pref_security_show_security_warning"
             android:summary="@string/pref_security_show_security_warning_summary" />
 
-    <ListPreference
-            android:key="autologin_account"
-            android:title="@string/pref_autologin_title" />
-
     <PreferenceCategory android:title="@string/pref_privacy_cookies_title">
         <CheckBoxPreference
                 android:key="accept_cookies"
diff --git a/src/com/android/browser/BaseUi.java b/src/com/android/browser/BaseUi.java
index 5084e31..71346ae 100644
--- a/src/com/android/browser/BaseUi.java
+++ b/src/com/android/browser/BaseUi.java
@@ -240,6 +240,7 @@
         onProgressChanged(tab);
         boolean incognito = mActiveTab.getWebView().isPrivateBrowsingEnabled();
         getTitleBar().setIncognitoMode(incognito);
+        updateAutoLogin(tab, false);
     }
 
     Tab getActiveTab() {
@@ -546,11 +547,23 @@
             && mComboView == null;
     }
 
+    @Override
+    public void showAutoLogin(Tab tab) {
+        updateAutoLogin(tab, true);
+    }
+
+    @Override
+    public void hideAutoLogin(Tab tab) {
+        updateAutoLogin(tab, true);
+    }
+
     // -------------------------------------------------------------------------
 
     protected void updateNavigationState(Tab tab) {
     }
 
+    protected void updateAutoLogin(Tab tab, boolean animate) {}
+
     /**
      * Update the lock icon to correspond to our latest state.
      */
diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
index 75e0cfb..357d1e9 100644
--- a/src/com/android/browser/BrowserSettings.java
+++ b/src/com/android/browser/BrowserSettings.java
@@ -92,9 +92,6 @@
     private String databasePath; // default value set in loadFromDb()
     private String geolocationDatabasePath; // default value set in loadFromDb()
     private WebStorageSizeManager webStorageSizeManager;
-    // Autologin settings
-    private boolean autoLoginEnabled;
-    private String autoLoginAccount;
 
     private String jsFlags = "";
 
@@ -168,8 +165,6 @@
 
     public final static String PREF_QUICK_CONTROLS = "enable_quick_controls";
     public final static String PREF_MOST_VISITED_HOMEPAGE = "use_most_visited_homepage";
-    public final static String PREF_AUTOLOGIN = "enable_autologin";
-    public final static String PREF_AUTOLOGIN_ACCOUNT = "autologin_account";
     public final static String PREF_PLUGIN_STATE = "plugin_state";
     public final static String PREF_USE_INSTANT = "use_instant_search";
 
@@ -537,11 +532,6 @@
         geolocationEnabled = p.getBoolean("enable_geolocation", geolocationEnabled);
         workersEnabled = p.getBoolean("enable_workers", workersEnabled);
 
-        // Autologin account settings.  The account preference may be null until
-        // the user explicitly changes the account in the settings.
-        autoLoginEnabled = p.getBoolean(PREF_AUTOLOGIN, autoLoginEnabled);
-        autoLoginAccount = p.getString(PREF_AUTOLOGIN_ACCOUNT, autoLoginAccount);
-
         update();
     }
 
@@ -632,32 +622,6 @@
         update();
     }
 
-    public boolean isAutoLoginEnabled() {
-        return autoLoginEnabled;
-    }
-
-    public String getAutoLoginAccount(Context context) {
-        // Each time we attempt to get the account, we need to verify that the
-        // account is still valid.
-        return GoogleAccountLogin.validateAccount(context, autoLoginAccount);
-    }
-
-    public void setAutoLoginAccount(Context context, String name) {
-        Editor ed = PreferenceManager.
-                getDefaultSharedPreferences(context).edit();
-        ed.putString(PREF_AUTOLOGIN_ACCOUNT, name);
-        ed.apply();
-        autoLoginAccount = name;
-    }
-
-    public void setAutoLoginEnabled(Context context, boolean enable) {
-        Editor ed = PreferenceManager.
-                getDefaultSharedPreferences(context).edit();
-        ed.putBoolean(PREF_AUTOLOGIN, enable);
-        ed.apply();
-        autoLoginEnabled = enable;
-    }
-
     public void setAutoFillProfile(Context ctx, AutoFillProfile profile, Message msg) {
         if (profile != null) {
             setActiveAutoFillProfileId(ctx, profile.getUniqueId());
@@ -854,9 +818,6 @@
         domStorageEnabled = true;
         geolocationEnabled = true;
         workersEnabled = true;  // only affects V8. JSC does not have a similar setting
-        // Autologin default is true.  The account will be populated when
-        // reading from the DB as that is when a context is available.
-        autoLoginEnabled = true;
     }
 
     private abstract class AutoFillProfileDbTask<T> extends AsyncTask<T, Void, Void> {
diff --git a/src/com/android/browser/Controller.java b/src/com/android/browser/Controller.java
index d02f05c..82aea47 100644
--- a/src/com/android/browser/Controller.java
+++ b/src/com/android/browser/Controller.java
@@ -279,7 +279,7 @@
             CookieManager.getInstance().removeSessionCookie();
         }
 
-        GoogleAccountLogin.startLoginIfNeeded(mActivity, mSettings,
+        GoogleAccountLogin.startLoginIfNeeded(mActivity,
                 new Runnable() {
                     @Override public void run() {
                         start(icicle, intent, currentTab, restoreIncognitoTabs);
@@ -996,6 +996,19 @@
         mPageDialogsHandler.showSSLCertificateOnError(view, handler, error);
     }
 
+    @Override
+    public void showAutoLogin(Tab tab) {
+        assert tab.inForeground();
+        // Update the title bar to show the auto-login request.
+        mUi.showAutoLogin(tab);
+    }
+
+    @Override
+    public void hideAutoLogin(Tab tab) {
+        assert tab.inForeground();
+        mUi.hideAutoLogin(tab);
+    }
+
     // helper method
 
     /*
@@ -2216,17 +2229,9 @@
             // animation behavior.
             addTab(tab);
             setActiveTab(tab);
-
-            // Callback to load the url data.
-            final Runnable load = new Runnable() {
-                @Override public void run() {
-                    if (!urlData.isEmpty()) {
-                        loadUrlDataIn(tab, urlData);
-                    }
-                }
-            };
-
-            GoogleAccountLogin.startLoginIfNeeded(mActivity, mSettings, load);
+            if (!urlData.isEmpty()) {
+                loadUrlDataIn(tab, urlData);
+            }
             return tab;
         } else {
             // Get rid of the subwindow if it exists
diff --git a/src/com/android/browser/DeviceAccountLogin.java b/src/com/android/browser/DeviceAccountLogin.java
new file mode 100644
index 0000000..50b8c97
--- /dev/null
+++ b/src/com/android/browser/DeviceAccountLogin.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.browser;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+public class DeviceAccountLogin implements
+        AccountManagerCallback<Bundle>, DialogInterface.OnClickListener {
+
+    private final Activity mActivity;
+    private final WebView mWebView;
+    private final Tab mTab;
+    private final WebViewController mWebViewController;
+    private final AccountManager mAccountManager;
+    private Account[] mAccounts;
+    private int mCurrentAccount;
+    private AutoLoginCallback mCallback;
+    private String mAuthToken;
+
+    // Current state of the login.
+    private int mState = INITIAL;
+
+    public static final int INITIAL = 0;
+    public static final int FAILED = 1;
+    public static final int PROCESSING = 2;
+
+    public interface AutoLoginCallback {
+        public void setAccount(String account);
+        public void loginFailed();
+    }
+
+    public DeviceAccountLogin(Activity activity, WebView view, Tab tab,
+            WebViewController controller) {
+        mActivity = activity;
+        mWebView = view;
+        mTab = tab;
+        mWebViewController = controller;
+        mAccountManager = AccountManager.get(activity);
+    }
+
+    public void handleLogin(String realm, String account, String args) {
+        mAccounts = mAccountManager.getAccountsByType(realm);
+        mAuthToken = "weblogin:" + args;
+
+        // No need to display UI if there are no accounts.
+        if (mAccounts.length == 0) {
+            return;
+        }
+
+        // Verify the account before using it.
+        for (Account a : mAccounts) {
+            if (a.name.equals(account)) {
+                // Handle the automatic login case where the service gave us an
+                // account to use.
+                mAccountManager.getAuthToken(a, mAuthToken, null,
+                       mActivity, this, null);
+                return;
+            }
+        }
+
+        displayLoginUi();
+    }
+
+    @Override
+    public void run(AccountManagerFuture<Bundle> value) {
+        try {
+            String result = value.getResult().getString(
+                    AccountManager.KEY_AUTHTOKEN);
+            if (result == null) {
+                loginFailed();
+            } else {
+                mWebView.loadUrl(result);
+                mTab.setDeviceAccountLogin(null);
+                if (mTab.inForeground()) {
+                    mWebViewController.hideAutoLogin(mTab);
+                }
+            }
+        } catch (Exception e) {
+            loginFailed();
+        }
+    }
+
+    public int getState() {
+        return mState;
+    }
+
+    private void loginFailed() {
+        mState = FAILED;
+        if (mTab.getDeviceAccountLogin() == null) {
+            displayLoginUi();
+        } else {
+            assert mCallback != null;
+            mCallback.loginFailed();
+        }
+    }
+
+    private void displayLoginUi() {
+        // Display the account picker.
+        mTab.setDeviceAccountLogin(this);
+        if (mTab.inForeground()) {
+            mWebViewController.showAutoLogin(mTab);
+        }
+    }
+
+    public void cancel() {
+        mTab.setDeviceAccountLogin(null);
+    }
+
+    public void login(AutoLoginCallback cb) {
+        mState = PROCESSING;
+        mCallback = cb;
+        mAccountManager.getAuthToken(
+                mAccounts[mCurrentAccount], mAuthToken, null,
+                mActivity, this, null);
+    }
+
+    public void chooseAccount(AutoLoginCallback cb) {
+        mCallback = cb;
+        CharSequence[] names = new CharSequence[mAccounts.length];
+        int i = 0;
+        for (Account a : mAccounts) {
+            names[i++] = a.name;
+        }
+        new AlertDialog.Builder(mActivity)
+                .setTitle(R.string.pref_autologin_title)
+                .setSingleChoiceItems(names, mCurrentAccount, this)
+                .setCancelable(true)
+                .show();
+    }
+
+    public String getCurrentAccount() {
+        return mAccounts[mCurrentAccount].name;
+    }
+
+    @Override
+    public void onClick(DialogInterface d, int which) {
+        assert mCallback != null;
+        mCallback.setAccount(mAccounts[which].name);
+        mCurrentAccount = which;
+        d.dismiss();
+    }
+}
diff --git a/src/com/android/browser/GoogleAccountLogin.java b/src/com/android/browser/GoogleAccountLogin.java
index f4ccfd1..0bde010 100644
--- a/src/com/android/browser/GoogleAccountLogin.java
+++ b/src/com/android/browser/GoogleAccountLogin.java
@@ -40,7 +40,6 @@
 import android.os.Handler;
 import android.preference.PreferenceManager;
 import android.util.Log;
-import android.webkit.CookieManager;
 import android.webkit.CookieSyncManager;
 import android.webkit.WebView;
 import android.webkit.WebViewClient;
@@ -80,10 +79,10 @@
     private int mState;  // {NONE(0), SID(1), LSID(2)}
     private boolean mTokensInvalidated;
 
-    private GoogleAccountLogin(Activity activity, String name,
+    private GoogleAccountLogin(Activity activity, Account account,
             Runnable runnable) {
         mActivity = activity;
-        mAccount = new Account(name, GOOGLE);
+        mAccount = account;
         mWebView = new WebView(mActivity);
         mRunnable = runnable;
 
@@ -233,28 +232,22 @@
     // Start the login process if auto-login is enabled and the user is not
     // already logged in.
     public static void startLoginIfNeeded(Activity activity,
-            BrowserSettings settings, Runnable runnable) {
-        // Auto login not enabled?
-        if (!settings.isAutoLoginEnabled()) {
-            runnable.run();
-            return;
-        }
-
-        // No account found?
-        String account = settings.getAutoLoginAccount(activity);
-        if (account == null) {
-            runnable.run();
-            return;
-        }
-
+            Runnable runnable) {
         // Already logged in?
         if (isLoggedIn(activity)) {
             runnable.run();
             return;
         }
 
+        // No account found?
+        Account[] accounts = getAccounts(activity);
+        if (accounts == null || accounts.length == 0) {
+            runnable.run();
+            return;
+        }
+
         GoogleAccountLogin login =
-                new GoogleAccountLogin(activity, account, runnable);
+                new GoogleAccountLogin(activity, accounts[0], runnable);
         login.startLogin();
     }
 
@@ -271,31 +264,12 @@
                 mAccount, "SID", null, mActivity, this, null);
     }
 
-    // Returns the account name passed in if the account exists, otherwise
-    // returns the default account.
-    public static String validateAccount(Context ctx, String name) {
-        Account[] accounts = getAccounts(ctx);
-        if (accounts.length == 0) {
-            return null;
-        }
-        if (name != null) {
-            // Make sure the account still exists.
-            for (Account a : accounts) {
-                if (a.name.equals(name)) {
-                    return name;
-                }
-            }
-        }
-        // Return the first entry.
-        return accounts[0].name;
-    }
-
-    public static Account[] getAccounts(Context ctx) {
+    private static Account[] getAccounts(Context ctx) {
         return AccountManager.get(ctx).getAccountsByType(GOOGLE);
     }
 
-    // Checks for the presence of the SID cookie on google.com.
-    public static boolean isLoggedIn(Context ctx) {
+    // Checks if we already did pre-login.
+    private static boolean isLoggedIn(Context ctx) {
         // See if we last logged in less than a week ago.
         long lastLogin = PreferenceManager.
                 getDefaultSharedPreferences(ctx).
@@ -303,31 +277,7 @@
         if (lastLogin == -1) {
             return false;
         }
-        long diff = System.currentTimeMillis() - lastLogin;
-        if (diff > WEEK_IN_MILLIS) {
-            Log.d(LOGTAG, "Forcing login after " + diff + "ms");
-            return false;
-        }
-
-        // This will potentially block the UI thread but we have to have the
-        // most updated cookies.
-        // FIXME: Figure out how to avoid waiting to clear session cookies.
-        CookieManager.getInstance().waitForCookieOperationsToComplete();
-
-        // Use /a/ to grab hosted cookies as well as the base set of google.com
-        // cookies.
-        String cookies = CookieManager.getInstance().getCookie(
-                "http://www.google.com/a/");
-        if (cookies != null) {
-            StringTokenizer tokenizer = new StringTokenizer(cookies, ";");
-            while (tokenizer.hasMoreTokens()) {
-                String cookie = tokenizer.nextToken().trim();
-                if (cookie.startsWith("SID=") || cookie.startsWith("ASIDAP=")) {
-                    return true;
-                }
-            }
-        }
-        return false;
+        return true;
     }
 
     // Used to indicate that the Browser should continue loading the main page.
diff --git a/src/com/android/browser/Tab.java b/src/com/android/browser/Tab.java
index bab3458..863fc95 100644
--- a/src/com/android/browser/Tab.java
+++ b/src/com/android/browser/Tab.java
@@ -132,6 +132,8 @@
     // Listener used to know when we move forward or back in the history list.
     private final WebBackForwardListClient mWebBackForwardListClient;
     private DataController mDataController;
+    // State of the auto-login request.
+    private DeviceAccountLogin mDeviceAccountLogin;
 
     // AsyncTask for downloading touch icons
     DownloadTouchIcon mTouchIconLoader;
@@ -530,6 +532,13 @@
                 }
             }
 
+            // Cancel the auto-login process.
+            if (mDeviceAccountLogin != null) {
+                mDeviceAccountLogin.cancel();
+                mDeviceAccountLogin = null;
+                mWebViewController.hideAutoLogin(Tab.this);
+            }
+
             // finally update the UI in the activity if it is in the foreground
             mWebViewController.onPageStarted(Tab.this, view, favicon);
 
@@ -812,8 +821,27 @@
             }
             mWebViewController.onUnhandledKeyEvent(event);
         }
+
+        @Override
+        public void onReceivedLoginRequest(WebView view, String realm,
+                String account, String args) {
+            new DeviceAccountLogin(mActivity, view, Tab.this, mWebViewController)
+                    .handleLogin(realm, account, args);
+        }
+
     };
 
+    // Called by DeviceAccountLogin when the Tab needs to have the auto-login UI
+    // displayed.
+    void setDeviceAccountLogin(DeviceAccountLogin login) {
+        mDeviceAccountLogin = login;
+    }
+
+    // Returns non-null if the title bar should display the auto-login UI.
+    DeviceAccountLogin getDeviceAccountLogin() {
+        return mDeviceAccountLogin;
+    }
+
     // -------------------------------------------------------------------------
     // WebChromeClient implementation for the main WebView
     // -------------------------------------------------------------------------
diff --git a/src/com/android/browser/TitleBarXLarge.java b/src/com/android/browser/TitleBarXLarge.java
index 51cf0c3..a786fd7 100644
--- a/src/com/android/browser/TitleBarXLarge.java
+++ b/src/com/android/browser/TitleBarXLarge.java
@@ -26,6 +26,9 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+import android.view.animation.AnimationUtils;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -34,9 +37,12 @@
 import android.view.ViewGroup;
 import android.webkit.WebView;
 import android.widget.AbsoluteLayout;
+import android.widget.Button;
 import android.widget.FrameLayout;
 import android.widget.ImageButton;
 import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
 
 import java.util.List;
 
@@ -44,7 +50,8 @@
  * tabbed title bar for xlarge screen browser
  */
 public class TitleBarXLarge extends TitleBarBase
-        implements OnClickListener, OnFocusChangeListener, TextChangeWatcher {
+        implements OnClickListener, OnFocusChangeListener, TextChangeWatcher,
+        DeviceAccountLogin.AutoLoginCallback {
 
     private XLargeUi mUi;
 
@@ -66,6 +73,14 @@
     private PageProgressView mProgressView;
     private Drawable mFocusDrawable;
     private Drawable mUnfocusDrawable;
+    // Auto-login UI
+    private View mAutoLogin;
+    private Button mAutoLoginAccount;
+    private Button mAutoLoginLogin;
+    private ProgressBar mAutoLoginProgress;
+    private TextView mAutoLoginError;
+    private ImageButton mAutoLoginCancel;
+    private DeviceAccountLogin mAutoLoginHandler;
 
     private boolean mInLoad;
     private boolean mUseQuickControls;
@@ -133,6 +148,18 @@
         mUrlInput.setOnFocusChangeListener(this);
         mUrlInput.setSelectAllOnFocus(true);
         mUrlInput.addQueryTextWatcher(this);
+        mAutoLogin = findViewById(R.id.autologin);
+        mAutoLoginAccount = (Button) findViewById(R.id.autologin_account);
+        mAutoLoginAccount.setOnClickListener(this);
+        mAutoLoginLogin = (Button) findViewById(R.id.autologin_login);
+        mAutoLoginLogin.setOnClickListener(this);
+        mAutoLoginProgress =
+                (ProgressBar) findViewById(R.id.autologin_progress);
+        mAutoLoginError = (TextView) findViewById(R.id.autologin_error);
+        mAutoLoginCancel =
+                (ImageButton) mAutoLogin.findViewById(R.id.autologin_close);
+        mAutoLoginCancel.setOnClickListener(this);
+
         setFocusState(false);
     }
 
@@ -148,6 +175,45 @@
         }
     }
 
+    void updateAutoLogin(Tab tab, boolean animate) {
+        DeviceAccountLogin login = tab.getDeviceAccountLogin();
+        if (login != null) {
+            mAutoLoginHandler = login;
+            mAutoLogin.setVisibility(View.VISIBLE);
+            mAutoLoginAccount.setText(login.getCurrentAccount());
+            mAutoLoginAccount.setEnabled(true);
+            mAutoLoginLogin.setEnabled(true);
+            mAutoLoginProgress.setVisibility(View.GONE);
+            mAutoLoginError.setVisibility(View.GONE);
+            switch (login.getState()) {
+                case DeviceAccountLogin.PROCESSING:
+                    mAutoLoginAccount.setEnabled(false);
+                    mAutoLoginLogin.setEnabled(false);
+                    mAutoLoginProgress.setVisibility(View.VISIBLE);
+                    break;
+                case DeviceAccountLogin.FAILED:
+                    mAutoLoginProgress.setVisibility(View.GONE);
+                    mAutoLoginError.setVisibility(View.VISIBLE);
+                    break;
+                case DeviceAccountLogin.INITIAL:
+                    break;
+                default:
+                    throw new IllegalStateException();
+            }
+            if (animate) {
+                mAutoLogin.startAnimation(AnimationUtils.loadAnimation(
+                        getContext(), R.anim.autologin_enter));
+            }
+        } else {
+            mAutoLoginHandler = null;
+            if (animate) {
+                hideAutoLogin();
+            } else if (mAutoLogin.getAnimation() == null) {
+                mAutoLogin.setVisibility(View.GONE);
+            }
+        }
+    }
+
     private ViewGroup.LayoutParams makeLayoutParams() {
         if (mUseQuickControls) {
             return new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
@@ -161,7 +227,11 @@
 
     @Override
     public int getEmbeddedHeight() {
-        return mContainer.getHeight();
+        int height = mContainer.getHeight();
+        if (mAutoLogin.getVisibility() == View.VISIBLE) {
+            height += mAutoLogin.getHeight();
+        }
+        return height;
     }
 
     void setUseQuickControls(boolean useQuickControls) {
@@ -234,6 +304,19 @@
         mUrlInput.clearFocus();
     }
 
+    private void hideAutoLogin() {
+        Animation anim = AnimationUtils.loadAnimation(
+                getContext(), R.anim.autologin_exit);
+        anim.setAnimationListener(new AnimationListener() {
+            @Override public void onAnimationEnd(Animation a) {
+                mAutoLogin.setVisibility(View.GONE);
+            }
+            @Override public void onAnimationStart(Animation a) {}
+            @Override public void onAnimationRepeat(Animation a) {}
+        });
+        mAutoLogin.startAnimation(anim);
+    }
+
     @Override
     public void onClick(View v) {
         if (mBackButton == v) {
@@ -258,10 +341,41 @@
             clearOrClose();
         } else if (mVoiceSearch == v) {
             mUiController.startVoiceSearch();
+        } else if (mAutoLoginCancel == v) {
+            if (mAutoLoginHandler != null) {
+                mAutoLoginHandler.cancel();
+                mAutoLoginHandler = null;
+            }
+            hideAutoLogin();
+        } else if (mAutoLoginLogin == v) {
+            if (mAutoLoginHandler != null) {
+                mAutoLoginAccount.setEnabled(false);
+                mAutoLoginLogin.setEnabled(false);
+                mAutoLoginProgress.setVisibility(View.VISIBLE);
+                mAutoLoginError.setVisibility(View.GONE);
+                mAutoLoginHandler.login(this);
+            }
+        } else if (mAutoLoginAccount == v) {
+            if (mAutoLoginHandler != null) {
+                mAutoLoginHandler.chooseAccount(this);
+            }
         }
     }
 
     @Override
+    public void setAccount(String account) {
+        mAutoLoginAccount.setText(account);
+    }
+
+    @Override
+    public void loginFailed() {
+        mAutoLoginAccount.setEnabled(true);
+        mAutoLoginLogin.setEnabled(true);
+        mAutoLoginProgress.setVisibility(View.GONE);
+        mAutoLoginError.setVisibility(View.VISIBLE);
+    }
+
+    @Override
     void setFavicon(Bitmap icon) { }
 
     private void clearOrClose() {
diff --git a/src/com/android/browser/UI.java b/src/com/android/browser/UI.java
index 13f8af2..368c829 100644
--- a/src/com/android/browser/UI.java
+++ b/src/com/android/browser/UI.java
@@ -124,9 +124,12 @@
 
     boolean dispatchKey(int code, KeyEvent event);
 
-
     public static interface DropdownChangeListener {
         void onNewDropdownDimensions(int height);
     }
     void registerDropdownChangeListener(DropdownChangeListener d);
+
+    void showAutoLogin(Tab tab);
+
+    void hideAutoLogin(Tab tab);
 }
diff --git a/src/com/android/browser/WebViewController.java b/src/com/android/browser/WebViewController.java
index 813b63b..6b44207 100644
--- a/src/com/android/browser/WebViewController.java
+++ b/src/com/android/browser/WebViewController.java
@@ -112,4 +112,7 @@
 
     void bookmarkedStatusHasChanged(Tab tab);
 
+    void showAutoLogin(Tab tab);
+
+    void hideAutoLogin(Tab tab);
 }
diff --git a/src/com/android/browser/XLargeUi.java b/src/com/android/browser/XLargeUi.java
index a9a55e8..02533b0 100644
--- a/src/com/android/browser/XLargeUi.java
+++ b/src/com/android/browser/XLargeUi.java
@@ -18,6 +18,9 @@
 
 import com.android.browser.ScrollWebView.ScrollListener;
 
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.ObjectAnimator;
 import android.app.ActionBar;
 import android.app.Activity;
 import android.content.pm.PackageManager;
@@ -47,6 +50,8 @@
     private TabBar mTabBar;
 
     private TitleBarXLarge mTitleBar;
+    private Animator mTitleBarAnimator;
+    private boolean mSkipTitleBarAnimations;
 
     private boolean mUseQuickControls;
     private PieControl mPieControl;
@@ -220,6 +225,8 @@
 
     @Override
     public void setActiveTab(final Tab tab) {
+        cancelTitleBarAnimation(true);
+        mSkipTitleBarAnimations = true;
         if (mUseQuickControls) {
             if (mActiveTab != null) {
                 captureTab(mActiveTab);
@@ -227,6 +234,7 @@
         }
         super.setActiveTab(tab, true);
         setActiveTab(tab, true);
+        mSkipTitleBarAnimations = false;
     }
 
     @Override
@@ -277,8 +285,11 @@
 
     @Override
     public void removeTab(Tab tab) {
+        cancelTitleBarAnimation(true);
+        mSkipTitleBarAnimations = true;
         super.removeTab(tab);
         mTabBar.onRemoveTab(tab);
+        mSkipTitleBarAnimations = false;
     }
 
     protected void onRemoveTabCompleted(Tab tab) {
@@ -317,6 +328,18 @@
             if (mUseQuickControls) {
                 mContentView.addView(mTitleBar);
             } else {
+                if (!mSkipTitleBarAnimations) {
+                    cancelTitleBarAnimation(false);
+                    int visibleHeight = getVisibleTitleHeight();
+                    float startPos = (-mTitleBar.getEmbeddedHeight() + visibleHeight);
+                    if (mTitleBar.getTranslationY() != 0) {
+                        startPos = Math.max(startPos, mTitleBar.getTranslationY());
+                    }
+                    mTitleBarAnimator = ObjectAnimator.ofFloat(mTitleBar,
+                            "translationY",
+                            startPos, 0);
+                    mTitleBarAnimator.start();
+                }
                 setTitleGravity(Gravity.TOP);
             }
             super.showTitleBar();
@@ -331,12 +354,63 @@
             if (mUseQuickControls) {
                 mContentView.removeView(mTitleBar);
             } else {
-                setTitleGravity(Gravity.NO_GRAVITY);
+                if (!mSkipTitleBarAnimations) {
+                    cancelTitleBarAnimation(false);
+                    int visibleHeight = getVisibleTitleHeight();
+                    mTitleBarAnimator = ObjectAnimator.ofFloat(mTitleBar,
+                            "translationY", mTitleBar.getTranslationY(),
+                            (-mTitleBar.getEmbeddedHeight() + visibleHeight));
+                    mTitleBarAnimator.addListener(mHideTileBarAnimatorListener);
+                    mTitleBarAnimator.start();
+                } else {
+                    setTitleGravity(Gravity.NO_GRAVITY);
+                }
             }
             super.hideTitleBar();
         }
     }
 
+    private void cancelTitleBarAnimation(boolean reset) {
+        if (mTitleBarAnimator != null) {
+            mTitleBarAnimator.cancel();
+            mTitleBarAnimator = null;
+        }
+        if (reset) {
+            mTitleBar.setTranslationY(0);
+        }
+    }
+
+    private int getVisibleTitleHeight() {
+        WebView webview = mActiveTab != null ? mActiveTab.getWebView() : null;
+        return webview != null ? webview.getVisibleTitleHeight() : 0;
+    }
+
+    private AnimatorListener mHideTileBarAnimatorListener = new AnimatorListener() {
+
+        boolean mWasCanceled;
+        @Override
+        public void onAnimationStart(Animator animation) {
+            mWasCanceled = false;
+        }
+
+        @Override
+        public void onAnimationRepeat(Animator animation) {
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            if (!mWasCanceled) {
+                mTitleBar.setTranslationY(0);
+            }
+            setTitleGravity(Gravity.NO_GRAVITY);
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            mWasCanceled = true;
+        }
+    };
+
     public boolean isEditingUrl() {
         return mTitleBar.isEditingUrl();
     }
@@ -390,6 +464,11 @@
     }
 
     @Override
+    protected void updateAutoLogin(Tab tab, boolean animate) {
+        mTitleBar.updateAutoLogin(tab, animate);
+    }
+
+    @Override
     public void setUrlTitle(Tab tab) {
         super.setUrlTitle(tab);
         mTabBar.onUrlAndTitle(tab, tab.getUrl(), tab.getTitle());
diff --git a/src/com/android/browser/preferences/PrivacySecurityPreferencesFragment.java b/src/com/android/browser/preferences/PrivacySecurityPreferencesFragment.java
index 2b2ee3e..2266608 100644
--- a/src/com/android/browser/preferences/PrivacySecurityPreferencesFragment.java
+++ b/src/com/android/browser/preferences/PrivacySecurityPreferencesFragment.java
@@ -17,14 +17,11 @@
 package com.android.browser.preferences;
 
 import com.android.browser.BrowserSettings;
-import com.android.browser.GoogleAccountLogin;
 import com.android.browser.R;
 
-import android.accounts.Account;
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
-import android.preference.ListPreference;
 import android.preference.Preference;
 import android.preference.PreferenceFragment;
 
@@ -44,53 +41,11 @@
 
         Preference e = findPreference(BrowserSettings.PREF_CLEAR_HISTORY);
         e.setOnPreferenceChangeListener(this);
-        setupAutoLoginPreference();
     }
 
     @Override
     public void onResume() {
         super.onResume();
-        setupAutoLoginPreference();
-    }
-
-    void setupAutoLoginPreference() {
-        ListPreference autologinPref = (ListPreference) findPreference(
-                BrowserSettings.PREF_AUTOLOGIN_ACCOUNT);
-        autologinPref.setOnPreferenceChangeListener(this);
-        updateAutoLoginSummary(autologinPref);
-        Account[] accounts = GoogleAccountLogin.getAccounts(getActivity());
-        // +1 for disable
-        CharSequence[] names = new CharSequence[accounts.length + 1];
-        CharSequence[] values = new CharSequence[names.length];
-        int i = 0;
-        int defaultAccount = 0;
-        for (Account a : accounts) {
-            values[i] = names[i] = a.name;
-            i++;
-        }
-        names[i] = getResources().getString(R.string.pref_autologin_disable);
-        values[i] = "";
-        autologinPref.setEntries(names);
-        autologinPref.setEntryValues(values);
-        BrowserSettings bs = BrowserSettings.getInstance();
-        if (bs.isAutoLoginEnabled()) {
-            autologinPref.setValue(bs.getAutoLoginAccount(getActivity()));
-        } else {
-            autologinPref.setValue("");
-        }
-    }
-
-    private void updateAutoLoginSummary(Preference pref) {
-        if (!mSettings.isAutoLoginEnabled()) {
-            pref.setSummary(R.string.pref_autologin_disable);
-        } else {
-            String account = mSettings.getAutoLoginAccount(getActivity());
-            if (account == null) {
-                pref.setSummary(R.string.pref_autologin_no_account);
-            } else {
-                pref.setSummary(getString(R.string.pref_autologin_summary, account));
-            }
-        }
     }
 
     @Override
@@ -102,17 +57,6 @@
             getActivity().setResult(Activity.RESULT_OK, (new Intent()).putExtra(Intent.EXTRA_TEXT,
                     pref.getKey()));
             return true;
-        } else if (pref.getKey().equals(BrowserSettings.PREF_AUTOLOGIN_ACCOUNT)) {
-            String account = (String) objValue;
-            if (account.length() == 0) {
-                // Disable
-                mSettings.setAutoLoginEnabled(getActivity(), false);
-            } else {
-                mSettings.setAutoLoginEnabled(getActivity(), true);
-            }
-            mSettings.setAutoLoginAccount(getActivity(), account);
-            updateAutoLoginSummary(pref);
-            return true;
         }
 
         return false;