/* * Copyright (C) 2018 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.os.UserManager.DISALLOW_MODIFY_ACCOUNTS; import static com.android.car.settings.enterprise.ActionDisabledByAdminDialogFragment.DISABLED_BY_ADMIN_CONFIRM_DIALOG_TAG; import static com.android.car.settings.enterprise.EnterpriseUtils.hasUserRestrictionByDpm; import static com.android.car.settings.enterprise.EnterpriseUtils.hasUserRestrictionByUm; import android.accounts.AccountManager; import android.accounts.AccountManagerCallback; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.car.drivingstate.CarUxRestrictions; import android.content.Context; import android.content.Intent; import android.os.Bundle; import androidx.annotation.CallSuper; import androidx.annotation.VisibleForTesting; import com.android.car.settings.R; import com.android.car.settings.common.ActivityResultCallback; import com.android.car.settings.common.ConfirmationDialogFragment; import com.android.car.settings.common.ErrorDialog; import com.android.car.settings.common.FragmentController; import com.android.car.settings.common.Logger; import com.android.car.settings.enterprise.EnterpriseUtils; import com.android.car.settings.profiles.ProfileHelper; import java.io.IOException; /** * Controller for the preference that shows the details of an account. It also handles a secondary * button for account removal. */ public class AccountDetailsPreferenceController extends AccountDetailsBasePreferenceController implements ActivityResultCallback { private static final Logger LOG = new Logger(AccountDetailsPreferenceController.class); private static final int REMOVE_ACCOUNT_REQUEST = 101; private AccountManagerCallback mCallback = future -> { // If already out of this screen, don't proceed. if (!isStarted()) { return; } boolean done = true; boolean success = false; try { Bundle result = future.getResult(); Intent removeIntent = result.getParcelable(AccountManager.KEY_INTENT); if (removeIntent != null) { done = false; getFragmentController().startActivityForResult(removeIntent, REMOVE_ACCOUNT_REQUEST, this); } else { success = future.getResult().getBoolean(AccountManager.KEY_BOOLEAN_RESULT); } } catch (OperationCanceledException | IOException | AuthenticatorException e) { LOG.v("removeAccount error: " + e); } if (done) { if (!success) { showErrorDialog(); } else { getFragmentController().goBack(); } } }; private ConfirmationDialogFragment.ConfirmListener mConfirmListener = arguments -> { AccountManager.get(getContext()).removeAccountAsUser(getAccount(), /* activity= */ null, mCallback, null, getUserHandle()); ConfirmationDialogFragment dialog = (ConfirmationDialogFragment) getFragmentController().findDialogByTag( ConfirmationDialogFragment.TAG); if (dialog != null) { dialog.dismiss(); } }; public AccountDetailsPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions) { super(context, preferenceKey, fragmentController, uxRestrictions); } @Override @CallSuper protected void onCreateInternal() { super.onCreateInternal(); ConfirmationDialogFragment dialog = (ConfirmationDialogFragment) getFragmentController().findDialogByTag( ConfirmationDialogFragment.TAG); ConfirmationDialogFragment.resetListeners( dialog, mConfirmListener, /* rejectListener= */ null, /* neutralListener= */ null); getPreference().setSecondaryActionVisible( getSecondaryActionAvailabilityStatus() == AVAILABLE || getSecondaryActionAvailabilityStatus() == AVAILABLE_FOR_VIEWING); getPreference().setSecondaryActionEnabled( getSecondaryActionAvailabilityStatus() != DISABLED_FOR_PROFILE); getPreference().setOnSecondaryActionClickListener(this::onRemoveAccountClicked); } @Override public void processActivityResult(int requestCode, int resultCode, Intent data) { if (!isStarted()) { return; } if (requestCode == REMOVE_ACCOUNT_REQUEST) { // Activity result code may not adequately reflect the account removal status, so // KEY_BOOLEAN_RESULT is used here instead. If the intent does not have this value // included, a no-op will be performed and the account update listener in // {@link AccountDetailsFragment} will still handle the back navigation upon removal. if (data != null && data.hasExtra(AccountManager.KEY_BOOLEAN_RESULT)) { boolean success = data.getBooleanExtra(AccountManager.KEY_BOOLEAN_RESULT, false); if (success) { getFragmentController().goBack(); } else { showErrorDialog(); } } } } private int getSecondaryActionAvailabilityStatus() { ProfileHelper profileHelper = getProfileHelper(); if (profileHelper.canCurrentProcessModifyAccounts()) { return AVAILABLE; } if (profileHelper.isDemoOrGuest() || hasUserRestrictionByUm(getContext(), DISALLOW_MODIFY_ACCOUNTS)) { return DISABLED_FOR_PROFILE; } return AVAILABLE_FOR_VIEWING; } private void onRemoveAccountClicked() { if (hasUserRestrictionByDpm(getContext(), DISALLOW_MODIFY_ACCOUNTS)) { showActionDisabledByAdminDialog(); } ConfirmationDialogFragment dialog = new ConfirmationDialogFragment.Builder(getContext()) .setTitle(R.string.really_remove_account_title) .setMessage(R.string.really_remove_account_message) .setNegativeButton(android.R.string.cancel, null) .setPositiveButton(R.string.remove_account_title, mConfirmListener) .build(); getFragmentController().showDialog(dialog, ConfirmationDialogFragment.TAG); } private void showErrorDialog() { getFragmentController().showDialog( ErrorDialog.newInstance(R.string.remove_account_error_title), /* tag= */ null); } private void showActionDisabledByAdminDialog() { getFragmentController().showDialog( EnterpriseUtils.getActionDisabledByAdminDialog(getContext(), DISALLOW_MODIFY_ACCOUNTS), DISABLED_BY_ADMIN_CONFIRM_DIALOG_TAG); } @VisibleForTesting ProfileHelper getProfileHelper() { return ProfileHelper.getInstance(getContext()); } }