blob: 85e942b199058c68a9eaa025deec49b8d3190ea6 [file] [log] [blame]
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accounts;
import static android.content.Intent.EXTRA_USER;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import android.widget.Toast;
import com.android.settings.R;
import com.android.settings.Settings;
import com.android.settings.Utils;
import com.android.settings.password.ChooseLockSettingsHelper;
import java.io.IOException;
/**
* Entry point Activity for account setup. Works as follows
*
* 1) When the other Activities launch this Activity, it launches {@link ChooseAccountActivity}
* without showing anything.
* 2) After receiving an account type from ChooseAccountActivity, this Activity launches the
* account setup specified by AccountManager.
* 3) After the account setup, this Activity finishes without showing anything.
*
* Note:
* Previously this Activity did what {@link ChooseAccountActivity} does right now, but we
* currently delegate the work to the other Activity. When we let this Activity do that work, users
* would see the list of account types when leaving this Activity, since the UI is already ready
* when returning from each account setup, which doesn't look good.
*
* An extra {@link UserHandle} can be specified in the intent as {@link EXTRA_USER}, if the user for
* which the action needs to be performed is different to the one the Settings App will run in.
*/
public class AddAccountSettings extends Activity {
/**
*
*/
private static final String KEY_ADD_CALLED = "AddAccountCalled";
/**
* Extra parameter to identify the caller. Applications may display a
* different UI if the calls is made from Settings or from a specific
* application.
*/
private static final String KEY_CALLER_IDENTITY = "pendingIntent";
private static final String SHOULD_NOT_RESOLVE = "SHOULDN'T RESOLVE!";
private static final String TAG = "AddAccountSettings";
/* package */ static final String EXTRA_SELECTED_ACCOUNT = "selected_account";
// show additional info regarding the use of a device with multiple users
static final String EXTRA_HAS_MULTIPLE_USERS = "hasMultipleUsers";
private static final int CHOOSE_ACCOUNT_REQUEST = 1;
private static final int ADD_ACCOUNT_REQUEST = 2;
private static final int UNLOCK_WORK_PROFILE_REQUEST = 3;
private PendingIntent mPendingIntent;
private final AccountManagerCallback<Bundle> mCallback = new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> future) {
boolean done = true;
try {
Bundle bundle = future.getResult();
//bundle.keySet();
Intent intent = (Intent) bundle.get(AccountManager.KEY_INTENT);
if (intent != null) {
done = false;
Bundle addAccountOptions = new Bundle();
addAccountOptions.putParcelable(KEY_CALLER_IDENTITY, mPendingIntent);
addAccountOptions.putBoolean(EXTRA_HAS_MULTIPLE_USERS,
Utils.hasMultipleUsers(AddAccountSettings.this));
addAccountOptions.putParcelable(EXTRA_USER, mUserHandle);
intent.putExtras(addAccountOptions)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivityForResultAsUser(
new Intent(intent), ADD_ACCOUNT_REQUEST, mUserHandle);
} else {
setResult(RESULT_OK);
if (mPendingIntent != null) {
mPendingIntent.cancel();
mPendingIntent = null;
}
}
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "account added: " + bundle);
} catch (OperationCanceledException e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount was canceled");
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount failed: " + e);
} catch (AuthenticatorException e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount failed: " + e);
} finally {
if (done) {
finish();
}
}
}
};
private boolean mAddAccountCalled = false;
private UserHandle mUserHandle;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mAddAccountCalled = savedInstanceState.getBoolean(KEY_ADD_CALLED);
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "restored");
}
final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
mUserHandle = Utils.getSecureTargetUser(getActivityToken(), um, null /* arguments */,
getIntent().getExtras());
if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, mUserHandle)) {
// We aren't allowed to add an account.
Toast.makeText(this, R.string.user_cannot_add_accounts_message, Toast.LENGTH_LONG)
.show();
finish();
return;
}
if (mAddAccountCalled) {
// We already called add account - maybe the callback was lost.
finish();
return;
}
if (Utils.startQuietModeDialogIfNecessary(this, um, mUserHandle.getIdentifier())) {
finish();
return;
}
if (um.isUserUnlocked(mUserHandle)) {
requestChooseAccount();
} else {
// If the user is locked by fbe: we couldn't start the authenticator. So we must ask the
// user to unlock it first.
final ChooseLockSettingsHelper.Builder builder =
new ChooseLockSettingsHelper.Builder(this);
final boolean launched = builder.setRequestCode(UNLOCK_WORK_PROFILE_REQUEST)
.setTitle(getString(R.string.unlock_set_unlock_launch_picker_title))
.setUserId(mUserHandle.getIdentifier())
.show();
if (!launched) {
requestChooseAccount();
}
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case UNLOCK_WORK_PROFILE_REQUEST:
if (resultCode == Activity.RESULT_OK) {
requestChooseAccount();
} else {
finish();
}
break;
case CHOOSE_ACCOUNT_REQUEST:
if (resultCode == RESULT_CANCELED) {
if (data != null) {
startActivityAsUser(data, mUserHandle);
}
setResult(resultCode);
finish();
return;
}
// Go to account setup screen. finish() is called inside mCallback.
addAccount(data.getStringExtra(EXTRA_SELECTED_ACCOUNT));
break;
case ADD_ACCOUNT_REQUEST:
setResult(resultCode);
if (mPendingIntent != null) {
mPendingIntent.cancel();
mPendingIntent = null;
}
finish();
break;
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(KEY_ADD_CALLED, mAddAccountCalled);
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "saved");
}
private void requestChooseAccount() {
final String[] authorities =
getIntent().getStringArrayExtra(AccountPreferenceBase.AUTHORITIES_FILTER_KEY);
final String[] accountTypes =
getIntent().getStringArrayExtra(AccountPreferenceBase.ACCOUNT_TYPES_FILTER_KEY);
final Intent intent = new Intent(this, Settings.ChooseAccountActivity.class);
if (authorities != null) {
intent.putExtra(AccountPreferenceBase.AUTHORITIES_FILTER_KEY, authorities);
}
if (accountTypes != null) {
intent.putExtra(AccountPreferenceBase.ACCOUNT_TYPES_FILTER_KEY, accountTypes);
}
intent.putExtra(EXTRA_USER, mUserHandle);
startActivityForResult(intent, CHOOSE_ACCOUNT_REQUEST);
}
private void addAccount(String accountType) {
Bundle addAccountOptions = new Bundle();
/*
* The identityIntent is for the purposes of establishing the identity
* of the caller and isn't intended for launching activities, services
* or broadcasts.
*
* Unfortunately for legacy reasons we still need to support this. But
* we can disable the intent so that 3rd party authenticators can't
* fill in addressing information and launch arbitrary actions.
*/
Intent identityIntent = new Intent();
identityIntent.setComponent(new ComponentName(SHOULD_NOT_RESOLVE, SHOULD_NOT_RESOLVE));
identityIntent.setAction(SHOULD_NOT_RESOLVE);
identityIntent.addCategory(SHOULD_NOT_RESOLVE);
mPendingIntent = PendingIntent.getBroadcast(this, 0, identityIntent,
PendingIntent.FLAG_IMMUTABLE);
addAccountOptions.putParcelable(KEY_CALLER_IDENTITY, mPendingIntent);
addAccountOptions.putBoolean(EXTRA_HAS_MULTIPLE_USERS, Utils.hasMultipleUsers(this));
AccountManager.get(this).addAccountAsUser(
accountType,
null, /* authTokenType */
null, /* requiredFeatures */
addAccountOptions,
null,
mCallback,
null /* handler */,
mUserHandle);
mAddAccountCalled = true;
}
}