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