/* * 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 * *
    *
  1. After receiving an account type from ChooseAccountFragment, this Activity launches the * account setup specified by AccountManager. *
  2. 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; } }