Skip to content

Commit 3d5759b

Browse files
fredquintanaAndroid (Google) Code Review
authored andcommitted
Merge "Add a generic account chooser/add account flow for apps." into ics-factoryrom
2 parents ebdcae7 + 1121bb5 commit 3d5759b

File tree

7 files changed

+600
-0
lines changed

7 files changed

+600
-0
lines changed

api/14.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2072,6 +2072,7 @@ package android.accounts {
20722072
method public java.lang.String getUserData(android.accounts.Account, java.lang.String);
20732073
method public android.accounts.AccountManagerFuture<java.lang.Boolean> hasFeatures(android.accounts.Account, java.lang.String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
20742074
method public void invalidateAuthToken(java.lang.String, java.lang.String);
2075+
method public static android.content.Intent newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, java.lang.String[], android.os.Bundle);
20752076
method public java.lang.String peekAuthToken(android.accounts.Account, java.lang.String);
20762077
method public android.accounts.AccountManagerFuture<java.lang.Boolean> removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
20772078
method public void removeOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener);

api/current.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2072,6 +2072,7 @@ package android.accounts {
20722072
method public java.lang.String getUserData(android.accounts.Account, java.lang.String);
20732073
method public android.accounts.AccountManagerFuture<java.lang.Boolean> hasFeatures(android.accounts.Account, java.lang.String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
20742074
method public void invalidateAuthToken(java.lang.String, java.lang.String);
2075+
method public static android.content.Intent newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, java.lang.String[], android.os.Bundle);
20752076
method public java.lang.String peekAuthToken(android.accounts.Account, java.lang.String);
20762077
method public android.accounts.AccountManagerFuture<java.lang.Boolean> removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
20772078
method public void removeOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener);

core/java/android/accounts/AccountManager.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import android.text.TextUtils;
3333

3434
import java.io.IOException;
35+
import java.util.ArrayList;
3536
import java.util.concurrent.Callable;
3637
import java.util.concurrent.CancellationException;
3738
import java.util.concurrent.ExecutionException;
@@ -41,6 +42,7 @@
4142
import java.util.HashMap;
4243
import java.util.Map;
4344

45+
import com.google.android.collect.Lists;
4446
import com.google.android.collect.Maps;
4547

