• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.car.settings.accounts;
18 
19 import static android.os.UserManager.DISALLOW_MODIFY_ACCOUNTS;
20 
21 import static com.android.car.settings.enterprise.ActionDisabledByAdminDialogFragment.DISABLED_BY_ADMIN_CONFIRM_DIALOG_TAG;
22 import static com.android.car.settings.enterprise.EnterpriseUtils.hasUserRestrictionByDpm;
23 import static com.android.car.settings.enterprise.EnterpriseUtils.hasUserRestrictionByUm;
24 
25 import android.accounts.AccountManager;
26 import android.accounts.AccountManagerCallback;
27 import android.accounts.AuthenticatorException;
28 import android.accounts.OperationCanceledException;
29 import android.car.drivingstate.CarUxRestrictions;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.os.Bundle;
33 
34 import androidx.annotation.CallSuper;
35 import androidx.annotation.VisibleForTesting;
36 
37 import com.android.car.settings.R;
38 import com.android.car.settings.common.ActivityResultCallback;
39 import com.android.car.settings.common.ConfirmationDialogFragment;
40 import com.android.car.settings.common.ErrorDialog;
41 import com.android.car.settings.common.FragmentController;
42 import com.android.car.settings.common.Logger;
43 import com.android.car.settings.enterprise.EnterpriseUtils;
44 import com.android.car.settings.profiles.ProfileHelper;
45 
46 import java.io.IOException;
47 
48 /**
49  * Controller for the preference that shows the details of an account. It also handles a secondary
50  * button for account removal.
51  */
52 public class AccountDetailsPreferenceController extends AccountDetailsBasePreferenceController
53         implements ActivityResultCallback {
54     private static final Logger LOG = new Logger(AccountDetailsPreferenceController.class);
55     private static final int REMOVE_ACCOUNT_REQUEST = 101;
56 
57     private AccountManagerCallback<Bundle> mCallback =
58             future -> {
59                 // If already out of this screen, don't proceed.
60                 if (!isStarted()) {
61                     return;
62                 }
63 
64                 boolean done = true;
65                 boolean success = false;
66                 try {
67                     Bundle result = future.getResult();
68                     Intent removeIntent = result.getParcelable(AccountManager.KEY_INTENT);
69                     if (removeIntent != null) {
70                         done = false;
71                         getFragmentController().startActivityForResult(new Intent(removeIntent),
72                                 REMOVE_ACCOUNT_REQUEST, this);
73                     } else {
74                         success = future.getResult().getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
75                     }
76                 } catch (OperationCanceledException | IOException | AuthenticatorException e) {
77                     LOG.v("removeAccount error: " + e);
78                 }
79                 if (done) {
80                     if (!success) {
81                         showErrorDialog();
82                     } else {
83                         getFragmentController().goBack();
84                     }
85                 }
86             };
87 
88     private ConfirmationDialogFragment.ConfirmListener mConfirmListener = arguments -> {
89         AccountManager.get(getContext()).removeAccountAsUser(getAccount(), /* activity= */ null,
90                 mCallback, null, getUserHandle());
91         ConfirmationDialogFragment dialog =
92                 (ConfirmationDialogFragment) getFragmentController().findDialogByTag(
93                         ConfirmationDialogFragment.TAG);
94         if (dialog != null) {
95             dialog.dismiss();
96         }
97     };
98 
AccountDetailsPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)99     public AccountDetailsPreferenceController(Context context, String preferenceKey,
100             FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
101         super(context, preferenceKey, fragmentController, uxRestrictions);
102     }
103 
104     @Override
105     @CallSuper
onCreateInternal()106     protected void onCreateInternal() {
107         super.onCreateInternal();
108         ConfirmationDialogFragment dialog =
109                 (ConfirmationDialogFragment) getFragmentController().findDialogByTag(
110                         ConfirmationDialogFragment.TAG);
111         ConfirmationDialogFragment.resetListeners(
112                 dialog,
113                 mConfirmListener,
114                 /* rejectListener= */ null,
115                 /* neutralListener= */ null);
116 
117         getPreference().setSecondaryActionVisible(
118                 getSecondaryActionAvailabilityStatus() == AVAILABLE
119                         || getSecondaryActionAvailabilityStatus() == AVAILABLE_FOR_VIEWING);
120         getPreference().setSecondaryActionEnabled(
121                 getSecondaryActionAvailabilityStatus() != DISABLED_FOR_PROFILE);
122 
123         getPreference().setOnSecondaryActionClickListener(this::onRemoveAccountClicked);
124     }
125 
126     @Override
processActivityResult(int requestCode, int resultCode, Intent data)127     public void processActivityResult(int requestCode, int resultCode, Intent data) {
128         if (!isStarted()) {
129             return;
130         }
131         if (requestCode == REMOVE_ACCOUNT_REQUEST) {
132             // Activity result code may not adequately reflect the account removal status, so
133             // KEY_BOOLEAN_RESULT is used here instead. If the intent does not have this value
134             // included, a no-op will be performed and the account update listener in
135             // {@link AccountDetailsFragment} will still handle the back navigation upon removal.
136             if (data != null && data.hasExtra(AccountManager.KEY_BOOLEAN_RESULT)) {
137                 boolean success = data.getBooleanExtra(AccountManager.KEY_BOOLEAN_RESULT, false);
138                 if (success) {
139                     getFragmentController().goBack();
140                 } else {
141                     showErrorDialog();
142                 }
143             }
144         }
145     }
146 
getSecondaryActionAvailabilityStatus()147     private int getSecondaryActionAvailabilityStatus() {
148         ProfileHelper profileHelper = getProfileHelper();
149         if (profileHelper.canCurrentProcessModifyAccounts()) {
150             return AVAILABLE;
151         }
152         if (profileHelper.isDemoOrGuest()
153                 || hasUserRestrictionByUm(getContext(), DISALLOW_MODIFY_ACCOUNTS)) {
154             return DISABLED_FOR_PROFILE;
155         }
156         return AVAILABLE_FOR_VIEWING;
157     }
158 
onRemoveAccountClicked()159     private void onRemoveAccountClicked() {
160         if (hasUserRestrictionByDpm(getContext(), DISALLOW_MODIFY_ACCOUNTS)) {
161             showActionDisabledByAdminDialog();
162         }
163         ConfirmationDialogFragment dialog =
164                 new ConfirmationDialogFragment.Builder(getContext())
165                         .setTitle(R.string.really_remove_account_title)
166                         .setMessage(R.string.really_remove_account_message)
167                         .setNegativeButton(android.R.string.cancel, null)
168                         .setPositiveButton(R.string.remove_account_title, mConfirmListener)
169                         .build();
170 
171         getFragmentController().showDialog(dialog, ConfirmationDialogFragment.TAG);
172     }
173 
showErrorDialog()174     private void showErrorDialog() {
175         getFragmentController().showDialog(
176                 ErrorDialog.newInstance(R.string.remove_account_error_title), /* tag= */ null);
177     }
178 
showActionDisabledByAdminDialog()179     private void showActionDisabledByAdminDialog() {
180         getFragmentController().showDialog(
181                 EnterpriseUtils.getActionDisabledByAdminDialog(getContext(),
182                         DISALLOW_MODIFY_ACCOUNTS),
183                 DISABLED_BY_ADMIN_CONFIRM_DIALOG_TAG);
184     }
185 
186     @VisibleForTesting
getProfileHelper()187     ProfileHelper getProfileHelper() {
188         return ProfileHelper.getInstance(getContext());
189     }
190 }
191