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 static android.app.admin.DevicePolicyResources.Strings.Settings.REMOVE_ACCOUNT_FAILED_ADMIN_RESTRICTION; 19 20 import android.accounts.Account; 21 import android.accounts.AccountManager; 22 import android.accounts.AuthenticatorException; 23 import android.accounts.OperationCanceledException; 24 import android.app.Activity; 25 import android.app.Dialog; 26 import android.app.admin.DevicePolicyManager; 27 import android.app.settings.SettingsEnums; 28 import android.content.Context; 29 import android.content.DialogInterface; 30 import android.content.Intent; 31 import android.os.Bundle; 32 import android.os.UserHandle; 33 import android.os.UserManager; 34 import android.util.Log; 35 import android.view.View; 36 import android.view.View.OnClickListener; 37 38 import androidx.appcompat.app.AlertDialog; 39 import androidx.fragment.app.Fragment; 40 import androidx.preference.Preference; 41 import androidx.preference.PreferenceScreen; 42 43 import com.android.settings.R; 44 import com.android.settings.core.PreferenceControllerMixin; 45 import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 46 import com.android.settings.overlay.FeatureFactory; 47 import com.android.settings.widget.RestrictedButton; 48 import com.android.settingslib.core.AbstractPreferenceController; 49 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 50 import com.android.settingslib.widget.LayoutPreference; 51 52 import java.io.IOException; 53 54 public class RemoveAccountPreferenceController extends AbstractPreferenceController 55 implements PreferenceControllerMixin, OnClickListener { 56 57 private static final String TAG = "RemoveAccountPrefController"; 58 private static final String KEY_REMOVE_ACCOUNT = "remove_account"; 59 60 private final MetricsFeatureProvider mMetricsFeatureProvider; 61 private Account mAccount; 62 private Fragment mParentFragment; 63 private UserHandle mUserHandle; 64 private LayoutPreference mRemoveAccountPreference; 65 private RestrictedButton mRemoveAccountButton; 66 RemoveAccountPreferenceController(Context context, Fragment parent)67 public RemoveAccountPreferenceController(Context context, Fragment parent) { 68 super(context); 69 mParentFragment = parent; 70 mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); 71 } 72 73 @Override displayPreference(PreferenceScreen screen)74 public void displayPreference(PreferenceScreen screen) { 75 super.displayPreference(screen); 76 mRemoveAccountPreference = screen.findPreference(KEY_REMOVE_ACCOUNT); 77 mRemoveAccountButton = mRemoveAccountPreference.findViewById(R.id.button); 78 mRemoveAccountButton.setOnClickListener(this); 79 } 80 81 @Override updateState(Preference preference)82 public void updateState(Preference preference) { 83 super.updateState(preference); 84 mRemoveAccountButton.updateState(); 85 } 86 87 @Override isAvailable()88 public boolean isAvailable() { 89 return true; 90 } 91 92 @Override getPreferenceKey()93 public String getPreferenceKey() { 94 return KEY_REMOVE_ACCOUNT; 95 } 96 97 @Override onClick(View v)98 public void onClick(View v) { 99 mMetricsFeatureProvider.logClickedPreference(mRemoveAccountPreference, 100 mMetricsFeatureProvider.getMetricsCategory(mParentFragment)); 101 ConfirmRemoveAccountDialog.show(mParentFragment, mAccount, mUserHandle); 102 } 103 init(Account account, UserHandle userHandle)104 public void init(Account account, UserHandle userHandle) { 105 mAccount = account; 106 mUserHandle = userHandle; 107 mRemoveAccountButton.init(mUserHandle, UserManager.DISALLOW_MODIFY_ACCOUNTS); 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 } 181 Log.i(TAG, "failed: " + failed); 182 if (failed) { 183 RemoveAccountFailureDialog.show(getTargetFragment()); 184 } else { 185 targetActivity.finish(); 186 } 187 }, null, mUserHandle); 188 } 189 } 190 191 /** 192 * Dialog to tell user about account removal failure 193 */ 194 public static class RemoveAccountFailureDialog extends InstrumentedDialogFragment { 195 196 private static final String FAILED_REMOVAL_DIALOG = "removeAccountFailed"; 197 show(Fragment parent)198 public static void show(Fragment parent) { 199 if (!parent.isAdded()) { 200 return; 201 } 202 final RemoveAccountFailureDialog dialog = new RemoveAccountFailureDialog(); 203 dialog.setTargetFragment(parent, 0); 204 try { 205 dialog.show(parent.getFragmentManager(), FAILED_REMOVAL_DIALOG); 206 } catch (IllegalStateException e) { 207 Log.w(TAG, "Can't show RemoveAccountFailureDialog. " + e.getMessage()); 208 } 209 } 210 211 @Override onCreateDialog(Bundle savedInstanceState)212 public Dialog onCreateDialog(Bundle savedInstanceState) { 213 final Context context = getActivity(); 214 215 return new AlertDialog.Builder(context) 216 .setTitle(R.string.remove_account_label) 217 .setMessage(getContext().getSystemService(DevicePolicyManager.class) 218 .getResources() 219 .getString(REMOVE_ACCOUNT_FAILED_ADMIN_RESTRICTION, 220 () -> getString(R.string.remove_account_failed))) 221 .setPositiveButton(android.R.string.ok, null) 222 .create(); 223 } 224 225 @Override getMetricsCategory()226 public int getMetricsCategory() { 227 return SettingsEnums.DIALOG_ACCOUNT_SYNC_FAILED_REMOVAL; 228 } 229 230 } 231 } 232