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.AccountManagerCallback; 21 import android.accounts.AccountManagerFuture; 22 import android.accounts.AuthenticatorException; 23 import android.accounts.OperationCanceledException; 24 import android.app.admin.DevicePolicyManager; 25 import android.app.Activity; 26 import android.app.AlertDialog; 27 import android.app.Dialog; 28 import android.app.Fragment; 29 import android.content.Context; 30 import android.content.DialogInterface; 31 import android.content.Intent; 32 import android.os.Bundle; 33 import android.os.UserHandle; 34 import android.os.UserManager; 35 import android.support.v7.preference.PreferenceScreen; 36 import android.view.View; 37 import android.view.View.OnClickListener; 38 import android.widget.Button; 39 40 import com.android.internal.annotations.VisibleForTesting; 41 import com.android.internal.logging.nano.MetricsProto; 42 import com.android.settings.R; 43 import com.android.settings.applications.LayoutPreference; 44 import com.android.settings.core.PreferenceControllerMixin; 45 import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 46 import com.android.settings.enterprise.DevicePolicyManagerWrapper; 47 import com.android.settings.enterprise.DevicePolicyManagerWrapperImpl; 48 import com.android.settingslib.core.AbstractPreferenceController; 49 50 import java.io.IOException; 51 52 public class RemoveAccountPreferenceController extends AbstractPreferenceController 53 implements PreferenceControllerMixin, OnClickListener { 54 55 private static final String KEY_REMOVE_ACCOUNT = "remove_account"; 56 57 private Account mAccount; 58 private Fragment mParentFragment; 59 private UserHandle mUserHandle; 60 private DevicePolicyManagerWrapper mDpm; 61 62 public RemoveAccountPreferenceController(Context context, Fragment parent) { 63 this(context, parent, new DevicePolicyManagerWrapperImpl( 64 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE))); 65 } 66 67 @VisibleForTesting 68 RemoveAccountPreferenceController(Context context, Fragment parent, 69 DevicePolicyManagerWrapper dpm) { 70 super(context); 71 mParentFragment = parent; 72 mDpm = dpm; 73 } 74 75 @Override 76 public void displayPreference(PreferenceScreen screen) { 77 super.displayPreference(screen); 78 final LayoutPreference removeAccountPreference = 79 (LayoutPreference) screen.findPreference(KEY_REMOVE_ACCOUNT); 80 Button removeAccountButton = (Button) removeAccountPreference.findViewById(R.id.button); 81 removeAccountButton.setOnClickListener(this); 82 } 83 84 @Override 85 public boolean isAvailable() { 86 return true; 87 } 88 89 @Override 90 public String getPreferenceKey() { 91 return KEY_REMOVE_ACCOUNT; 92 } 93 94 @Override 95 public void onClick(View v) { 96 final Intent intent = mDpm.createAdminSupportIntent(UserManager.DISALLOW_MODIFY_ACCOUNTS); 97 if (intent != null) { 98 // DISALLOW_MODIFY_ACCOUNTS is active, show admin support dialog 99 mContext.startActivity(intent); 100 return; 101 } 102 ConfirmRemoveAccountDialog.show(mParentFragment, mAccount, mUserHandle); 103 } 104 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 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 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 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 155 public int getMetricsCategory() { 156 return MetricsProto.MetricsEvent.DIALOG_ACCOUNT_SYNC_REMOVE; 157 } 158 159 @Override 160 public void onClick(DialogInterface dialog, int which) { 161 Activity activity = getTargetFragment().getActivity(); 162 AccountManager.get(activity).removeAccountAsUser(mAccount, activity, 163 new AccountManagerCallback<Bundle>() { 164 @Override 165 public void run(AccountManagerFuture<Bundle> future) { 166 // If already out of this screen, don't proceed. 167 if (!getTargetFragment().isResumed()) { 168 return; 169 } 170 boolean failed = true; 171 try { 172 if (future.getResult() 173 .getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) { 174 failed = false; 175 } 176 } catch (OperationCanceledException e) { 177 // handled below 178 } catch (IOException e) { 179 // handled below 180 } catch (AuthenticatorException e) { 181 // handled below 182 } 183 final Activity activity = getTargetFragment().getActivity(); 184 if (failed && activity != null && !activity.isFinishing()) { 185 RemoveAccountFailureDialog.show(getTargetFragment()); 186 } else { 187 activity.finish(); 188 } 189 } 190 }, null, mUserHandle); 191 } 192 } 193 194 /** 195 * Dialog to tell user about account removal failure 196 */ 197 public static class RemoveAccountFailureDialog extends InstrumentedDialogFragment { 198 199 private static final String FAILED_REMOVAL_DIALOG = "removeAccountFailed"; 200 201 public static void show(Fragment parent) { 202 if (!parent.isAdded()) { 203 return; 204 } 205 final RemoveAccountFailureDialog dialog = new RemoveAccountFailureDialog(); 206 dialog.setTargetFragment(parent, 0); 207 dialog.show(parent.getFragmentManager(), FAILED_REMOVAL_DIALOG); 208 } 209 210 @Override 211 public Dialog onCreateDialog(Bundle savedInstanceState) { 212 final Context context = getActivity(); 213 214 return new AlertDialog.Builder(context) 215 .setTitle(R.string.really_remove_account_title) 216 .setMessage(R.string.remove_account_failed) 217 .setPositiveButton(android.R.string.ok, null) 218 .create(); 219 } 220 221 @Override 222 public int getMetricsCategory() { 223 return MetricsProto.MetricsEvent.DIALOG_ACCOUNT_SYNC_FAILED_REMOVAL; 224 } 225 226 } 227 } 228