4648
/**
@@ -1769,6 +1771,44 @@ public AccountManagerFuture<Bundle> getAuthTokenByFeatures(
17691771
return task;
17701772
}
17711773

1774+
/**
1775+
* Returns an intent to an {@link Activity} that prompts the user to choose from a list of
1776+
* accounts.
1777+
* The caller will then typically start the activity by calling
1778+
* <code>startActivityWithResult(intent, ...);</code>.
1779+
* <p>
1780+
* On success the activity returns a Bundle with the account name and type specified using
1781+
* keys {@link #KEY_ACCOUNT_NAME} and {@link #KEY_ACCOUNT_TYPE}.
1782+
* <p>
1783+
* The most common case is to call this with one account type, e.g.:
1784+
* <p>
1785+
* <pre> newChooseAccountsIntent(null, null, new String[]{"com.google"}, null);</pre>
1786+
* @param selectedAccount if specified, indicates that the {@link Account} is the currently
1787+
* selected one, according to the caller's definition of selected.
1788+
* @param allowableAccounts an optional {@link ArrayList} of accounts that are allowed to be
1789+
* shown. If not specified then this field will not limit the displayed accounts.
1790+
* @param allowableAccountTypes an optional string array of account types. These are used
1791+
* both to filter the shown accounts and to filter the list of account types that are shown
1792+
* when adding an account.
1793+
* @param addAccountOptions This {@link Bundle} is passed as the addAccount options
1794+
* @return an {@link Intent} that can be used to launch the ChooseAccount activity flow.
1795+
*/
1796+
static public Intent newChooseAccountIntent(Account selectedAccount,
1797+
ArrayList<Account> allowableAccounts,
1798+
String[] allowableAccountTypes,
1799+
Bundle addAccountOptions) {
1800+
Intent intent = new Intent();
1801+
intent.setClassName("android", "android.accounts.ChooseTypeAndAccountActivity");
1802+
intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ALLOWABLE_ACCOUNTS_ARRAYLIST,
1803+
allowableAccounts);
1804+
intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ALLOWABLE_ACCOUNT_TYPES_ARRAYLIST,
1805+
allowableAccountTypes != null ? Lists.newArrayList(allowableAccountTypes) : 0);
1806+
intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE,
1807+
addAccountOptions);
1808+
intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_SELECTED_ACCOUNT, selectedAccount);
1809+
return intent;
1810+
}
1811+
17721812
private final HashMap<OnAccountsUpdateListener, Handler> mAccountsUpdatedListeners =
17731813
Maps.newHashMap();
17741814

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
/*
2+
* Copyright (C) 2011 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package android.accounts;
17+
18+
import android.app.Activity;
19+
import android.content.Context;
20+
import android.content.Intent;
21+
import android.content.pm.PackageManager;
22+
import android.content.res.Resources;
23+
import android.graphics.drawable.Drawable;
24+
import android.os.Bundle;
25+
import android.util.Log;
26+
import android.view.LayoutInflater;
27+
import android.view.View;
28+
import android.view.ViewGroup;
29+
import android.widget.AdapterView;
30+
import android.widget.ArrayAdapter;
31+
import android.widget.ImageView;
32+
import android.widget.ListView;
33+
import android.widget.TextView;
34+
import com.android.internal.R;
35+
36+
import java.io.IOException;
37+
import java.util.ArrayList;
38+
import java.util.HashMap;
39+
import java.util.HashSet;
40+
import java.util.Map;
41+
import java.util.Set;
42+
43+
/**
44+
* @hide
45+
*/
46+
public class ChooseAccountTypeActivity extends Activity implements AccountManagerCallback<Bundle> {
47+
private static final String TAG = "AccountManager";
48+
49+
private HashMap<String, AuthInfo> mTypeToAuthenticatorInfo = new HashMap<String, AuthInfo>();
50+
private ArrayList<AuthInfo> mAuthenticatorInfosToDisplay;
51+
52+
@Override
53+
public void onCreate(Bundle savedInstanceState) {
54+
super.onCreate(savedInstanceState);
55+
setContentView(R.layout.choose_account);
56+
57+
// Read the validAccountTypes, if present, and add them to the setOfAllowableAccountTypes
58+
Set<String> setOfAllowableAccountTypes = null;
59+
ArrayList<String> validAccountTypes = getIntent().getStringArrayListExtra(
60+
ChooseTypeAndAccountActivity.EXTRA_ALLOWABLE_ACCOUNT_TYPES_ARRAYLIST);
61+
if (validAccountTypes != null) {
62+
setOfAllowableAccountTypes = new HashSet<String>(validAccountTypes.size());
63+
for (String type : validAccountTypes) {
64+
setOfAllowableAccountTypes.add(type);
65+
}
66+
}
67+
68+
// create a map of account authenticators
69+
buildTypeToAuthDescriptionMap();
70+
71+
// Create a list of authenticators that are allowable. Filter out those that
72+
// don't match the allowable account types, if provided.
73+
mAuthenticatorInfosToDisplay = new ArrayList<AuthInfo>(mTypeToAuthenticatorInfo.size());
74+
for (Map.Entry<String, AuthInfo> entry: mTypeToAuthenticatorInfo.entrySet()) {
75+
final String type = entry.getKey();
76+
final AuthInfo info = entry.getValue();
77+
if (setOfAllowableAccountTypes != null
78+
&& !setOfAllowableAccountTypes.contains(type)) {
79+
continue;
80+
}
81+
mAuthenticatorInfosToDisplay.add(info);
82+
}
83+
84+
if (mAuthenticatorInfosToDisplay.isEmpty()) {
85+
Bundle bundle = new Bundle();
86+
bundle.putString(AccountManager.KEY_ERROR_MESSAGE, "no allowable account types");
87+
setResult(Activity.RESULT_OK, new Intent().putExtras(bundle));
88+
finish();
89+
return;
90+
}
91+
92+
if (mAuthenticatorInfosToDisplay.size() == 1) {
93+
runAddAccountForAuthenticator(mAuthenticatorInfosToDisplay.get(0));
94+
return;
95+
}
96+
97+
// Setup the list
98+
ListView list = (ListView) findViewById(android.R.id.list);
99+
// Use an existing ListAdapter that will map an array of strings to TextViews
100+
list.setAdapter(new AccountArrayAdapter(this,
101+
android.R.layout.simple_list_item_1, mAuthenticatorInfosToDisplay));
102+
list.setChoiceMode(ListView.CHOICE_MODE_NONE);
103+
list.setTextFilterEnabled(false);
104+
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
105+
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
106+
runAddAccountForAuthenticator(mAuthenticatorInfosToDisplay.get(position));
107+
}
108+
});
109+
}
110+
111+
private void buildTypeToAuthDescriptionMap() {
112+
for(AuthenticatorDescription desc : AccountManager.get(this).getAuthenticatorTypes()) {
113+
String name = null;
114+
Drawable icon = null;
115+
try {
116+
Context authContext = createPackageContext(desc.packageName, 0);
117+
icon = authContext.getResources().getDrawable(desc.iconId);
118+
final CharSequence sequence = authContext.getResources().getText(desc.labelId);
119+
if (sequence != null) {
120+
name = sequence.toString();
121+
}
122+
name = sequence.toString();
123+
} catch (PackageManager.NameNotFoundException e) {
124+
// Nothing we can do much here, just log
125+
if (Log.isLoggable(TAG, Log.WARN)) {
126+
Log.w(TAG, "No icon name for account type " + desc.type);
127+
}
128+
} catch (Resources.NotFoundException e) {
129+
// Nothing we can do much here, just log
130+
if (Log.isLoggable(TAG, Log.WARN)) {
131+
Log.w(TAG, "No icon resource for account type " + desc.type);
132+
}
133+
}
134+
AuthInfo authInfo = new AuthInfo(desc, name, icon);
135+
mTypeToAuthenticatorInfo.put(desc.type, authInfo);
136+
}
137+
}
138+
139+
protected void runAddAccountForAuthenticator(AuthInfo authInfo) {
140+
Log.d(TAG, "selected account type " + authInfo.name);
141+
Bundle options = getIntent().getBundleExtra(
142+
ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE);
143+
AccountManager.get(this).addAccount(authInfo.desc.type, null, null, options,
144+
this, this, null);
145+
}
146+
147+
public void run(final AccountManagerFuture<Bundle> accountManagerFuture) {
148+
try {
149+
Bundle accountManagerResult = accountManagerFuture.getResult();
150+
Bundle bundle = new Bundle();
151+
bundle.putString(AccountManager.KEY_ACCOUNT_NAME,
152+
accountManagerResult.getString(AccountManager.KEY_ACCOUNT_NAME));
153+
bundle.putString(AccountManager.KEY_ACCOUNT_TYPE,
154+
accountManagerResult.getString(AccountManager.KEY_ACCOUNT_TYPE));
155+
setResult(Activity.RESULT_OK, new Intent().putExtras(bundle));
156+
finish();
157+
return;
158+
} catch (OperationCanceledException e) {
159+
setResult(Activity.RESULT_CANCELED);
160+
finish();
161+
return;
162+
} catch (IOException e) {
163+
} catch (AuthenticatorException e) {
164+
}
165+
Bundle bundle = new Bundle();
166+
bundle.putString(AccountManager.KEY_ERROR_MESSAGE, "error communicating with server");
167+
setResult(Activity.RESULT_OK, new Intent().putExtras(bundle));
168+
finish();
169+
}
170+
171+
private static class AuthInfo {
172+
final AuthenticatorDescription desc;
173+
final String name;
174+
final Drawable drawable;
175+
176+
AuthInfo(AuthenticatorDescription desc, String name, Drawable drawable) {
177+
this.desc = desc;
178+
this.name = name;
179+
this.drawable = drawable;
180+
}
181+
}
182+
183+
private static class ViewHolder {
184+
ImageView icon;
185+
TextView text;
186+
}
187+
188+
private static class AccountArrayAdapter extends ArrayAdapter<AuthInfo> {
189+
private LayoutInflater mLayoutInflater;
190+
private ArrayList<AuthInfo> mInfos;
191+
192+
public AccountArrayAdapter(Context context, int textViewResourceId,
193+
ArrayList<AuthInfo> infos) {
194+
super(context, textViewResourceId, infos);
195+
mInfos = infos;
196+
mLayoutInflater = (LayoutInflater) context.getSystemService(
197+
Context.LAYOUT_INFLATER_SERVICE);
198+
}
199+
200+
@Override
201+
public View getView(int position, View convertView, ViewGroup parent) {
202+
ViewHolder holder;
203+
204+
if (convertView == null) {
205+
convertView = mLayoutInflater.inflate(R.layout.choose_account_row, null);
206+
holder = new ViewHolder();
207+
holder.text = (TextView) convertView.findViewById(R.id.account_row_text);
208+
holder.icon = (ImageView) convertView.findViewById(R.id.account_row_icon);
209+
convertView.setTag(holder);
210+
} else {
211+
holder = (ViewHolder) convertView.getTag();
212+
}
213+
214+
holder.text.setText(mInfos.get(position).name);
215+
holder.icon.setImageDrawable(mInfos.get(position).drawable);
216+
217+
return convertView;
218+
}
219+
}
220+
}

0 commit comments

Comments
 (0)