blob: 6680ce6acb0438e7e7c7e2aec6daaa3e2201556f [file] [log] [blame]
Fred Quintana1121bb52011-09-14 23:19:35 -07001/*
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 */
16package android.accounts;
17
Jatin Lodhia8d167782012-09-12 13:59:33 -070018import com.google.android.collect.Sets;
19
Fred Quintana1121bb52011-09-14 23:19:35 -070020import android.app.Activity;
Sudheer Shankadc589ac2016-11-10 15:30:17 -080021import android.app.ActivityManager;
Fred Quintana1121bb52011-09-14 23:19:35 -070022import android.content.Intent;
Fred Quintana1121bb52011-09-14 23:19:35 -070023import android.os.Bundle;
Amith Yamasani27db4682013-03-30 17:07:47 -070024import android.os.IBinder;
Fred Quintana1121bb52011-09-14 23:19:35 -070025import android.os.Parcelable;
Amith Yamasani27db4682013-03-30 17:07:47 -070026import android.os.RemoteException;
27import android.os.UserHandle;
28import android.os.UserManager;
Fred Quintanab04fe4e2011-09-16 21:17:21 -070029import android.text.TextUtils;
Fred Quintana1121bb52011-09-14 23:19:35 -070030import android.util.Log;
Fred Quintana1121bb52011-09-14 23:19:35 -070031import android.view.View;
Amith Yamasani5a3915b2013-05-24 09:53:47 -070032import android.view.Window;
Fred Quintana1121bb52011-09-14 23:19:35 -070033import android.widget.AdapterView;
34import android.widget.ArrayAdapter;
35import android.widget.Button;
Fred Quintana1121bb52011-09-14 23:19:35 -070036import android.widget.ListView;
37import android.widget.TextView;
Alice Yang727c5992012-05-29 13:31:04 -070038
Fred Quintana1121bb52011-09-14 23:19:35 -070039import com.android.internal.R;
40
Fred Quintana9bbdd0b2011-09-27 17:24:32 -070041import java.io.IOException;
Fred Quintana1121bb52011-09-14 23:19:35 -070042import java.util.ArrayList;
Fred Quintana1121bb52011-09-14 23:19:35 -070043import java.util.HashSet;
Dmitry Dementyev06e232f2017-03-29 12:16:55 -070044import java.util.LinkedHashMap;
Dmitry Dementyev01985ff2017-01-19 16:03:39 -080045import java.util.Map;
Fred Quintana1121bb52011-09-14 23:19:35 -070046import java.util.Set;
47
48/**
49 * @hide
50 */
Fred Quintana9bbdd0b2011-09-27 17:24:32 -070051public class ChooseTypeAndAccountActivity extends Activity
52 implements AccountManagerCallback<Bundle> {
Fred Quintanae9095bd2011-10-11 17:47:58 -070053 private static final String TAG = "AccountChooser";
Fred Quintana1121bb52011-09-14 23:19:35 -070054
55 /**
56 * A Parcelable ArrayList of Account objects that limits the choosable accounts to those
57 * in this list, if this parameter is supplied.
58 */
59 public static final String EXTRA_ALLOWABLE_ACCOUNTS_ARRAYLIST = "allowableAccounts";
60
61 /**
62 * A Parcelable ArrayList of String objects that limits the accounts to choose to those
63 * that match the types in this list, if this parameter is supplied. This list is also
64 * used to filter the allowable account types if add account is selected.
65 */
Fred Quintanab04fe4e2011-09-16 21:17:21 -070066 public static final String EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY = "allowableAccountTypes";
Fred Quintana1121bb52011-09-14 23:19:35 -070067
68 /**
Fred Quintanab04fe4e2011-09-16 21:17:21 -070069 * This is passed as the addAccountOptions parameter in AccountManager.addAccount()
70 * if it is called.
Fred Quintana1121bb52011-09-14 23:19:35 -070071 */
72 public static final String EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE = "addAccountOptions";
73
74 /**
Fred Quintanab04fe4e2011-09-16 21:17:21 -070075 * This is passed as the requiredFeatures parameter in AccountManager.addAccount()
76 * if it is called.
77 */
Fred Quintana01df6a82011-10-17 21:04:47 -070078 public static final String EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY =
Fred Quintanab04fe4e2011-09-16 21:17:21 -070079 "addAccountRequiredFeatures";
80
81 /**
82 * This is passed as the authTokenType string in AccountManager.addAccount()
83 * if it is called.
84 */
85 public static final String EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING = "authTokenType";
86
87 /**
Fred Quintana1121bb52011-09-14 23:19:35 -070088 * If set then the specified account is already "selected".
89 */
90 public static final String EXTRA_SELECTED_ACCOUNT = "selectedAccount";
91
Fred Quintanab04fe4e2011-09-16 21:17:21 -070092 /**
Carlos Valdiviaa3db8ac2015-07-10 13:04:43 -070093 * Deprecated. Providing this extra to {@link ChooseTypeAndAccountActivity}
94 * will have no effect.
Fred Quintanab04fe4e2011-09-16 21:17:21 -070095 */
Carlos Valdiviaa3db8ac2015-07-10 13:04:43 -070096 @Deprecated
Fred Quintanab04fe4e2011-09-16 21:17:21 -070097 public static final String EXTRA_ALWAYS_PROMPT_FOR_ACCOUNT =
98 "alwaysPromptForAccount";
99
100 /**
Dmitry Dementyev06e232f2017-03-29 12:16:55 -0700101 * If set then this string will be used as the description rather than
Fred Quintanab04fe4e2011-09-16 21:17:21 -0700102 * the default.
103 */
Dmitry Dementyev06e232f2017-03-29 12:16:55 -0700104 public static final String EXTRA_DESCRIPTION_TEXT_OVERRIDE = "descriptionTextOverride";
Fred Quintanab04fe4e2011-09-16 21:17:21 -0700105
Fred Quintanae9095bd2011-10-11 17:47:58 -0700106 public static final int REQUEST_NULL = 0;
107 public static final int REQUEST_CHOOSE_TYPE = 1;
108 public static final int REQUEST_ADD_ACCOUNT = 2;
109
110 private static final String KEY_INSTANCE_STATE_PENDING_REQUEST = "pendingRequest";
111 private static final String KEY_INSTANCE_STATE_EXISTING_ACCOUNTS = "existingAccounts";
Alice Yang727c5992012-05-29 13:31:04 -0700112 private static final String KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME = "selectedAccountName";
113 private static final String KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT = "selectedAddAccount";
Dmitry Dementyev06e232f2017-03-29 12:16:55 -0700114 private static final String KEY_INSTANCE_STATE_ACCOUNTS_LIST = "accountsList";
115 private static final String KEY_INSTANCE_STATE_VISIBILITY_LIST = "visibilityList";
Fred Quintanae9095bd2011-10-11 17:47:58 -0700116
Alice Yang727c5992012-05-29 13:31:04 -0700117 private static final int SELECTED_ITEM_NONE = -1;
118
Jatin Lodhia8d167782012-09-12 13:59:33 -0700119 private Set<Account> mSetOfAllowableAccounts;
120 private Set<String> mSetOfRelevantAccountTypes;
121 private String mSelectedAccountName = null;
122 private boolean mSelectedAddNewAccount = false;
Jatin Lodhia8d167782012-09-12 13:59:33 -0700123 private String mDescriptionOverride;
124
Dmitry Dementyev06e232f2017-03-29 12:16:55 -0700125 private LinkedHashMap<Account, Integer> mAccounts;
Dmitry Dementyev01985ff2017-01-19 16:03:39 -0800126 // TODO Redesign flow to show NOT_VISIBLE accounts
127 // and display a warning if they are selected.
128 // Currently NOT_VISBILE accounts are not shown at all.
129 private ArrayList<Account> mPossiblyVisibleAccounts;
Fred Quintanae9095bd2011-10-11 17:47:58 -0700130 private int mPendingRequest = REQUEST_NULL;
131 private Parcelable[] mExistingAccounts = null;
Alice Yang727c5992012-05-29 13:31:04 -0700132 private int mSelectedItemIndex;
133 private Button mOkButton;
Amith Yamasani27db4682013-03-30 17:07:47 -0700134 private int mCallingUid;
135 private String mCallingPackage;
136 private boolean mDisallowAddAccounts;
Amith Yamasani5a3915b2013-05-24 09:53:47 -0700137 private boolean mDontShowPicker;
Fred Quintana1121bb52011-09-14 23:19:35 -0700138
139 @Override
140 public void onCreate(Bundle savedInstanceState) {
Fred Quintanae9095bd2011-10-11 17:47:58 -0700141 if (Log.isLoggable(TAG, Log.VERBOSE)) {
142 Log.v(TAG, "ChooseTypeAndAccountActivity.onCreate(savedInstanceState="
143 + savedInstanceState + ")");
144 }
145
Amith Yamasani27db4682013-03-30 17:07:47 -0700146 String message = null;
147
148 try {
149 IBinder activityToken = getActivityToken();
Sudheer Shankadc589ac2016-11-10 15:30:17 -0800150 mCallingUid = ActivityManager.getService().getLaunchedFromUid(activityToken);
151 mCallingPackage = ActivityManager.getService().getLaunchedFromPackage(
Amith Yamasani27db4682013-03-30 17:07:47 -0700152 activityToken);
153 if (mCallingUid != 0 && mCallingPackage != null) {
154 Bundle restrictions = UserManager.get(this)
155 .getUserRestrictions(new UserHandle(UserHandle.getUserId(mCallingUid)));
156 mDisallowAddAccounts =
157 restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false);
158 }
159 } catch (RemoteException re) {
160 // Couldn't figure out caller details
161 Log.w(getClass().getSimpleName(), "Unable to get caller identity \n" + re);
162 }
163
Fred Quintanab04fe4e2011-09-16 21:17:21 -0700164 // save some items we use frequently
Fred Quintanab04fe4e2011-09-16 21:17:21 -0700165 final Intent intent = getIntent();
166
Dmitry Dementyev06e232f2017-03-29 12:16:55 -0700167 mSetOfAllowableAccounts = getAllowableAccountSet(intent);
168 mSetOfRelevantAccountTypes = getReleventAccountTypes(intent);
169 mDescriptionOverride = intent.getStringExtra(EXTRA_DESCRIPTION_TEXT_OVERRIDE);
170
Alice Yang727c5992012-05-29 13:31:04 -0700171 if (savedInstanceState != null) {
172 mPendingRequest = savedInstanceState.getInt(KEY_INSTANCE_STATE_PENDING_REQUEST);
173 mExistingAccounts =
174 savedInstanceState.getParcelableArray(KEY_INSTANCE_STATE_EXISTING_ACCOUNTS);
175
176 // Makes sure that any user selection is preserved across orientation changes.
Dmitry Dementyev01985ff2017-01-19 16:03:39 -0800177 mSelectedAccountName =
178 savedInstanceState.getString(KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME);
179 mSelectedAddNewAccount =
180 savedInstanceState.getBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, false);
Dmitry Dementyev06e232f2017-03-29 12:16:55 -0700181 // restore mAccounts
182 Parcelable[] accounts =
183 savedInstanceState.getParcelableArray(KEY_INSTANCE_STATE_ACCOUNTS_LIST);
184 ArrayList<Integer> visibility =
185 savedInstanceState.getIntegerArrayList(KEY_INSTANCE_STATE_VISIBILITY_LIST);
186 mAccounts = new LinkedHashMap<>();
187 for (int i = 0; i < accounts.length; i++) {
188 mAccounts.put((Account) accounts[i], visibility.get(i));
189 }
Alice Yang727c5992012-05-29 13:31:04 -0700190 } else {
191 mPendingRequest = REQUEST_NULL;
192 mExistingAccounts = null;
193 // If the selected account as specified in the intent matches one in the list we will
194 // show is as pre-selected.
195 Account selectedAccount = (Account) intent.getParcelableExtra(EXTRA_SELECTED_ACCOUNT);
196 if (selectedAccount != null) {
Jatin Lodhia8d167782012-09-12 13:59:33 -0700197 mSelectedAccountName = selectedAccount.name;
Alice Yang727c5992012-05-29 13:31:04 -0700198 }
Dmitry Dementyev06e232f2017-03-29 12:16:55 -0700199 mAccounts = getAcceptableAccountChoices(AccountManager.get(this));
Fred Quintanab04fe4e2011-09-16 21:17:21 -0700200 }
201
Alice Yang727c5992012-05-29 13:31:04 -0700202 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Jatin Lodhia8d167782012-09-12 13:59:33 -0700203 Log.v(TAG, "selected account name is " + mSelectedAccountName);
Alice Yang727c5992012-05-29 13:31:04 -0700204 }
Fred Quintana1121bb52011-09-14 23:19:35 -0700205
Dmitry Dementyev06e232f2017-03-29 12:16:55 -0700206 mPossiblyVisibleAccounts = new ArrayList<>(mAccounts.size());
207 for (Map.Entry<Account, Integer> entry : mAccounts.entrySet()) {
208 if (AccountManager.VISIBILITY_NOT_VISIBLE != entry.getValue()) {
209 mPossiblyVisibleAccounts.add(entry.getKey());
210 }
211 }
Fred Quintana1121bb52011-09-14 23:19:35 -0700212
Dmitry Dementyev06e232f2017-03-29 12:16:55 -0700213 if (mPossiblyVisibleAccounts.isEmpty() && mDisallowAddAccounts) {
Amith Yamasani5a3915b2013-05-24 09:53:47 -0700214 requestWindowFeature(Window.FEATURE_NO_TITLE);
215 setContentView(R.layout.app_not_authorized);
216 mDontShowPicker = true;
217 }
Fred Quintana1121bb52011-09-14 23:19:35 -0700218
Simranjit Singh Kohli734f8fb2015-05-22 14:00:32 -0700219 if (mDontShowPicker) {
220 super.onCreate(savedInstanceState);
221 return;
222 }
Carlos Valdiviacf0a8812012-05-16 16:32:06 -0700223
Jatin Lodhia8d167782012-09-12 13:59:33 -0700224 // In cases where the activity does not need to show an account picker, cut the chase
225 // and return the result directly. Eg:
226 // Single account -> select it directly
227 // No account -> launch add account activity directly
Fred Quintanae9095bd2011-10-11 17:47:58 -0700228 if (mPendingRequest == REQUEST_NULL) {
Alice Yang727c5992012-05-29 13:31:04 -0700229 // If there are no relevant accounts and only one relevant account type go directly to
Carlos Valdiviacf0a8812012-05-16 16:32:06 -0700230 // add account. Otherwise let the user choose.
Dmitry Dementyev06e232f2017-03-29 12:16:55 -0700231 if (mPossiblyVisibleAccounts.isEmpty()) {
Simranjit Singh Kohli734f8fb2015-05-22 14:00:32 -0700232 setNonLabelThemeAndCallSuperCreate(savedInstanceState);
Jatin Lodhia8d167782012-09-12 13:59:33 -0700233 if (mSetOfRelevantAccountTypes.size() == 1) {
234 runAddAccountForAuthenticator(mSetOfRelevantAccountTypes.iterator().next());
Carlos Valdiviacf0a8812012-05-16 16:32:06 -0700235 } else {
236 startChooseAccountTypeActivity();
237 }
Fred Quintanae9095bd2011-10-11 17:47:58 -0700238 }
239 }
Carlos Valdiviacf0a8812012-05-16 16:32:06 -0700240
Dmitry Dementyev01985ff2017-01-19 16:03:39 -0800241 String[] listItems = getListOfDisplayableOptions(mPossiblyVisibleAccounts);
242 mSelectedItemIndex = getItemIndexToSelect(mPossiblyVisibleAccounts, mSelectedAccountName,
243 mSelectedAddNewAccount);
Jatin Lodhia8d167782012-09-12 13:59:33 -0700244
Simranjit Singh Kohli734f8fb2015-05-22 14:00:32 -0700245 super.onCreate(savedInstanceState);
Carlos Valdiviacf0a8812012-05-16 16:32:06 -0700246 setContentView(R.layout.choose_type_and_account);
Jatin Lodhia8d167782012-09-12 13:59:33 -0700247 overrideDescriptionIfSupplied(mDescriptionOverride);
248 populateUIAccountList(listItems);
Alice Yang6cab5e82012-05-31 15:48:51 -0700249
250 // Only enable "OK" button if something has been selected.
Alan Viverette51efddb2017-04-05 10:00:01 -0400251 mOkButton = findViewById(android.R.id.button2);
Alice Yang6cab5e82012-05-31 15:48:51 -0700252 mOkButton.setEnabled(mSelectedItemIndex != SELECTED_ITEM_NONE);
Fred Quintanae9095bd2011-10-11 17:47:58 -0700253 }
254
255 @Override
256 protected void onDestroy() {
257 if (Log.isLoggable(TAG, Log.VERBOSE)) {
258 Log.v(TAG, "ChooseTypeAndAccountActivity.onDestroy()");
259 }
260 super.onDestroy();
261 }
262
263 @Override
264 protected void onSaveInstanceState(final Bundle outState) {
265 super.onSaveInstanceState(outState);
266 outState.putInt(KEY_INSTANCE_STATE_PENDING_REQUEST, mPendingRequest);
Fred Quintana01df6a82011-10-17 21:04:47 -0700267 if (mPendingRequest == REQUEST_ADD_ACCOUNT) {
268 outState.putParcelableArray(KEY_INSTANCE_STATE_EXISTING_ACCOUNTS, mExistingAccounts);
269 }
Alice Yang727c5992012-05-29 13:31:04 -0700270 if (mSelectedItemIndex != SELECTED_ITEM_NONE) {
Dmitry Dementyev01985ff2017-01-19 16:03:39 -0800271 if (mSelectedItemIndex == mPossiblyVisibleAccounts.size()) {
Alice Yang727c5992012-05-29 13:31:04 -0700272 outState.putBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, true);
273 } else {
274 outState.putBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, false);
275 outState.putString(KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME,
Dmitry Dementyev01985ff2017-01-19 16:03:39 -0800276 mPossiblyVisibleAccounts.get(mSelectedItemIndex).name);
Alice Yang727c5992012-05-29 13:31:04 -0700277 }
278 }
Dmitry Dementyev06e232f2017-03-29 12:16:55 -0700279 // save mAccounts
280 Parcelable[] accounts = new Parcelable[mAccounts.size()];
281 ArrayList<Integer> visibility = new ArrayList<>(mAccounts.size());
282 int i = 0;
283 for (Map.Entry<Account, Integer> e : mAccounts.entrySet()) {
284 accounts[i++] = e.getKey();
285 visibility.add(e.getValue());
286 }
287 outState.putParcelableArray(KEY_INSTANCE_STATE_ACCOUNTS_LIST, accounts);
288 outState.putIntegerArrayList(KEY_INSTANCE_STATE_VISIBILITY_LIST, visibility);
Alice Yang727c5992012-05-29 13:31:04 -0700289 }
290
291 public void onCancelButtonClicked(View view) {
292 onBackPressed();
293 }
294
295 public void onOkButtonClicked(View view) {
Dmitry Dementyev01985ff2017-01-19 16:03:39 -0800296 if (mSelectedItemIndex == mPossiblyVisibleAccounts.size()) {
Alice Yang727c5992012-05-29 13:31:04 -0700297 // Selected "Add New Account" option
298 startChooseAccountTypeActivity();
299 } else if (mSelectedItemIndex != SELECTED_ITEM_NONE) {
Dmitry Dementyev01985ff2017-01-19 16:03:39 -0800300 onAccountSelected(mPossiblyVisibleAccounts.get(mSelectedItemIndex));
Alice Yang727c5992012-05-29 13:31:04 -0700301 }
Fred Quintana1121bb52011-09-14 23:19:35 -0700302 }
303
304 // Called when the choose account type activity (for adding an account) returns.
305 // If it was a success read the account and set it in the result. In all cases
306 // return the result and finish this activity.
307 @Override
308 protected void onActivityResult(final int requestCode, final int resultCode,
309 final Intent data) {
Fred Quintanae9095bd2011-10-11 17:47:58 -0700310 if (Log.isLoggable(TAG, Log.VERBOSE)) {
311 if (data != null && data.getExtras() != null) data.getExtras().keySet();
312 Bundle extras = data != null ? data.getExtras() : null;
313 Log.v(TAG, "ChooseTypeAndAccountActivity.onActivityResult(reqCode=" + requestCode
314 + ", resCode=" + resultCode + ", extras=" + extras + ")");
Fred Quintana1121bb52011-09-14 23:19:35 -0700315 }
Fred Quintanae9095bd2011-10-11 17:47:58 -0700316
317 // we got our result, so clear the fact that we had a pending request
318 mPendingRequest = REQUEST_NULL;
Fred Quintanae9095bd2011-10-11 17:47:58 -0700319
320 if (resultCode == RESULT_CANCELED) {
Alice Yang727c5992012-05-29 13:31:04 -0700321 // if canceling out of addAccount and the original state caused us to skip this,
Fred Quintana2becf932011-11-15 17:33:08 -0800322 // finish this activity
Dmitry Dementyev06e232f2017-03-29 12:16:55 -0700323 if (mPossiblyVisibleAccounts.isEmpty()) {
Fred Quintana2becf932011-11-15 17:33:08 -0800324 setResult(Activity.RESULT_CANCELED);
325 finish();
326 }
Fred Quintanae9095bd2011-10-11 17:47:58 -0700327 return;
328 }
329
330 if (resultCode == RESULT_OK) {
331 if (requestCode == REQUEST_CHOOSE_TYPE) {
332 if (data != null) {
333 String accountType = data.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE);
334 if (accountType != null) {
335 runAddAccountForAuthenticator(accountType);
336 return;
337 }
338 }
339 Log.d(TAG, "ChooseTypeAndAccountActivity.onActivityResult: unable to find account "
340 + "type, pretending the request was canceled");
341 } else if (requestCode == REQUEST_ADD_ACCOUNT) {
342 String accountName = null;
343 String accountType = null;
344
345 if (data != null) {
346 accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
347 accountType = data.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE);
348 }
349
350 if (accountName == null || accountType == null) {
Dmitry Dementyev01985ff2017-01-19 16:03:39 -0800351 // new account was added.
Amith Yamasani27db4682013-03-30 17:07:47 -0700352 Account[] currentAccounts = AccountManager.get(this).getAccountsForPackage(
353 mCallingPackage, mCallingUid);
Fred Quintanae9095bd2011-10-11 17:47:58 -0700354 Set<Account> preExistingAccounts = new HashSet<Account>();
Fred Quintana01df6a82011-10-17 21:04:47 -0700355 for (Parcelable accountParcel : mExistingAccounts) {
Fred Quintanae9095bd2011-10-11 17:47:58 -0700356 preExistingAccounts.add((Account) accountParcel);
357 }
358 for (Account account : currentAccounts) {
Dmitry Dementyev01985ff2017-01-19 16:03:39 -0800359 // New account is visible to the app - return it.
Fred Quintanae9095bd2011-10-11 17:47:58 -0700360 if (!preExistingAccounts.contains(account)) {
361 accountName = account.name;
362 accountType = account.type;
363 break;
364 }
365 }
366 }
367
368 if (accountName != null || accountType != null) {
369 setResultAndFinish(accountName, accountType);
370 return;
371 }
372 }
373 Log.d(TAG, "ChooseTypeAndAccountActivity.onActivityResult: unable to find added "
374 + "account, pretending the request was canceled");
375 }
376 if (Log.isLoggable(TAG, Log.VERBOSE)) {
377 Log.v(TAG, "ChooseTypeAndAccountActivity.onActivityResult: canceled");
378 }
Fred Quintana1121bb52011-09-14 23:19:35 -0700379 setResult(Activity.RESULT_CANCELED);
380 finish();
381 }
382
Fred Quintana9bbdd0b2011-09-27 17:24:32 -0700383 protected void runAddAccountForAuthenticator(String type) {
Fred Quintanae9095bd2011-10-11 17:47:58 -0700384 if (Log.isLoggable(TAG, Log.VERBOSE)) {
385 Log.v(TAG, "runAddAccountForAuthenticator: " + type);
386 }
Fred Quintana9bbdd0b2011-09-27 17:24:32 -0700387 final Bundle options = getIntent().getBundleExtra(
388 ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE);
389 final String[] requiredFeatures = getIntent().getStringArrayExtra(
390 ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY);
391 final String authTokenType = getIntent().getStringExtra(
392 ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING);
393 AccountManager.get(this).addAccount(type, authTokenType, requiredFeatures,
Fred Quintanae9095bd2011-10-11 17:47:58 -0700394 options, null /* activity */, this /* callback */, null /* Handler */);
Fred Quintana9bbdd0b2011-09-27 17:24:32 -0700395 }
396
Alice Yang727c5992012-05-29 13:31:04 -0700397 @Override
Fred Quintana9bbdd0b2011-09-27 17:24:32 -0700398 public void run(final AccountManagerFuture<Bundle> accountManagerFuture) {
399 try {
400 final Bundle accountManagerResult = accountManagerFuture.getResult();
Fred Quintanae9095bd2011-10-11 17:47:58 -0700401 final Intent intent = (Intent)accountManagerResult.getParcelable(
402 AccountManager.KEY_INTENT);
403 if (intent != null) {
404 mPendingRequest = REQUEST_ADD_ACCOUNT;
Amith Yamasani27db4682013-03-30 17:07:47 -0700405 mExistingAccounts = AccountManager.get(this).getAccountsForPackage(mCallingPackage,
406 mCallingUid);
Fred Quintanae9095bd2011-10-11 17:47:58 -0700407 intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK);
408 startActivityForResult(intent, REQUEST_ADD_ACCOUNT);
Fred Quintana9bbdd0b2011-09-27 17:24:32 -0700409 return;
410 }
411 } catch (OperationCanceledException e) {
412 setResult(Activity.RESULT_CANCELED);
413 finish();
414 return;
415 } catch (IOException e) {
416 } catch (AuthenticatorException e) {
417 }
418 Bundle bundle = new Bundle();
419 bundle.putString(AccountManager.KEY_ERROR_MESSAGE, "error communicating with server");
420 setResult(Activity.RESULT_OK, new Intent().putExtras(bundle));
421 finish();
422 }
Fred Quintanab04fe4e2011-09-16 21:17:21 -0700423
Simranjit Singh Kohli734f8fb2015-05-22 14:00:32 -0700424 /**
425 * The default activity theme shows label at the top. Set a theme which does
426 * not show label, which effectively makes the activity invisible. Note that
427 * no content is being set. If something gets set, using this theme may be
428 * useless.
429 */
430 private void setNonLabelThemeAndCallSuperCreate(Bundle savedInstanceState) {
Salvador Martinez7e2657c2016-09-26 12:49:31 -0700431 setTheme(R.style.Theme_DeviceDefault_Light_Dialog_NoActionBar);
Simranjit Singh Kohli734f8fb2015-05-22 14:00:32 -0700432 super.onCreate(savedInstanceState);
433 }
434
Alice Yang727c5992012-05-29 13:31:04 -0700435 private void onAccountSelected(Account account) {
436 Log.d(TAG, "selected account " + account);
437 setResultAndFinish(account.name, account.type);
Fred Quintana1121bb52011-09-14 23:19:35 -0700438 }
439
440 private void setResultAndFinish(final String accountName, final String accountType) {
Dmitry Dementyev01985ff2017-01-19 16:03:39 -0800441 // Mark account as visible since user chose it.
442 Account account = new Account(accountName, accountType);
Dmitry Dementyev06e232f2017-03-29 12:16:55 -0700443 Integer oldVisibility =
444 AccountManager.get(this).getAccountVisibility(account, mCallingPackage);
Dmitry Dementyev01985ff2017-01-19 16:03:39 -0800445 if (oldVisibility != null
446 && oldVisibility == AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE) {
Dmitry Dementyeve366f822017-01-31 10:25:10 -0800447 AccountManager.get(this).setAccountVisibility(account, mCallingPackage,
Dmitry Dementyev01985ff2017-01-19 16:03:39 -0800448 AccountManager.VISIBILITY_USER_MANAGED_VISIBLE);
449 }
Dmitry Dementyev06e232f2017-03-29 12:16:55 -0700450
451 if (oldVisibility != null && oldVisibility == AccountManager.VISIBILITY_NOT_VISIBLE) {
452 // Added account is not visible to caller.
453 setResult(Activity.RESULT_CANCELED);
454 finish();
455 return;
456 }
Fred Quintana1121bb52011-09-14 23:19:35 -0700457 Bundle bundle = new Bundle();
458 bundle.putString(AccountManager.KEY_ACCOUNT_NAME, accountName);
459 bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, accountType);
460 setResult(Activity.RESULT_OK, new Intent().putExtras(bundle));
Fred Quintanae9095bd2011-10-11 17:47:58 -0700461 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Dmitry Dementyev01985ff2017-01-19 16:03:39 -0800462 Log.v(TAG, "ChooseTypeAndAccountActivity.setResultAndFinish: selected account "
463 + accountName + ", " + accountType);
Fred Quintanae9095bd2011-10-11 17:47:58 -0700464 }
Dmitry Dementyev06e232f2017-03-29 12:16:55 -0700465
Fred Quintana1121bb52011-09-14 23:19:35 -0700466 finish();
467 }
468
469 private void startChooseAccountTypeActivity() {
Fred Quintanae9095bd2011-10-11 17:47:58 -0700470 if (Log.isLoggable(TAG, Log.VERBOSE)) {
471 Log.v(TAG, "ChooseAccountTypeActivity.startChooseAccountTypeActivity()");
472 }
Fred Quintana1121bb52011-09-14 23:19:35 -0700473 final Intent intent = new Intent(this, ChooseAccountTypeActivity.class);
Fred Quintana9bbdd0b2011-09-27 17:24:32 -0700474 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
Fred Quintanaa77253a2011-09-19 15:28:18 -0700475 intent.putExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY,
476 getIntent().getStringArrayExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY));
Fred Quintana1121bb52011-09-14 23:19:35 -0700477 intent.putExtra(EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE,
Fred Quintanab04fe4e2011-09-16 21:17:21 -0700478 getIntent().getBundleExtra(EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE));
479 intent.putExtra(EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY,
480 getIntent().getStringArrayExtra(EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY));
481 intent.putExtra(EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING,
Fred Quintanaa77253a2011-09-19 15:28:18 -0700482 getIntent().getStringExtra(EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING));
Fred Quintanae9095bd2011-10-11 17:47:58 -0700483 startActivityForResult(intent, REQUEST_CHOOSE_TYPE);
484 mPendingRequest = REQUEST_CHOOSE_TYPE;
Fred Quintana1121bb52011-09-14 23:19:35 -0700485 }
Jatin Lodhia8d167782012-09-12 13:59:33 -0700486
487 /**
488 * @return a value between 0 (inclusive) and accounts.size() (inclusive) or SELECTED_ITEM_NONE.
489 * An index value of accounts.size() indicates 'Add account' option.
490 */
491 private int getItemIndexToSelect(ArrayList<Account> accounts, String selectedAccountName,
492 boolean selectedAddNewAccount) {
493 // If "Add account" option was previously selected by user, preserve it across
494 // orientation changes.
495 if (selectedAddNewAccount) {
496 return accounts.size();
497 }
498 // search for the selected account name if present
499 for (int i = 0; i < accounts.size(); i++) {
500 if (accounts.get(i).name.equals(selectedAccountName)) {
501 return i;
502 }
503 }
504 // no account selected.
505 return SELECTED_ITEM_NONE;
506 }
507
508 private String[] getListOfDisplayableOptions(ArrayList<Account> accounts) {
509 // List of options includes all accounts found together with "Add new account" as the
510 // last item in the list.
Amith Yamasani27db4682013-03-30 17:07:47 -0700511 String[] listItems = new String[accounts.size() + (mDisallowAddAccounts ? 0 : 1)];
Jatin Lodhia8d167782012-09-12 13:59:33 -0700512 for (int i = 0; i < accounts.size(); i++) {
513 listItems[i] = accounts.get(i).name;
514 }
Amith Yamasani27db4682013-03-30 17:07:47 -0700515 if (!mDisallowAddAccounts) {
516 listItems[accounts.size()] = getResources().getString(
517 R.string.add_account_button_label);
518 }
Jatin Lodhia8d167782012-09-12 13:59:33 -0700519 return listItems;
520 }
521
522 /**
Dmitry Dementyev01985ff2017-01-19 16:03:39 -0800523 * Create a list of Account objects for each account that is acceptable. Filter out accounts
524 * that don't match the allowable types, if provided, or that don't match the allowable
525 * accounts, if provided.
Jatin Lodhia8d167782012-09-12 13:59:33 -0700526 */
Dmitry Dementyev06e232f2017-03-29 12:16:55 -0700527 private LinkedHashMap<Account, Integer> getAcceptableAccountChoices(AccountManager accountManager) {
528 Map<Account, Integer> accountsAndVisibilityForCaller =
529 accountManager.getAccountsAndVisibilityForPackage(mCallingPackage, null);
530 Account[] allAccounts = accountManager.getAccounts();
531 LinkedHashMap<Account, Integer> accountsToPopulate =
532 new LinkedHashMap<>(accountsAndVisibilityForCaller.size());
533 for (Account account : allAccounts) {
Dmitry Dementyev01985ff2017-01-19 16:03:39 -0800534 if (mSetOfAllowableAccounts != null
Dmitry Dementyev06e232f2017-03-29 12:16:55 -0700535 && !mSetOfAllowableAccounts.contains(account)) {
Dmitry Dementyev01985ff2017-01-19 16:03:39 -0800536 continue;
537 }
538 if (mSetOfRelevantAccountTypes != null
Dmitry Dementyev06e232f2017-03-29 12:16:55 -0700539 && !mSetOfRelevantAccountTypes.contains(account.type)) {
Dmitry Dementyev01985ff2017-01-19 16:03:39 -0800540 continue;
541 }
Dmitry Dementyev06e232f2017-03-29 12:16:55 -0700542 if (accountsAndVisibilityForCaller.get(account) != null) {
543 accountsToPopulate.put(account, accountsAndVisibilityForCaller.get(account));
544 }
Dmitry Dementyev01985ff2017-01-19 16:03:39 -0800545 }
546 return accountsToPopulate;
Jatin Lodhia8d167782012-09-12 13:59:33 -0700547 }
548
549 /**
Simranjit Singh Kohli734f8fb2015-05-22 14:00:32 -0700550 * Return a set of account types specified by the intent as well as supported by the
Jatin Lodhia8d167782012-09-12 13:59:33 -0700551 * AccountManager.
552 */
553 private Set<String> getReleventAccountTypes(final Intent intent) {
554 // An account type is relevant iff it is allowed by the caller and supported by the account
555 // manager.
556 Set<String> setOfRelevantAccountTypes = null;
557 final String[] allowedAccountTypes =
558 intent.getStringArrayExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY);
Simranjit Singh Kohli734f8fb2015-05-22 14:00:32 -0700559 AuthenticatorDescription[] descs = AccountManager.get(this).getAuthenticatorTypes();
560 Set<String> supportedAccountTypes = new HashSet<String>(descs.length);
561 for (AuthenticatorDescription desc : descs) {
562 supportedAccountTypes.add(desc.type);
563 }
564 if (allowedAccountTypes != null) {
565 setOfRelevantAccountTypes = Sets.newHashSet(allowedAccountTypes);
566 setOfRelevantAccountTypes.retainAll(supportedAccountTypes);
567 } else {
568 setOfRelevantAccountTypes = supportedAccountTypes;
Jatin Lodhia8d167782012-09-12 13:59:33 -0700569 }
570 return setOfRelevantAccountTypes;
571 }
572
573 /**
574 * Returns a set of whitelisted accounts given by the intent or null if none specified by the
575 * intent.
576 */
577 private Set<Account> getAllowableAccountSet(final Intent intent) {
578 Set<Account> setOfAllowableAccounts = null;
579 final ArrayList<Parcelable> validAccounts =
580 intent.getParcelableArrayListExtra(EXTRA_ALLOWABLE_ACCOUNTS_ARRAYLIST);
581 if (validAccounts != null) {
582 setOfAllowableAccounts = new HashSet<Account>(validAccounts.size());
583 for (Parcelable parcelable : validAccounts) {
584 setOfAllowableAccounts.add((Account)parcelable);
585 }
586 }
587 return setOfAllowableAccounts;
588 }
589
590 /**
591 * Overrides the description text view for the picker activity if specified by the intent.
592 * If not specified then makes the description invisible.
593 */
594 private void overrideDescriptionIfSupplied(String descriptionOverride) {
Alan Viverette51efddb2017-04-05 10:00:01 -0400595 TextView descriptionView = findViewById(R.id.description);
Jatin Lodhia8d167782012-09-12 13:59:33 -0700596 if (!TextUtils.isEmpty(descriptionOverride)) {
597 descriptionView.setText(descriptionOverride);
598 } else {
599 descriptionView.setVisibility(View.GONE);
600 }
601 }
602
603 /**
604 * Populates the UI ListView with the given list of items and selects an item
605 * based on {@code mSelectedItemIndex} member variable.
606 */
607 private final void populateUIAccountList(String[] listItems) {
Alan Viverette51efddb2017-04-05 10:00:01 -0400608 ListView list = findViewById(android.R.id.list);
Jatin Lodhia8d167782012-09-12 13:59:33 -0700609 list.setAdapter(new ArrayAdapter<String>(this,
610 android.R.layout.simple_list_item_single_choice, listItems));
611 list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
612 list.setItemsCanFocus(false);
613 list.setOnItemClickListener(
614 new AdapterView.OnItemClickListener() {
615 @Override
616 public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
617 mSelectedItemIndex = position;
618 mOkButton.setEnabled(true);
619 }
620 });
621 if (mSelectedItemIndex != SELECTED_ITEM_NONE) {
622 list.setItemChecked(mSelectedItemIndex, true);
623 if (Log.isLoggable(TAG, Log.VERBOSE)) {
624 Log.v(TAG, "List item " + mSelectedItemIndex + " should be selected");
625 }
626 }
627 }
Fred Quintana1121bb52011-09-14 23:19:35 -0700628}