1package com.xtremelabs.robolectric.shadows;
2
3import android.accounts.Account;
4import android.accounts.AccountManager;
5import android.accounts.AccountManagerCallback;
6import android.accounts.AccountManagerFuture;
7import android.accounts.AuthenticatorException;
8import android.accounts.OperationCanceledException;
9import android.app.Activity;
10import android.content.Context;
11import android.os.Bundle;
12import android.os.Handler;
13
14import com.xtremelabs.robolectric.Robolectric;
15import com.xtremelabs.robolectric.internal.Implementation;
16import com.xtremelabs.robolectric.internal.Implements;
17import com.xtremelabs.robolectric.internal.RealObject;
18
19import java.io.IOException;
20import java.util.*;
21import java.util.concurrent.TimeUnit;
22
23import static com.xtremelabs.robolectric.Robolectric.newInstanceOf;
24import static com.xtremelabs.robolectric.Robolectric.shadowOf;
25
26/**
27 * Shadows the {@code android.accounts.AccountManager} class.
28 */
29@SuppressWarnings({"UnusedDeclaration"})
30@Implements(AccountManager.class)
31public 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
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
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
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