/*
* Copyright (C) 2017 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.car.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.widget.Toast;
import com.android.car.settings.R;
import com.android.car.settings.common.Logger;
import java.io.IOException;
/**
* Entry point Activity for account setup. Works as follows
*
*
* - After receiving an account type from ChooseAccountFragment, this Activity launches the
* account setup specified by AccountManager.
*
- After the account setup, this Activity finishes without showing anything.
*
*/
public class AddAccountActivity extends Activity {
/**
* A boolean to keep the state of whether add account has already been called.
*/
private static final String KEY_ADD_CALLED = "AddAccountCalled";
/**
* Extra parameter to identify the caller. Applications may display a
* different UI if the call 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 Logger LOG = new Logger(AddAccountActivity.class);
private static final String ALLOW_SKIP = "allowSkip";
/* 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";
// Need a specific request code for add account activity.
private static final int ADD_ACCOUNT_REQUEST = 2001;
private UserManager mUserManager;
private UserHandle mUserHandle;
private PendingIntent mPendingIntent;
private boolean mAddAccountCalled;
private final AccountManagerCallback mCallback = new AccountManagerCallback() {
@Override
public void run(AccountManagerFuture future) {
if (!future.isDone()) {
LOG.v("Account manager future is not done.");
finish();
}
boolean done = true;
try {
Bundle result = future.getResult();
Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
if (intent != null) {
done = false;
Bundle addAccountOptions = new Bundle();
addAccountOptions.putBoolean(EXTRA_HAS_MULTIPLE_USERS,
hasMultipleUsers(AddAccountActivity.this));
addAccountOptions.putParcelable(EXTRA_USER, mUserHandle);
intent.putExtras(addAccountOptions);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityForResultAsUser(
intent, ADD_ACCOUNT_REQUEST, mUserHandle);
} else {
setResult(RESULT_OK);
if (mPendingIntent != null) {
mPendingIntent.cancel();
mPendingIntent = null;
}
}
LOG.v("account added: " + result);
} catch (OperationCanceledException | IOException | AuthenticatorException e) {
LOG.v("addAccount error: " + e);
} finally {
if (done) {
finish();
}
}
}
};
/**
* Creates an intent to start the {@link AddAccountActivity} to add an account of the given
* account type.
*/
public static Intent createAddAccountActivityIntent(Context context, String accountType) {
Intent intent = new Intent(context, AddAccountActivity.class);
intent.putExtra(EXTRA_SELECTED_ACCOUNT, accountType);
return intent;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(KEY_ADD_CALLED, mAddAccountCalled);
LOG.v("saved");
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mAddAccountCalled = savedInstanceState.getBoolean(KEY_ADD_CALLED);
LOG.v("Restored from previous add account call: " + mAddAccountCalled);
}
mUserManager = UserManager.get(getApplicationContext());
mUserHandle = UserHandle.of(UserHandle.myUserId());
if (mUserManager.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
// 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;
}
addAccount(getIntent().getStringExtra(EXTRA_SELECTED_ACCOUNT));
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
setResult(resultCode);
if (mPendingIntent != null) {
mPendingIntent.cancel();
mPendingIntent = null;
}
finish();
}
private void addAccount(String accountType) {
Bundle addAccountOptions = new Bundle();
addAccountOptions.putBoolean(EXTRA_HAS_MULTIPLE_USERS, hasMultipleUsers(this));
addAccountOptions.putBoolean(ALLOW_SKIP, true);
/*
* The identityIntent is for the purposes of establishing the identity
* of the caller and isn't intended for launching activities, services
* or broadcasts.
*/
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);
AccountManager.get(this).addAccountAsUser(
accountType,
/* authTokenType= */ null,
/* requiredFeatures= */ null,
addAccountOptions,
null,
mCallback,
/* handler= */ null,
mUserHandle);
mAddAccountCalled = true;
}
private boolean hasMultipleUsers(Context context) {
return ((UserManager) context.getSystemService(Context.USER_SERVICE))
.getUsers().size() > 1;
}
}