a sample app that uses the AccountManager API

This commit is contained in:
Fred Quintana
2009-06-15 17:23:00 -07:00
parent cb4821d09f
commit ac657e02c0
7 changed files with 664 additions and 0 deletions

View File

@@ -73,6 +73,14 @@
</intent-filter>
</activity>
<activity android:name="AccountsTester" android:label="AccountsTester"
android:theme="@android:style/Theme.Light">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.TEST" />
</intent-filter>
</activity>
<activity android:name="DataList">
</activity>
<activity android:name="Details">

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 Google Inc.
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.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/accounts_tester_remove_account"
android:title="@string/accounts_tester_remove_account" />
<item android:id="@+id/accounts_tester_get_auth_token"
android:title="@string/accounts_tester_get_auth_token" />
<item android:id="@+id/accounts_tester_invalidate_auth_token"
android:title="@string/accounts_tester_invalidate_auth_token" />
<item android:id="@+id/accounts_tester_update_credentials"
android:title="@string/accounts_tester_update_credentials" />
<item android:id="@+id/accounts_tester_confirm_credentials"
android:title="@string/accounts_tester_confirm_credentials" />
</menu>

View File

@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView android:id="@+id/accounts_tester_account_types_spinner_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/accounts_tester_select_account_type"/>
<Spinner android:id="@+id/accounts_tester_account_types_spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<ListView android:id="@+id/accounts_tester_authenticators_list"
android:layout_width="fill_parent" android:layout_height="fill_parent"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/accounts_tester_get_accounts_by_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/accounts_tester_get_accounts_by_type"/>
<Button
android:id="@+id/accounts_tester_get_all_accounts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/accounts_tester_get_all_accounts"/>
<Button android:id="@+id/accounts_tester_add_account"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/accounts_tester_add_account"/>
<Button android:id="@+id/accounts_tester_edit_properties"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/accounts_tester_edit_properties"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ListView android:id="@+id/accounts_tester_accounts_list"
android:layout_width="fill_parent" android:layout_height="fill_parent"/>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
* Copyright (C) 2008 Esmertec AG.
* Copyright (C) 2008 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.
*/
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingTop="1dip"
android:paddingBottom="1dip"
android:paddingLeft="9dip"
android:paddingRight="9dip">
<ImageView android:id="@+id/accounts_tester_authenticator_icon"
android:paddingRight="9dip"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView android:id="@+id/accounts_tester_authenticator_label"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:gravity="center_vertical"
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_gravity="center_horizontal|center_vertical" />
</LinearLayout>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:id="@+id/accounts_tester_get_auth_token_dialog_message"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:text="@string/accounts_tester_enter_auth_token_type" />
<EditText android:id="@+id/accounts_tester_auth_token_type"
android:singleLine="true"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minWidth="250dip"
android:scrollHorizontally="true"
android:capitalize="none"
android:autoText="false"/>
</LinearLayout>

View File

@@ -133,4 +133,21 @@
<string name="perm_list_header_text">Apps using permission </string>
<string name="source_uid_text">Source uid : </string>
<string name="shared_pkgs_text">Packages accessing via shared uid : </string>
<!-- AccountsTester -->
<string name="accounts_tester_app_name">Accounts Tester</string>
<string name="accounts_tester_get_accounts_by_type">Get Accounts By Type</string>
<string name="accounts_tester_get_all_accounts">Get All Accounts</string>
<string name="accounts_tester_add_account">Add Account</string>
<string name="accounts_tester_select_account_type">Select Account Type</string>
<string name="accounts_tester_process_name_header">Process Name:</string>
<string name="accounts_tester_remove_account">remove</string>
<string name="accounts_tester_get_auth_token">authenticate</string>
<string name="accounts_tester_invalidate_auth_token">invalidate token</string>
<string name="accounts_tester_account_context_menu_title">account operations</string>
<string name="accounts_tester_do_get_auth_token">Ok</string>
<string name="accounts_tester_enter_auth_token_type">Enter the authtoken type:</string>
<string name="accounts_tester_update_credentials">Update Credentials</string>
<string name="accounts_tester_confirm_credentials">Confirm Credentials</string>
<string name="accounts_tester_edit_properties">Edit Properties</string>
</resources>

