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());