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.settings.accounts; 17 18 import android.accounts.Account; 19 import android.accounts.AccountManager; 20 import android.accounts.AuthenticatorException; 21 import android.accounts.OperationCanceledException; 22 import android.app.Activity; 23 import android.app.Dialog; 24 import android.app.settings.SettingsEnums; 25 import android.content.Context; 26 import android.content.DialogInterface; 27 import android.content.Intent; 28 import android.os.Bundle; 29 import android.os.UserHandle; 30 import android.os.UserManager; 31 import android.util.Log; 32 import android.view.View; 33 import android.view.View.OnClickListener; 34 import android.widget.Button; 35 36 import androidx.appcompat.app.AlertDialog; 37 import androidx.fragment.app.Fragment; 38 import androidx.preference.PreferenceScreen; 39 40 import com.android.settings.R; 41 import com.android.settings.core.PreferenceControllerMixin; 42 import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 43 import com.android.settings.overlay.FeatureFactory; 44 import com.android.settingslib.RestrictedLockUtils; 45 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 46 import com.android.settingslib.RestrictedLockUtilsInternal; 47 import com.android.settingslib.core.AbstractPreferenceController; 48 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 49 import com.android.settingslib.widget.LayoutPreference; 50 51 import java.io.IOException; 52 53 public class RemoveAccountPreferenceController extends AbstractPreferenceController 54 implements PreferenceControllerMixin, OnClickListener { 55 56 private static final String TAG = "RemoveAccountPrefController"; 57 private static final String KEY_REMOVE_ACCOUNT = "remove_account"; 58 59 private final MetricsFeatureProvider mMetricsFeatureProvider; 60 private Account mAccount; 61 private Fragment mParentFragment; 62 private UserHandle mUserHandle; 63 private LayoutPreference mRemoveAccountPreference; 64 RemoveAccountPreferenceController(Context context, Fragment parent)65 public RemoveAccountPreferenceController(Context context, Fragment parent) { 66 super(context); 67 mParentFragment = parent; 68 mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); 69 } 70 71 @Override displayPreference(PreferenceScreen screen)72 public void displayPreference(PreferenceScreen screen) { 73 super.displayPreference(screen); 74 mRemoveAccountPreference = screen.findPreference(KEY_REMOVE_ACCOUNT); 75 final Button removeAccountButton = mRemoveAccountPreference.findViewById(R.id.button); 76 removeAccountButton.setOnClickListener(this); 77 } 78 79 @Override isAvailable()80 public boolean isAvailable() { 81 return true; 82 } 83 84 @Override getPreferenceKey()85 public String getPreferenceKey() { 86 return KEY_REMOVE_ACCOUNT; 87 } 88 89 @Override onClick(View v)90 public void onClick(View v) { 91 mMetricsFeatureProvider.logClickedPreference(mRemoveAccountPreference, 92 mMetricsFeatureProvider.getMetricsCategory(mParentFragment)); 93 if (mUserHandle != null) { 94 final EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced( 95 mContext, UserManager.DISALLOW_MODIFY_ACCOUNTS, mUserHandle.getIdentifier()); 96 if (admin != null) { 97 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, admin); 98 return; 99 } 100 } 101 102 ConfirmRemoveAccountDialog.show(mParentFragment, mAccount, mUserHandle); 103 } 104 init(Account account, UserHandle userHandle)105 public void init(Account account, UserHandle userHandle) { 106 mAccount = account; 107 mUserHandle = userHandle; 108 } 109 110 /** 111 * Dialog to confirm with user about account removal 112 */ 113 public static class ConfirmRemoveAccountDialog extends InstrumentedDialogFragment implements 114 DialogInterface.OnClickListener { 115 private static final String KEY_ACCOUNT = "account"; 116 private static final String REMOVE_ACCOUNT_DIALOG = "confirmRemoveAccount"; 117 private Account mAccount; 118 private UserHandle mUserHandle; 119 show( Fragment parent, Account account, UserHandle userHandle)120 public static ConfirmRemoveAccountDialog show( 121 Fragment parent, Account account, UserHandle userHandle) { 122 if (!parent.isAdded()) { 123 return null; 124 } 125 final ConfirmRemoveAccountDialog dialog = new ConfirmRemoveAccountDialog(); 126 Bundle bundle = new Bundle(); 127 bundle.putParcelable(KEY_ACCOUNT, account); 128 bundle.putParcelable(Intent.EXTRA_USER, userHandle); 129 dialog.setArguments(bundle); 130 dialog.setTargetFragment(parent, 0); 131 dialog.show(parent.getFragmentManager(), REMOVE_ACCOUNT_DIALOG); 132 return dialog; 133 } 134 135 @Override onCreate(Bundle savedInstanceState)136 public void onCreate(Bundle savedInstanceState) { 137 super.onCreate(savedInstanceState); 138 final Bundle arguments = getArguments(); 139 mAccount = arguments.getParcelable(KEY_ACCOUNT); 140 mUserHandle = arguments.getParcelable(Intent.EXTRA_USER); 141 } 142 143 @Override onCreateDialog(Bundle savedInstanceState)144 public Dialog onCreateDialog(Bundle savedInstanceState) { 145 final Context context = getActivity(); 146 return new AlertDialog.Builder(context) 147 .setTitle(R.string.really_remove_account_title) 148 .setMessage(R.string.really_remove_account_message) 149 .setNegativeButton(android.R.string.cancel, null) 150 .setPositiveButton(R.string.remove_account_label, this) 151 .create(); 152 } 153 154 @Override getMetricsCategory()155 public int getMetricsCategory() { 156 return SettingsEnums.DIALOG_ACCOUNT_SYNC_REMOVE; 157 } 158 159 @Override onClick(DialogInterface dialog, int which)160 public void onClick(DialogInterface dialog, int which) { 161 Activity activity = getTargetFragment().getActivity(); 162 AccountManager.get(activity).removeAccountAsUser(mAccount, activity, 163 future -> { 164 final Activity targetActivity = getTargetFragment().getActivity(); 165 if (targetActivity == null || targetActivity.isFinishing()) { 166 Log.w(TAG, "Activity is no longer alive, skipping results"); 167 return; 168 } 169 boolean failed = true; 170 try { 171 if (future.getResult() 172 .getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) { 173 failed = false; 174 } 175 } catch (OperationCanceledException 176 | IOException 177 | AuthenticatorException e) { 178 // handled below 179 Log.w(TAG, "Remove account error: " + e); 180 RemoveAccountFailureDialog.show(getTargetFragment()); 181 } 182 Log.i(TAG, "failed: " + failed); 183 if (!failed) { 184 targetActivity.finish(); 185 } 186 }, null, mUserHandle); 187 } 188 } 189 190 /** 191 * Dialog to tell user about account removal failure 192 */ 193 public static class RemoveAccountFailureDialog extends InstrumentedDialogFragment { 194 195 private static final String FAILED_REMOVAL_DIALOG = "removeAccountFailed"; 196 show(Fragment parent)197 public static void show(Fragment parent) { 198 if (!parent.isAdded()) { 199 return; 200 } 201 final RemoveAccountFailureDialog dialog = new RemoveAccountFailureDialog(); 202 dialog.setTargetFragment(parent, 0); 203 try { 204 dialog.show(parent.getFragmentManager(), FAILED_REMOVAL_DIALOG); 205 } catch (IllegalStateException e) { 206 Log.w(TAG, "Can't show RemoveAccountFailureDialog. " + e.getMessage()); 207 } 208 } 209 210 @Override onCreateDialog(Bundle savedInstanceState)211 public Dialog onCreateDialog(Bundle savedInstanceState) { 212 final Context context = getActivity(); 213 214 return new AlertDialog.Builder(context) 215 .setTitle(R.string.remove_account_label) 216 .setMessage(R.string.remove_account_failed) 217 .setPositiveButton(android.R.string.ok, null) 218 .create(); 219 } 220 221 @Override getMetricsCategory()222 public int getMetricsCategory() { 223 return SettingsEnums.DIALOG_ACCOUNT_SYNC_FAILED_REMOVAL; 224 } 225 226 } 227 } 228