View File

@@ -0,0 +1,442 @@
/*
* Copyright (C) 2009 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.development;
import android.app.Activity;
import android.app.Dialog;
import android.app.AlertDialog;
import android.content.*;
import android.content.pm.PackageManager;
import android.accounts.*;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.Handler;
import android.view.*;
import android.widget.*;
import android.widget.ArrayAdapter;
import android.util.Log;
import android.text.TextUtils;
import java.io.IOException;
public class AccountsTester extends Activity implements OnAccountsUpdatedListener {
private static final String TAG = "AccountsTester";
private Spinner mAccountTypesSpinner;
private ListView mAccountsListView;
private ListView mAuthenticatorsListView;
private AccountManager mAccountManager;
private String mLongPressedAccount = null;
private Future1Callback<Account[]> mGetAccountsCallback;
private static final String COM_GOOGLE_GAIA = "com.google.GAIA";
private AuthenticatorDescription[] mAuthenticatorDescs;
private static final int GET_AUTH_TOKEN_DIALOG_ID = 1;
private static final int UPDATE_CREDENTIALS_DIALOG_ID = 2;
private static final int INVALIDATE_AUTH_TOKEN_DIALOG_ID = 3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAccountManager = AccountManager.get(this);
setContentView(R.layout.accounts_tester);
ButtonClickListener buttonClickListener = new ButtonClickListener();
mGetAccountsCallback = new Future1Callback<Account[]>() {
public void run(Future1<Account[]> future) {
Log.d(TAG, "mGetAccountsCallback: starting");
try {
Account[] accounts = future.getResult();
onAccountsUpdated(accounts);
} catch (OperationCanceledException e) {
// the request was canceled
Log.d(TAG, "mGetAccountsCallback: request was canceled", e);
}
}
};
mAccountTypesSpinner = (Spinner) findViewById(R.id.accounts_tester_account_types_spinner);
mAccountsListView = (ListView) findViewById(R.id.accounts_tester_accounts_list);
mAuthenticatorsListView = (ListView) findViewById(R.id.accounts_tester_authenticators_list);
registerForContextMenu(mAccountsListView);
asyncGetAuthenticatorTypes();
findViewById(R.id.accounts_tester_get_all_accounts).setOnClickListener(buttonClickListener);
findViewById(R.id.accounts_tester_get_accounts_by_type).setOnClickListener(
buttonClickListener);
findViewById(R.id.accounts_tester_add_account).setOnClickListener(buttonClickListener);
findViewById(R.id.accounts_tester_edit_properties).setOnClickListener(buttonClickListener);
}
private static class AuthenticatorsArrayAdapter extends ArrayAdapter<AuthenticatorDescription> {
protected LayoutInflater mInflater;
private static final int mResource = R.layout.authenticators_list_item;
public AuthenticatorsArrayAdapter(Context context, AuthenticatorDescription[] items) {
super(context, mResource, items);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
static class ViewHolder {
TextView label;
ImageView icon;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// A ViewHolder keeps references to children views to avoid unneccessary calls
// to findViewById() on each row.
ViewHolder holder;
// When convertView is not null, we can reuse it directly, there is no need
// to reinflate it. We only inflate a new View when the convertView supplied
// by ListView is null.
if (convertView == null) {
convertView = mInflater.inflate(R.layout.authenticators_list_item, null);
// Creates a ViewHolder and store references to the two children views
// we want to bind data to.
holder = new ViewHolder();
holder.label = (TextView) convertView.findViewById(
R.id.accounts_tester_authenticator_label);
holder.icon = (ImageView) convertView.findViewById(
R.id.accounts_tester_authenticator_icon);
convertView.setTag(holder);
} else {
// Get the ViewHolder back to get fast access to the TextView
// and the ImageView.
holder = (ViewHolder) convertView.getTag();
}
final AuthenticatorDescription desc = getItem(position);
final String packageName = desc.packageName;
try {
final Context authContext = getContext().createPackageContext(packageName, 0);
// Set text field
holder.label.setText(authContext.getString(desc.labelId));
// Set resource icon
holder.icon.setImageDrawable(authContext.getResources().getDrawable(desc.iconId));
} catch (PackageManager.NameNotFoundException e) {
Log.d(TAG, "error getting the Package Context for " + packageName, e);
}
return convertView;
}
}
private void asyncGetAuthenticatorTypes() {
mAccountManager.getAuthenticatorTypes(new GetAuthenticatorsCallback(), null /* handler */);
}
public void onAccountsUpdated(Account[] accounts) {
Log.d(TAG, "onAccountsUpdated: \n " + TextUtils.join("\n ", accounts));
String[] accountNames = new String[accounts.length];
for (int i = 0; i < accounts.length; i++) {
accountNames[i] = accounts[i].mName;
}
ArrayAdapter<String> adapter =
new ArrayAdapter<String>(AccountsTester.this,
android.R.layout.simple_list_item_1, accountNames);
mAccountsListView.setAdapter(adapter);
}
protected void onStart() {
super.onStart();
final Handler mainHandler = new Handler(getMainLooper());
mAccountManager.addOnAccountsUpdatedListener(this, mainHandler,
true /* updateImmediately */);
}
protected void onStop() {
super.onStop();
mAccountManager.removeOnAccountsUpdatedListener(this);
}
class ButtonClickListener implements View.OnClickListener {
public void onClick(View v) {
if (R.id.accounts_tester_get_all_accounts == v.getId()) {
mAccountManager.getAccounts(mGetAccountsCallback, null /* handler */);
} else if (R.id.accounts_tester_get_accounts_by_type == v.getId()) {
String type = getSelectedAuthenticator().type;
mAccountManager.getAccountsByType(mGetAccountsCallback, type,
null /* handler */);
} else if (R.id.accounts_tester_add_account == v.getId()) {
Future2Callback callback = new Future2Callback() {
public void run(Future2 future) {
try {
Bundle bundle = future.getResult();
bundle.keySet();
Log.d(TAG, "account added: " + bundle);
} catch (OperationCanceledException e) {
Log.d(TAG, "addAccount was canceled");
} catch (IOException e) {
Log.d(TAG, "addAccount failed: " + e);
} catch (AuthenticatorException e) {
Log.d(TAG, "addAccount failed: " + e);
}
}
};
mAccountManager.addAccount(getSelectedAuthenticator().type,
null /* authTokenType */,
null /* requiredFeatures */, null /* options */,
AccountsTester.this, callback, null /* handler */);
} else if (R.id.accounts_tester_edit_properties == v.getId()) {
mAccountManager.editProperties(getSelectedAuthenticator().type,
AccountsTester.this, new EditPropertiesCallback(), null /* handler */);
} else {
// unknown button
}
}
private class EditPropertiesCallback implements Future2Callback {
public void run(Future2 future) {
try {
Bundle bundle = future.getResult();
bundle.keySet();
Log.d(TAG, "editProperties succeeded: " + bundle);
} catch (OperationCanceledException e) {
Log.d(TAG, "editProperties was canceled");
} catch (IOException e) {
Log.d(TAG, "editProperties failed: ", e);
} catch (AuthenticatorException e) {
Log.d(TAG, "editProperties failed: ", e);
}
}
}
}
private AuthenticatorDescription getSelectedAuthenticator() {
return mAuthenticatorDescs[mAccountTypesSpinner.getSelectedItemPosition()];
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenu.ContextMenuInfo menuInfo) {
menu.setHeaderTitle(R.string.accounts_tester_account_context_menu_title);
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)menuInfo;
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.layout.account_list_context_menu, menu);
mLongPressedAccount = ((TextView)info.targetView).getText().toString();
}
@Override
public boolean onContextItemSelected(MenuItem item) {
if (item.getItemId() == R.id.accounts_tester_remove_account) {
mAccountManager.removeAccount(null /* callback */, new Account(mLongPressedAccount,
COM_GOOGLE_GAIA), null /* handler */);
} else if (item.getItemId() == R.id.accounts_tester_get_auth_token) {
showDialog(GET_AUTH_TOKEN_DIALOG_ID);
} else if (item.getItemId() == R.id.accounts_tester_invalidate_auth_token) {
showDialog(INVALIDATE_AUTH_TOKEN_DIALOG_ID);
} else if (item.getItemId() == R.id.accounts_tester_update_credentials) {
showDialog(UPDATE_CREDENTIALS_DIALOG_ID);
} else if (item.getItemId() == R.id.accounts_tester_confirm_credentials) {
mAccountManager.confirmCredentials(new Account(mLongPressedAccount, COM_GOOGLE_GAIA),
AccountsTester.this, new ConfirmCredentialsCallback(), null /* handler */);
}
return true;
}
@Override
protected Dialog onCreateDialog(final int id) {
if (id == GET_AUTH_TOKEN_DIALOG_ID || id == INVALIDATE_AUTH_TOKEN_DIALOG_ID
|| id == UPDATE_CREDENTIALS_DIALOG_ID) {
final View view = LayoutInflater.from(this).inflate(R.layout.get_auth_token_view, null);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setPositiveButton(R.string.accounts_tester_do_get_auth_token,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
EditText value = (EditText) view.findViewById(
R.id.accounts_tester_auth_token_type);
String authTokenType = value.getText().toString();
Future2Callback callback = new Future2Callback() {
public void run(Future2 future) {
try {
Bundle bundle = future.getResult();
bundle.keySet();
Log.d(TAG, "dialog " + id + " success: " + bundle);
} catch (OperationCanceledException e) {
Log.d(TAG, "dialog " + id + " canceled");
} catch (IOException e) {
Log.d(TAG, "dialog " + id + " failed: " + e);
} catch (AuthenticatorException e) {
Log.d(TAG, "dialog " + id + " failed: " + e);
}
}
};
final Account account = new Account(mLongPressedAccount,
COM_GOOGLE_GAIA);
if (id == GET_AUTH_TOKEN_DIALOG_ID) {
mAccountManager.getAuthToken(account, authTokenType,
null /* loginOptions */, AccountsTester.this,
callback, null /* handler */);
} else if (id == INVALIDATE_AUTH_TOKEN_DIALOG_ID) {
mAccountManager.getAuthToken(account, authTokenType, false,
new GetAndInvalidateAuthTokenCallback(), null);
} else {
mAccountManager.updateCredentials(
account,
authTokenType, null /* loginOptions */,
AccountsTester.this, callback, null /* handler */);
}
}
});
builder.setView(view);
return builder.create();
}
return super.onCreateDialog(id);
}
Future2Callback newAccountsCallback(String type, String[] features) {
return new GetAccountsCallback(type, features);
}
class GetAccountsCallback implements Future2Callback {
final String[] mFeatures;
final String mAccountType;
public GetAccountsCallback(String type, String[] features) {
mFeatures = features;
mAccountType = type;
}
public void run(Future2 future) {
Log.d(TAG, "GetAccountsCallback: type " + mAccountType
+ ", features "
+ (mFeatures == null ? "none" : TextUtils.join(",", mFeatures)));
try {
Bundle result = future.getResult();
Parcelable[] accounts = result.getParcelableArray(Constants.ACCOUNTS_KEY);
Log.d(TAG, "found " + accounts.length + " accounts");
for (Parcelable account : accounts) {
Log.d(TAG, " " + account);
}
} catch (OperationCanceledException e) {
Log.d(TAG, "failure", e);
} catch (IOException e) {
Log.d(TAG, "failure", e);
} catch (AuthenticatorException e) {
Log.d(TAG, "failure", e);
}
}
}
Future2Callback newAuthTokensCallback(String type, String authTokenType, String[] features) {
return new GetAuthTokenCallback(type, authTokenType, features);
}
class GetAuthTokenCallback implements Future2Callback {
final String[] mFeatures;
final String mAccountType;
final String mAuthTokenType;
public GetAuthTokenCallback(String type, String authTokenType, String[] features) {
mFeatures = features;
mAccountType = type;
mAuthTokenType = authTokenType;
}
public void run(Future2 future) {
Log.d(TAG, "GetAuthTokenCallback: type " + mAccountType
+ ", features "
+ (mFeatures == null ? "none" : TextUtils.join(",", mFeatures)));
try {
Bundle result = future.getResult();
result.keySet();
Log.d(TAG, " result: " + result);
} catch (OperationCanceledException e) {
Log.d(TAG, "failure", e);
} catch (IOException e) {
Log.d(TAG, "failure", e);
} catch (AuthenticatorException e) {
Log.d(TAG, "failure", e);
}
}
}
private class GetAndInvalidateAuthTokenCallback implements Future2Callback {
public void run(Future2 future) {
try {
Bundle bundle = future.getResult();
String authToken = bundle.getString(Constants.AUTHTOKEN_KEY);
mAccountManager.invalidateAuthToken(new Future1Callback<Void>() {
public void run(Future1<Void> future) {
try {
future.getResult();
} catch (OperationCanceledException e) {
// the request was canceled
}
}
}, COM_GOOGLE_GAIA, authToken, null);
} catch (OperationCanceledException e) {
Log.d(TAG, "invalidate: interrupted while getting authToken");
} catch (IOException e) {
Log.d(TAG, "invalidate: error getting authToken", e);
} catch (AuthenticatorException e) {
Log.d(TAG, "invalidate: error getting authToken", e);
}
}
}
private static class ConfirmCredentialsCallback implements Future2Callback {
public void run(Future2 future) {
try {
Bundle bundle = future.getResult();
bundle.keySet();
Log.d(TAG, "confirmCredentials success: " + bundle);
} catch (OperationCanceledException e) {
Log.d(TAG, "confirmCredentials canceled");
} catch (AuthenticatorException e) {
Log.d(TAG, "confirmCredentials failed: " + e);
} catch (IOException e) {
Log.d(TAG, "confirmCredentials failed: " + e);
}
}
}
private class GetAuthenticatorsCallback implements Future1Callback<AuthenticatorDescription[]> {
public void run(Future1<AuthenticatorDescription[]> future) {
if (isFinishing()) return;
try {
mAuthenticatorDescs = future.getResult();
String[] names = new String[mAuthenticatorDescs.length];
for (int i = 0; i < mAuthenticatorDescs.length; i++) {
Context authContext;
try {
authContext = createPackageContext(mAuthenticatorDescs[i].packageName, 0);
} catch (PackageManager.NameNotFoundException e) {
continue;
}
names[i] = authContext.getString(mAuthenticatorDescs[i].labelId);
}
ArrayAdapter<String> adapter =
new ArrayAdapter<String>(AccountsTester.this,
android.R.layout.simple_spinner_item, names);
mAccountTypesSpinner.setAdapter(adapter);
mAuthenticatorsListView.setAdapter(new AuthenticatorsArrayAdapter(
AccountsTester.this, mAuthenticatorDescs));
} catch (OperationCanceledException e) {
// the request was canceled
}
}
}
}