1 package com.xtremelabs.robolectric.shadows;
2 
3 import android.accounts.Account;
4 import android.accounts.AccountManager;
5 import android.accounts.AccountManagerCallback;
6 import android.accounts.AccountManagerFuture;
7 import android.accounts.AuthenticatorException;
8 import android.accounts.OperationCanceledException;
9 import android.app.Activity;
10 import android.content.Context;
11 import android.os.Bundle;
12 import android.os.Handler;
13 
14 import com.xtremelabs.robolectric.Robolectric;
15 import com.xtremelabs.robolectric.internal.Implementation;
16 import com.xtremelabs.robolectric.internal.Implements;
17 import com.xtremelabs.robolectric.internal.RealObject;
18 
19 import java.io.IOException;
20 import java.util.*;
21 import java.util.concurrent.TimeUnit;
22 
23 import static com.xtremelabs.robolectric.Robolectric.newInstanceOf;
24 import static com.xtremelabs.robolectric.Robolectric.shadowOf;
25 
26 /**
27  * Shadows the {@code android.accounts.AccountManager} class.
28  */
29 @SuppressWarnings({"UnusedDeclaration"})
30 @Implements(AccountManager.class)
31 public class ShadowAccountManager {
32 
33     public static final String AUTH_TOKEN_VALUE = "authToken";
34 
35     private static AccountManager singleton;
36 
37     private Account[] accounts;
38     private HashMap<Account, HashMap<String, String>> cachedAuthTokenValues =
39             new HashMap<Account, HashMap<String, String>>();
40 
41     @Implementation
get(Context context)42     public static AccountManager get(Context context) {
43         if (singleton == null) {
44             singleton = Robolectric.newInstanceOf(AccountManager.class);
45         }
46         return singleton;
47     }
48 
49     @Implementation
getAuthToken(Account account, String authTokenType, Bundle options, Activity activity, AccountManagerCallback<Bundle> callback, Handler handler)50     public AccountManagerFuture<Bundle> getAuthToken(Account account, String authTokenType, Bundle options, Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
51         //TODO: Add complete activity to perform the account intent dance.
52         final Account finalAccount = account;
53         return new AccountManagerFuture<Bundle>() {
54 
55             private boolean isFutureCancelled;
56             private boolean isFutureDone;
57 
58             @Override
59             public boolean cancel(boolean mayInterruptIfRunning) {
60                 if (isFutureDone) {
61                     return false;
62                 }
63                 isFutureCancelled = true;
64                 return isCancelled();
65             }
66 
67             @Override
68             public Bundle getResult(long timeout, TimeUnit unit) throws OperationCanceledException,
69                     AuthenticatorException, IOException {
70                 Bundle result = new Bundle();
71                 if (!isCancelled()) {
72                     addBundleResults(result, finalAccount);
73                     isFutureDone = true;
74                 }
75                 return result;
76             }
77 
78             @Override
79             public Bundle getResult() throws OperationCanceledException,
80                     AuthenticatorException, IOException {
81                 Bundle result = new Bundle();
82                 if (!isCancelled()) {
83                     addBundleResults(result, finalAccount);
84                     isFutureDone = true;
85                 }
86                 return result;
87             }
88 
89             @Override
90             public boolean isCancelled() {
91                 return isFutureCancelled;
92             }
93 
94             @Override
95             public boolean isDone() {
96                 return isFutureDone || isFutureCancelled;
97             }
98 
99             private void addBundleResults(Bundle bundle, final Account account) {
100                 bundle.putString(AccountManager.KEY_AUTHTOKEN, AUTH_TOKEN_VALUE);
101                 bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
102                 bundle.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
103             }
104         };
105     }
106 
107     @Implementation
getAuthTokenByFeatures(String accountType, String authTokenType, String[] features, Activity activity, Bundle addAccountOptions, Bundle getAuthTokenOptions, AccountManagerCallback<Bundle> callback, Handler handler)108     public AccountManagerFuture<Bundle> getAuthTokenByFeatures(String accountType, String authTokenType, String[] features, Activity activity, Bundle addAccountOptions, Bundle getAuthTokenOptions, AccountManagerCallback<Bundle> callback, Handler handler) {
109         //TODO: Add complete activity to perform the account intent dance.
110         final String finalAccountType = accountType;
111         return new AccountManagerFuture<Bundle>() {
112 
113             private boolean isFutureCancelled;
114             private boolean isFutureDone;
115 
116             @Override
117             public boolean cancel(boolean mayInterruptIfRunning) {
118                 if (isFutureDone) {
119                     return false;
120                 }
121                 isFutureCancelled = true;
122                 return isCancelled();
123             }
124 
125             @Override
126             public Bundle getResult(long timeout, TimeUnit unit) throws OperationCanceledException,
127                     AuthenticatorException, IOException {
128                 Bundle result = new Bundle();
129                 if (!isCancelled()) {
130                     addBundleResults(result, finalAccountType);
131                     isFutureDone = true;
132                 }
133                 return result;
134             }
135 
136             @Override
137             public Bundle getResult() throws OperationCanceledException,
138                     AuthenticatorException, IOException {
139                 Bundle result = new Bundle();
140                 if (!isCancelled()) {
141                     addBundleResults(result, finalAccountType);
142                     isFutureDone = true;
143                 }
144                 return result;
145             }
146 
147             @Override
148             public boolean isCancelled() {
149                 return isFutureCancelled;
150             }
151 
152             @Override
153             public boolean isDone() {
154                 return isFutureDone || isFutureCancelled;
155             }
156 
157             private void addBundleResults(Bundle bundle, final String accountType) {
158                 bundle.putString(AccountManager.KEY_AUTHTOKEN, AUTH_TOKEN_VALUE);
159                 bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, accountType);
160                 bundle.putString(AccountManager.KEY_ACCOUNT_NAME, "accountName");
161             }
162         };
163     }
164 
165     @Implementation
166     public void invalidateAuthToken(String accountType, String authToken) {}
167 
168     @Implementation
169     public Account[] getAccounts() {
170         return getAccountsByType(null);
171     }
172 
173     @Implementation
174     public Account[] getAccountsByType(String accountType) {
175         if (accountType == null) {
176             return accounts;
177         }
178 
179         ArrayList<Account> accountList = new ArrayList<Account>();
180         if (accounts != null) {
181             for (Account account : accounts) {
182                 if (accountType.equals(account.type)) {
183                     accountList.add(account);
184                 }
185             }
186         }
187         return accountList.toArray(new Account[accountList.size()]);
188     }
189 
190     @Implementation
191     public String peekAuthToken(Account account, String authTokenType) {
192         HashMap<String, String> tokens = cachedAuthTokenValues.get(account);
193         return (tokens != null) ? tokens.get(authTokenType) : null;
194     }
195 
196     public void setCachedAuthToken(Account account, String authTokenType, String authTokenValue) {
197         if (!cachedAuthTokenValues.containsKey(account)) {
198             cachedAuthTokenValues.put(account, new HashMap<String, String>());
199         }
200         cachedAuthTokenValues.get(account).put(authTokenType, authTokenValue);
201     }
202 
203     public void setAccounts(Account[] accounts) {
204         this.accounts = accounts;
205     }
206 }
207