Merge "Add new auto-login UI." 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/src/com/android/browser/BaseUi.java b/src/com/android/browser/BaseUi.java
index e89012f..5898680 100644
--- a/src/com/android/browser/BaseUi.java
+++ b/src/com/android/browser/BaseUi.java
@@ -238,6 +238,7 @@
         onProgressChanged(tab);
         boolean incognito = mActiveTab.getWebView().isPrivateBrowsingEnabled();
         getTitleBar().setIncognitoMode(incognito);
+        updateAutoLogin(tab, false);
     }
 
     Tab getActiveTab() {
@@ -539,11 +540,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/Controller.java b/src/com/android/browser/Controller.java
index d02f05c..b04c956 100644
--- a/src/com/android/browser/Controller.java
+++ b/src/com/android/browser/Controller.java
@@ -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
 
     /*
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/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 9023f17..7bb9ff0 100644
--- a/src/com/android/browser/XLargeUi.java
+++ b/src/com/android/browser/XLargeUi.java
@@ -445,6 +445,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());