• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 package com.android.car.settings.accounts;
17 
18 import android.accounts.Account;
19 import android.accounts.AccountManager;
20 import android.accounts.AccountManagerCallback;
21 import android.accounts.AuthenticatorException;
22 import android.accounts.OperationCanceledException;
23 import android.app.Activity;
24 import android.app.AlertDialog;
25 import android.app.Dialog;
26 import android.car.userlib.CarUserManagerHelper;
27 import android.content.Context;
28 import android.content.DialogInterface;
29 import android.content.Intent;
30 import android.content.pm.UserInfo;
31 import android.os.Bundle;
32 import android.os.UserHandle;
33 import android.view.View;
34 import android.widget.Button;
35 import android.widget.TextView;
36 
37 import androidx.annotation.LayoutRes;
38 import androidx.annotation.VisibleForTesting;
39 import androidx.annotation.XmlRes;
40 import androidx.fragment.app.DialogFragment;
41 import androidx.fragment.app.Fragment;
42 
43 import com.android.car.settings.R;
44 import com.android.car.settings.common.ErrorDialog;
45 import com.android.car.settings.common.Logger;
46 import com.android.car.settings.common.SettingsFragment;
47 import com.android.settingslib.accounts.AuthenticatorHelper;
48 
49 import java.io.IOException;
50 import java.util.Arrays;
51 
52 /**
53  * Shows account details, and delete account option.
54  */
55 public class AccountDetailsFragment extends SettingsFragment implements
56         AuthenticatorHelper.OnAccountsUpdateListener {
57     public static final String EXTRA_ACCOUNT = "extra_account";
58     public static final String EXTRA_ACCOUNT_LABEL = "extra_account_label";
59     public static final String EXTRA_USER_INFO = "extra_user_info";
60 
61     private Account mAccount;
62     private UserInfo mUserInfo;
63     private AuthenticatorHelper mAuthenticatorHelper;
64 
65     /**
66      * Creates a new AccountDetailsFragment.
67      *
68      * <p>Passes the provided account, label, and user info to the fragment via fragment arguments.
69      */
newInstance(Account account, CharSequence label, UserInfo userInfo)70     public static AccountDetailsFragment newInstance(Account account, CharSequence label,
71             UserInfo userInfo) {
72         AccountDetailsFragment
73                 accountDetailsFragment = new AccountDetailsFragment();
74         Bundle bundle = new Bundle();
75         bundle.putParcelable(EXTRA_ACCOUNT, account);
76         bundle.putCharSequence(EXTRA_ACCOUNT_LABEL, label);
77         bundle.putParcelable(EXTRA_USER_INFO, userInfo);
78         accountDetailsFragment.setArguments(bundle);
79         return accountDetailsFragment;
80     }
81 
82     @Override
83     @XmlRes
getPreferenceScreenResId()84     protected int getPreferenceScreenResId() {
85         return R.xml.account_details_fragment;
86     }
87 
88     @Override
89     @LayoutRes
getActionBarLayoutId()90     protected int getActionBarLayoutId() {
91         return R.layout.action_bar_with_button;
92     }
93 
94     @Override
onAttach(Context context)95     public void onAttach(Context context) {
96         super.onAttach(context);
97 
98         mAccount = getArguments().getParcelable(EXTRA_ACCOUNT);
99         mUserInfo = getArguments().getParcelable(EXTRA_USER_INFO);
100 
101         use(AccountDetailsPreferenceController.class, R.string.pk_account_details)
102                 .setAccount(mAccount);
103         use(AccountDetailsPreferenceController.class, R.string.pk_account_details)
104                 .setUserHandle(mUserInfo.getUserHandle());
105 
106         use(AccountSyncPreferenceController.class, R.string.pk_account_sync)
107                 .setAccount(mAccount);
108         use(AccountDetailsSettingController.class, R.string.pk_account_settings)
109                 .setAccount(mAccount);
110         use(AccountSyncPreferenceController.class, R.string.pk_account_sync)
111                 .setUserHandle(mUserInfo.getUserHandle());
112     }
113 
114     @Override
onActivityCreated(Bundle savedInstanceState)115     public void onActivityCreated(Bundle savedInstanceState) {
116         super.onActivityCreated(savedInstanceState);
117 
118         // Set the fragment's title
119         TextView titleView = requireActivity().findViewById(R.id.title);
120         titleView.setText(getArguments().getCharSequence(EXTRA_ACCOUNT_LABEL));
121 
122         // Enable the remove account button if the user is allowed to modify accounts.
123         Button removeAccountButton = requireActivity().findViewById(R.id.action_button1);
124         if (new CarUserManagerHelper(getContext()).canCurrentProcessModifyAccounts()) {
125             removeAccountButton.setText(R.string.remove_button);
126             removeAccountButton.setOnClickListener(v -> onRemoveAccountClicked());
127         } else {
128             removeAccountButton.setVisibility(View.GONE);
129         }
130     }
131 
132     @Override
onStart()133     public void onStart() {
134         super.onStart();
135 
136         mAuthenticatorHelper = new AuthenticatorHelper(getContext(), mUserInfo.getUserHandle(),
137                 this);
138         mAuthenticatorHelper.listenToAccountUpdates();
139     }
140 
141     @Override
onStop()142     public void onStop() {
143         super.onStop();
144         mAuthenticatorHelper.stopListeningToAccountUpdates();
145     }
146 
147     @Override
onAccountsUpdate(UserHandle userHandle)148     public void onAccountsUpdate(UserHandle userHandle) {
149         if (!accountExists()) {
150             // The account was deleted. Pop back.
151             goBack();
152         }
153     }
154 
155     /** Returns whether the account being shown by this fragment still exists. */
156     @VisibleForTesting
accountExists()157     boolean accountExists() {
158         if (mAccount == null) {
159             return false;
160         }
161 
162         Account[] accounts = AccountManager.get(getContext()).getAccountsByTypeAsUser(mAccount.type,
163                 mUserInfo.getUserHandle());
164 
165         return Arrays.asList(accounts).contains(mAccount);
166     }
167 
onRemoveAccountClicked()168     private void onRemoveAccountClicked() {
169         ConfirmRemoveAccountDialogFragment.show(this, mAccount, mUserInfo.getUserHandle());
170     }
171 
172     /**
173      * Dialog to confirm with user about account removal
174      */
175     public static class ConfirmRemoveAccountDialogFragment extends DialogFragment implements
176             DialogInterface.OnClickListener {
177         private static final String KEY_ACCOUNT = "account";
178         private static final String DIALOG_TAG = "confirmRemoveAccount";
179         private static final Logger LOG = new Logger(ConfirmRemoveAccountDialogFragment.class);
180         private final AccountManagerCallback<Bundle> mCallback =
181                 future -> {
182                     // If already out of this screen, don't proceed.
183                     if (!getTargetFragment().isResumed()) {
184                         return;
185                     }
186 
187                     boolean success = false;
188                     try {
189                         success =
190                                 future.getResult().getBoolean(
191                                         AccountManager.KEY_BOOLEAN_RESULT);
192                     } catch (OperationCanceledException | IOException | AuthenticatorException e) {
193                         LOG.v("removeAccount error: " + e);
194                     }
195                     final Activity activity = getTargetFragment().getActivity();
196                     if (!success && activity != null && !activity.isFinishing()) {
197                         ErrorDialog.show(getTargetFragment(),
198                                 R.string.remove_account_error_title);
199                     } else {
200                         getTargetFragment().getFragmentManager().popBackStack();
201                     }
202                 };
203         private Account mAccount;
204         private UserHandle mUserHandle;
205 
show( Fragment parent, Account account, UserHandle userHandle)206         public static void show(
207                 Fragment parent, Account account, UserHandle userHandle) {
208             final ConfirmRemoveAccountDialogFragment dialog =
209                     new ConfirmRemoveAccountDialogFragment();
210             Bundle bundle = new Bundle();
211             bundle.putParcelable(KEY_ACCOUNT, account);
212             bundle.putParcelable(Intent.EXTRA_USER, userHandle);
213             dialog.setArguments(bundle);
214             dialog.setTargetFragment(parent, 0);
215             dialog.show(parent.getFragmentManager(), DIALOG_TAG);
216         }
217 
218         @Override
onCreate(Bundle savedInstanceState)219         public void onCreate(Bundle savedInstanceState) {
220             super.onCreate(savedInstanceState);
221             final Bundle arguments = getArguments();
222             mAccount = arguments.getParcelable(KEY_ACCOUNT);
223             mUserHandle = arguments.getParcelable(Intent.EXTRA_USER);
224         }
225 
226         @Override
onCreateDialog(Bundle savedInstanceState)227         public Dialog onCreateDialog(Bundle savedInstanceState) {
228             return new AlertDialog.Builder(getContext())
229                     .setTitle(R.string.really_remove_account_title)
230                     .setMessage(R.string.really_remove_account_message)
231                     .setNegativeButton(android.R.string.cancel, null)
232                     .setPositiveButton(R.string.remove_account_title, this)
233                     .create();
234         }
235 
236         @Override
onClick(DialogInterface dialog, int which)237         public void onClick(DialogInterface dialog, int which) {
238             Activity activity = getTargetFragment().getActivity();
239             AccountManager.get(activity).removeAccountAsUser(
240                     mAccount, activity, mCallback, null, mUserHandle);
241             dialog.dismiss();
242         }
243     }
244 }
245