1 /* 2 * Copyright (C) 2008 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 17 package com.android.settings.accounts; 18 19 import android.accounts.AccountManager; 20 import android.accounts.AccountManagerCallback; 21 import android.accounts.AccountManagerFuture; 22 import android.accounts.AuthenticatorException; 23 import android.accounts.OperationCanceledException; 24 import android.app.Activity; 25 import android.app.PendingIntent; 26 import android.content.ComponentName; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.os.Bundle; 30 import android.os.UserHandle; 31 import android.os.UserManager; 32 import android.util.Log; 33 import android.widget.Toast; 34 35 import com.android.settings.R; 36 import com.android.settings.Utils; 37 38 import java.io.IOException; 39 40 import static android.content.Intent.EXTRA_USER; 41 42 /** 43 * Entry point Activity for account setup. Works as follows 44 * 45 * 1) When the other Activities launch this Activity, it launches {@link ChooseAccountActivity} 46 * without showing anything. 47 * 2) After receiving an account type from ChooseAccountActivity, this Activity launches the 48 * account setup specified by AccountManager. 49 * 3) After the account setup, this Activity finishes without showing anything. 50 * 51 * Note: 52 * Previously this Activity did what {@link ChooseAccountActivity} does right now, but we 53 * currently delegate the work to the other Activity. When we let this Activity do that work, users 54 * would see the list of account types when leaving this Activity, since the UI is already ready 55 * when returning from each account setup, which doesn't look good. 56 * 57 * An extra {@link UserHandle} can be specified in the intent as {@link EXTRA_USER}, if the user for 58 * which the action needs to be performed is different to the one the Settings App will run in. 59 */ 60 public class AddAccountSettings extends Activity { 61 /** 62 * 63 */ 64 private static final String KEY_ADD_CALLED = "AddAccountCalled"; 65 66 /** 67 * Extra parameter to identify the caller. Applications may display a 68 * different UI if the calls is made from Settings or from a specific 69 * application. 70 */ 71 private static final String KEY_CALLER_IDENTITY = "pendingIntent"; 72 private static final String SHOULD_NOT_RESOLVE = "SHOULDN'T RESOLVE!"; 73 74 private static final String TAG = "AccountSettings"; 75 76 /* package */ static final String EXTRA_SELECTED_ACCOUNT = "selected_account"; 77 78 // show additional info regarding the use of a device with multiple users 79 static final String EXTRA_HAS_MULTIPLE_USERS = "hasMultipleUsers"; 80 81 private static final int CHOOSE_ACCOUNT_REQUEST = 1; 82 private static final int ADD_ACCOUNT_REQUEST = 2; 83 84 private PendingIntent mPendingIntent; 85 86 private final AccountManagerCallback<Bundle> mCallback = new AccountManagerCallback<Bundle>() { 87 @Override 88 public void run(AccountManagerFuture<Bundle> future) { 89 boolean done = true; 90 try { 91 Bundle bundle = future.getResult(); 92 //bundle.keySet(); 93 Intent intent = (Intent) bundle.get(AccountManager.KEY_INTENT); 94 if (intent != null) { 95 done = false; 96 Bundle addAccountOptions = new Bundle(); 97 addAccountOptions.putParcelable(KEY_CALLER_IDENTITY, mPendingIntent); 98 addAccountOptions.putBoolean(EXTRA_HAS_MULTIPLE_USERS, 99 Utils.hasMultipleUsers(AddAccountSettings.this)); 100 addAccountOptions.putParcelable(EXTRA_USER, mUserHandle); 101 intent.putExtras(addAccountOptions); 102 startActivityForResultAsUser(intent, ADD_ACCOUNT_REQUEST, mUserHandle); 103 } else { 104 setResult(RESULT_OK); 105 if (mPendingIntent != null) { 106 mPendingIntent.cancel(); 107 mPendingIntent = null; 108 } 109 } 110 111 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "account added: " + bundle); 112 } catch (OperationCanceledException e) { 113 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount was canceled"); 114 } catch (IOException e) { 115 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount failed: " + e); 116 } catch (AuthenticatorException e) { 117 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount failed: " + e); 118 } finally { 119 if (done) { 120 finish(); 121 } 122 } 123 } 124 }; 125 126 private boolean mAddAccountCalled = false; 127 private UserHandle mUserHandle; 128 129 @Override onCreate(Bundle savedInstanceState)130 public void onCreate(Bundle savedInstanceState) { 131 super.onCreate(savedInstanceState); 132 133 if (savedInstanceState != null) { 134 mAddAccountCalled = savedInstanceState.getBoolean(KEY_ADD_CALLED); 135 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "restored"); 136 } 137 138 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); 139 mUserHandle = Utils.getSecureTargetUser(getActivityToken(), um, null /* arguments */, 140 getIntent().getExtras()); 141 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, mUserHandle)) { 142 // We aren't allowed to add an account. 143 Toast.makeText(this, R.string.user_cannot_add_accounts_message, Toast.LENGTH_LONG) 144 .show(); 145 finish(); 146 return; 147 } 148 if (mAddAccountCalled) { 149 // We already called add account - maybe the callback was lost. 150 finish(); 151 return; 152 } 153 final String[] authorities = 154 getIntent().getStringArrayExtra(AccountPreferenceBase.AUTHORITIES_FILTER_KEY); 155 final String[] accountTypes = 156 getIntent().getStringArrayExtra(AccountPreferenceBase.ACCOUNT_TYPES_FILTER_KEY); 157 final Intent intent = new Intent(this, ChooseAccountActivity.class); 158 if (authorities != null) { 159 intent.putExtra(AccountPreferenceBase.AUTHORITIES_FILTER_KEY, authorities); 160 } 161 if (accountTypes != null) { 162 intent.putExtra(AccountPreferenceBase.ACCOUNT_TYPES_FILTER_KEY, accountTypes); 163 } 164 intent.putExtra(EXTRA_USER, mUserHandle); 165 startActivityForResult(intent, CHOOSE_ACCOUNT_REQUEST); 166 } 167 168 @Override onActivityResult(int requestCode, int resultCode, Intent data)169 public void onActivityResult(int requestCode, int resultCode, Intent data) { 170 switch (requestCode) { 171 case CHOOSE_ACCOUNT_REQUEST: 172 if (resultCode == RESULT_CANCELED) { 173 setResult(resultCode); 174 finish(); 175 return; 176 } 177 // Go to account setup screen. finish() is called inside mCallback. 178 addAccount(data.getStringExtra(EXTRA_SELECTED_ACCOUNT)); 179 break; 180 case ADD_ACCOUNT_REQUEST: 181 setResult(resultCode); 182 if (mPendingIntent != null) { 183 mPendingIntent.cancel(); 184 mPendingIntent = null; 185 } 186 finish(); 187 break; 188 } 189 } 190 191 @Override onSaveInstanceState(Bundle outState)192 protected void onSaveInstanceState(Bundle outState) { 193 super.onSaveInstanceState(outState); 194 outState.putBoolean(KEY_ADD_CALLED, mAddAccountCalled); 195 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "saved"); 196 } 197 addAccount(String accountType)198 private void addAccount(String accountType) { 199 Bundle addAccountOptions = new Bundle(); 200 /* 201 * The identityIntent is for the purposes of establishing the identity 202 * of the caller and isn't intended for launching activities, services 203 * or broadcasts. 204 * 205 * Unfortunately for legacy reasons we still need to support this. But 206 * we can cripple the intent so that 3rd party authenticators can't 207 * fill in addressing information and launch arbitrary actions. 208 */ 209 Intent identityIntent = new Intent(); 210 identityIntent.setComponent(new ComponentName(SHOULD_NOT_RESOLVE, SHOULD_NOT_RESOLVE)); 211 identityIntent.setAction(SHOULD_NOT_RESOLVE); 212 identityIntent.addCategory(SHOULD_NOT_RESOLVE); 213 214 mPendingIntent = PendingIntent.getBroadcast(this, 0, identityIntent, 0); 215 addAccountOptions.putParcelable(KEY_CALLER_IDENTITY, mPendingIntent); 216 addAccountOptions.putBoolean(EXTRA_HAS_MULTIPLE_USERS, Utils.hasMultipleUsers(this)); 217 AccountManager.get(this).addAccountAsUser( 218 accountType, 219 null, /* authTokenType */ 220 null, /* requiredFeatures */ 221 addAccountOptions, 222 null, 223 mCallback, 224 null /* handler */, 225 mUserHandle); 226 mAddAccountCalled = true; 227 } 228 } 229