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