Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 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.os.Parcelable; |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 26 | import android.text.TextUtils; |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 27 | import android.util.Log; |
| 28 | import android.view.LayoutInflater; |
| 29 | import android.view.View; |
| 30 | import android.view.ViewGroup; |
| 31 | import android.widget.AdapterView; |
| 32 | import android.widget.ArrayAdapter; |
| 33 | import android.widget.Button; |
| 34 | import android.widget.ImageView; |
| 35 | import android.widget.ListView; |
| 36 | import android.widget.TextView; |
| 37 | import com.android.internal.R; |
| 38 | |
| 39 | import java.util.ArrayList; |
| 40 | import java.util.HashMap; |
| 41 | import java.util.HashSet; |
| 42 | import java.util.Set; |
| 43 | |
| 44 | /** |
| 45 | * @hide |
| 46 | */ |
| 47 | public class ChooseTypeAndAccountActivity extends Activity { |
| 48 | private static final String TAG = "AccountManager"; |
| 49 | |
| 50 | /** |
| 51 | * A Parcelable ArrayList of Account objects that limits the choosable accounts to those |
| 52 | * in this list, if this parameter is supplied. |
| 53 | */ |
| 54 | public static final String EXTRA_ALLOWABLE_ACCOUNTS_ARRAYLIST = "allowableAccounts"; |
| 55 | |
| 56 | /** |
| 57 | * A Parcelable ArrayList of String objects that limits the accounts to choose to those |
| 58 | * that match the types in this list, if this parameter is supplied. This list is also |
| 59 | * used to filter the allowable account types if add account is selected. |
| 60 | */ |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 61 | public static final String EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY = "allowableAccountTypes"; |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 62 | |
| 63 | /** |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 64 | * This is passed as the addAccountOptions parameter in AccountManager.addAccount() |
| 65 | * if it is called. |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 66 | */ |
| 67 | public static final String EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE = "addAccountOptions"; |
| 68 | |
| 69 | /** |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 70 | * This is passed as the requiredFeatures parameter in AccountManager.addAccount() |
| 71 | * if it is called. |
| 72 | */ |
| 73 | public static final String EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY = |
| 74 | "addAccountRequiredFeatures"; |
| 75 | |
| 76 | /** |
| 77 | * This is passed as the authTokenType string in AccountManager.addAccount() |
| 78 | * if it is called. |
| 79 | */ |
| 80 | public static final String EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING = "authTokenType"; |
| 81 | |
| 82 | /** |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 83 | * If set then the specified account is already "selected". |
| 84 | */ |
| 85 | public static final String EXTRA_SELECTED_ACCOUNT = "selectedAccount"; |
| 86 | |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 87 | /** |
| 88 | * If true then display the account selection list even if there is just |
| 89 | * one account to choose from. boolean. |
| 90 | */ |
| 91 | public static final String EXTRA_ALWAYS_PROMPT_FOR_ACCOUNT = |
| 92 | "alwaysPromptForAccount"; |
| 93 | |
| 94 | /** |
| 95 | * If set then this string willb e used as the description rather than |
| 96 | * the default. |
| 97 | */ |
| 98 | public static final String EXTRA_DESCRIPTION_TEXT_OVERRIDE = |
| 99 | "descriptionTextOverride"; |
| 100 | |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 101 | private ArrayList<AccountInfo> mAccountInfos; |
| 102 | |
| 103 | @Override |
| 104 | public void onCreate(Bundle savedInstanceState) { |
| 105 | super.onCreate(savedInstanceState); |
| 106 | setContentView(R.layout.choose_type_and_account); |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 107 | |
| 108 | // save some items we use frequently |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 109 | final AccountManager accountManager = AccountManager.get(this); |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 110 | final Intent intent = getIntent(); |
| 111 | |
| 112 | // override the description text if supplied |
| 113 | final String descriptionOverride = |
| 114 | intent.getStringExtra(EXTRA_DESCRIPTION_TEXT_OVERRIDE); |
| 115 | if (!TextUtils.isEmpty(descriptionOverride)) { |
| 116 | ((TextView)findViewById(R.id.description)).setText(descriptionOverride); |
| 117 | } |
| 118 | |
| 119 | // If the selected account matches one in the list we will place a |
| 120 | // checkmark next to it. |
| 121 | final Account selectedAccount = |
| 122 | (Account)intent.getParcelableExtra(EXTRA_SELECTED_ACCOUNT); |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 123 | |
| 124 | // build an efficiently queryable map of account types to authenticator descriptions |
| 125 | final HashMap<String, AuthenticatorDescription> typeToAuthDescription = |
| 126 | new HashMap<String, AuthenticatorDescription>(); |
| 127 | for(AuthenticatorDescription desc : accountManager.getAuthenticatorTypes()) { |
| 128 | typeToAuthDescription.put(desc.type, desc); |
| 129 | } |
| 130 | |
| 131 | // Read the validAccounts, if present, and add them to the setOfAllowableAccounts |
| 132 | Set<Account> setOfAllowableAccounts = null; |
| 133 | final ArrayList<Parcelable> validAccounts = |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 134 | intent.getParcelableArrayListExtra(EXTRA_ALLOWABLE_ACCOUNTS_ARRAYLIST); |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 135 | if (validAccounts != null) { |
| 136 | setOfAllowableAccounts = new HashSet<Account>(validAccounts.size()); |
| 137 | for (Parcelable parcelable : validAccounts) { |
| 138 | setOfAllowableAccounts.add((Account)parcelable); |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | // Read the validAccountTypes, if present, and add them to the setOfAllowableAccountTypes |
| 143 | Set<String> setOfAllowableAccountTypes = null; |
| 144 | final ArrayList<String> validAccountTypes = |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 145 | intent.getStringArrayListExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY); |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 146 | if (validAccountTypes != null) { |
| 147 | setOfAllowableAccountTypes = new HashSet<String>(validAccountTypes.size()); |
| 148 | for (String type : validAccountTypes) { |
| 149 | setOfAllowableAccountTypes.add(type); |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | // Create a list of AccountInfo objects for each account that is allowable. Filter out |
| 154 | // accounts that don't match the allowable types, if provided, or that don't match the |
| 155 | // allowable accounts, if provided. |
| 156 | final Account[] accounts = accountManager.getAccounts(); |
| 157 | mAccountInfos = new ArrayList<AccountInfo>(accounts.length); |
| 158 | for (Account account : accounts) { |
| 159 | if (setOfAllowableAccounts != null |
| 160 | && !setOfAllowableAccounts.contains(account)) { |
| 161 | continue; |
| 162 | } |
| 163 | if (setOfAllowableAccountTypes != null |
| 164 | && !setOfAllowableAccountTypes.contains(account.type)) { |
| 165 | continue; |
| 166 | } |
| 167 | mAccountInfos.add(new AccountInfo(account, |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 168 | getDrawableForType(typeToAuthDescription, account.type), |
| 169 | account.equals(selectedAccount))); |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 170 | } |
| 171 | |
| 172 | // If there are no allowable accounts go directly to add account |
| 173 | if (mAccountInfos.isEmpty()) { |
| 174 | startChooseAccountTypeActivity(); |
| 175 | return; |
| 176 | } |
| 177 | |
| 178 | // if there is only one allowable account return it |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 179 | if (!intent.getBooleanExtra(EXTRA_ALWAYS_PROMPT_FOR_ACCOUNT, false) |
| 180 | && mAccountInfos.size() == 1) { |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 181 | Account account = mAccountInfos.get(0).account; |
| 182 | setResultAndFinish(account.name, account.type); |
| 183 | return; |
| 184 | } |
| 185 | |
| 186 | // there is more than one allowable account. initialize the list adapter to allow |
| 187 | // the user to select an account. |
| 188 | ListView list = (ListView) findViewById(android.R.id.list); |
| 189 | list.setAdapter(new AccountArrayAdapter(this, |
| 190 | android.R.layout.simple_list_item_1, mAccountInfos)); |
| 191 | list.setChoiceMode(ListView.CHOICE_MODE_SINGLE); |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 192 | list.setOnItemClickListener(new AdapterView.OnItemClickListener() { |
| 193 | public void onItemClick(AdapterView<?> parent, View v, int position, long id) { |
| 194 | onListItemClick((ListView)parent, v, position, id); |
| 195 | } |
| 196 | }); |
| 197 | |
| 198 | // set the listener for the addAccount button |
| 199 | Button addAccountButton = (Button) findViewById(R.id.addAccount); |
| 200 | addAccountButton.setOnClickListener(new View.OnClickListener() { |
| 201 | public void onClick(final View v) { |
| 202 | startChooseAccountTypeActivity(); |
| 203 | } |
| 204 | }); |
| 205 | } |
| 206 | |
| 207 | // Called when the choose account type activity (for adding an account) returns. |
| 208 | // If it was a success read the account and set it in the result. In all cases |
| 209 | // return the result and finish this activity. |
| 210 | @Override |
| 211 | protected void onActivityResult(final int requestCode, final int resultCode, |
| 212 | final Intent data) { |
| 213 | if (resultCode == RESULT_OK && data != null) { |
| 214 | String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME); |
| 215 | String accountType = data.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE); |
| 216 | if (accountName != null && accountType != null) { |
| 217 | setResultAndFinish(accountName, accountType); |
| 218 | return; |
| 219 | } |
| 220 | } |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 221 | Log.d(TAG, "ChooseTypeAndAccountActivity.onActivityResult: canceled"); |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 222 | setResult(Activity.RESULT_CANCELED); |
| 223 | finish(); |
| 224 | } |
| 225 | |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 226 | |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 227 | private Drawable getDrawableForType( |
| 228 | final HashMap<String, AuthenticatorDescription> typeToAuthDescription, |
| 229 | String accountType) { |
| 230 | Drawable icon = null; |
| 231 | if (typeToAuthDescription.containsKey(accountType)) { |
| 232 | try { |
| 233 | AuthenticatorDescription desc = typeToAuthDescription.get(accountType); |
| 234 | Context authContext = createPackageContext(desc.packageName, 0); |
| 235 | icon = authContext.getResources().getDrawable(desc.iconId); |
| 236 | } catch (PackageManager.NameNotFoundException e) { |
| 237 | // Nothing we can do much here, just log |
| 238 | if (Log.isLoggable(TAG, Log.WARN)) { |
| 239 | Log.w(TAG, "No icon name for account type " + accountType); |
| 240 | } |
| 241 | } catch (Resources.NotFoundException e) { |
| 242 | // Nothing we can do much here, just log |
| 243 | if (Log.isLoggable(TAG, Log.WARN)) { |
| 244 | Log.w(TAG, "No icon resource for account type " + accountType); |
| 245 | } |
| 246 | } |
| 247 | } |
| 248 | return icon; |
| 249 | } |
| 250 | |
| 251 | protected void onListItemClick(ListView l, View v, int position, long id) { |
| 252 | AccountInfo accountInfo = mAccountInfos.get(position); |
| 253 | Log.d(TAG, "selected account " + accountInfo.account); |
| 254 | setResultAndFinish(accountInfo.account.name, accountInfo.account.type); |
| 255 | } |
| 256 | |
| 257 | private void setResultAndFinish(final String accountName, final String accountType) { |
| 258 | Bundle bundle = new Bundle(); |
| 259 | bundle.putString(AccountManager.KEY_ACCOUNT_NAME, accountName); |
| 260 | bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, accountType); |
| 261 | setResult(Activity.RESULT_OK, new Intent().putExtras(bundle)); |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 262 | Log.d(TAG, "ChooseTypeAndAccountActivity.setResultAndFinish: " |
| 263 | + "selected account " + accountName + ", " + accountType); |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 264 | finish(); |
| 265 | } |
| 266 | |
| 267 | private void startChooseAccountTypeActivity() { |
| 268 | final Intent intent = new Intent(this, ChooseAccountTypeActivity.class); |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 269 | intent.putStringArrayListExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY, |
| 270 | getIntent().getStringArrayListExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY)); |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 271 | intent.putExtra(EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE, |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 272 | getIntent().getBundleExtra(EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE)); |
| 273 | intent.putExtra(EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY, |
| 274 | getIntent().getStringArrayExtra(EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY)); |
| 275 | intent.putExtra(EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING, |
| 276 | getIntent().getStringArrayExtra(EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING)); |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 277 | startActivityForResult(intent, 0); |
| 278 | } |
| 279 | |
| 280 | private static class AccountInfo { |
| 281 | final Account account; |
| 282 | final Drawable drawable; |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 283 | private final boolean checked; |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 284 | |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 285 | AccountInfo(Account account, Drawable drawable, boolean checked) { |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 286 | this.account = account; |
| 287 | this.drawable = drawable; |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 288 | this.checked = checked; |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 289 | } |
| 290 | } |
| 291 | |
| 292 | private static class ViewHolder { |
| 293 | ImageView icon; |
| 294 | TextView text; |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 295 | ImageView checkmark; |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 296 | } |
| 297 | |
| 298 | private static class AccountArrayAdapter extends ArrayAdapter<AccountInfo> { |
| 299 | private LayoutInflater mLayoutInflater; |
| 300 | private ArrayList<AccountInfo> mInfos; |
| 301 | |
| 302 | public AccountArrayAdapter(Context context, int textViewResourceId, |
| 303 | ArrayList<AccountInfo> infos) { |
| 304 | super(context, textViewResourceId, infos); |
| 305 | mInfos = infos; |
| 306 | mLayoutInflater = (LayoutInflater) context.getSystemService( |
| 307 | Context.LAYOUT_INFLATER_SERVICE); |
| 308 | } |
| 309 | |
| 310 | @Override |
| 311 | public View getView(int position, View convertView, ViewGroup parent) { |
| 312 | ViewHolder holder; |
| 313 | |
| 314 | if (convertView == null) { |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 315 | convertView = mLayoutInflater.inflate(R.layout.choose_selected_account_row, null); |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 316 | holder = new ViewHolder(); |
| 317 | holder.text = (TextView) convertView.findViewById(R.id.account_row_text); |
| 318 | holder.icon = (ImageView) convertView.findViewById(R.id.account_row_icon); |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 319 | holder.checkmark = (ImageView) convertView.findViewById(R.id.account_row_checkmark); |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 320 | convertView.setTag(holder); |
| 321 | } else { |
| 322 | holder = (ViewHolder) convertView.getTag(); |
| 323 | } |
| 324 | |
| 325 | holder.text.setText(mInfos.get(position).account.name); |
| 326 | holder.icon.setImageDrawable(mInfos.get(position).drawable); |
Fred Quintana | b04fe4e | 2011-09-16 21:17:21 -0700 | [diff] [blame^] | 327 | final int displayCheckmark = |
| 328 | mInfos.get(position).checked ? View.VISIBLE : View.INVISIBLE; |
| 329 | holder.checkmark.setVisibility(displayCheckmark); |
Fred Quintana | 1121bb5 | 2011-09-14 23:19:35 -0700 | [diff] [blame] | 330 | return convertView; |
| 331 | } |
| 332 | } |
| 333 | } |