/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.accounts; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.app.Activity; import android.app.Dialog; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.widget.LayoutPreference; import java.io.IOException; public class RemoveAccountPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin, OnClickListener { private static final String TAG = "RemoveAccountPrefController"; private static final String KEY_REMOVE_ACCOUNT = "remove_account"; private final MetricsFeatureProvider mMetricsFeatureProvider; private Account mAccount; private Fragment mParentFragment; private UserHandle mUserHandle; private LayoutPreference mRemoveAccountPreference; public RemoveAccountPreferenceController(Context context, Fragment parent) { super(context); mParentFragment = parent; mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); } @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); mRemoveAccountPreference = screen.findPreference(KEY_REMOVE_ACCOUNT); final Button removeAccountButton = mRemoveAccountPreference.findViewById(R.id.button); removeAccountButton.setOnClickListener(this); } @Override public boolean isAvailable() { return true; } @Override public String getPreferenceKey() { return KEY_REMOVE_ACCOUNT; } @Override public void onClick(View v) { mMetricsFeatureProvider.logClickedPreference(mRemoveAccountPreference, mMetricsFeatureProvider.getMetricsCategory(mParentFragment)); if (mUserHandle != null) { final EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced( mContext, UserManager.DISALLOW_MODIFY_ACCOUNTS, mUserHandle.getIdentifier()); if (admin != null) { RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, admin); return; } } ConfirmRemoveAccountDialog.show(mParentFragment, mAccount, mUserHandle); } public void init(Account account, UserHandle userHandle) { mAccount = account; mUserHandle = userHandle; } /** * Dialog to confirm with user about account removal */ public static class ConfirmRemoveAccountDialog extends InstrumentedDialogFragment implements DialogInterface.OnClickListener { private static final String KEY_ACCOUNT = "account"; private static final String REMOVE_ACCOUNT_DIALOG = "confirmRemoveAccount"; private Account mAccount; private UserHandle mUserHandle; public static ConfirmRemoveAccountDialog show( Fragment parent, Account account, UserHandle userHandle) { if (!parent.isAdded()) { return null; } final ConfirmRemoveAccountDialog dialog = new ConfirmRemoveAccountDialog(); Bundle bundle = new Bundle(); bundle.putParcelable(KEY_ACCOUNT, account); bundle.putParcelable(Intent.EXTRA_USER, userHandle); dialog.setArguments(bundle); dialog.setTargetFragment(parent, 0); dialog.show(parent.getFragmentManager(), REMOVE_ACCOUNT_DIALOG); return dialog; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Bundle arguments = getArguments(); mAccount = arguments.getParcelable(KEY_ACCOUNT); mUserHandle = arguments.getParcelable(Intent.EXTRA_USER); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final Context context = getActivity(); return new AlertDialog.Builder(context) .setTitle(R.string.really_remove_account_title) .setMessage(R.string.really_remove_account_message) .setNegativeButton(android.R.string.cancel, null) .setPositiveButton(R.string.remove_account_label, this) .create(); } @Override public int getMetricsCategory() { return SettingsEnums.DIALOG_ACCOUNT_SYNC_REMOVE; } @Override public void onClick(DialogInterface dialog, int which) { Activity activity = getTargetFragment().getActivity(); AccountManager.get(activity).removeAccountAsUser(mAccount, activity, future -> { final Activity targetActivity = getTargetFragment().getActivity(); if (targetActivity == null || targetActivity.isFinishing()) { Log.w(TAG, "Activity is no longer alive, skipping results"); return; } boolean failed = true; try { if (future.getResult() .getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) { failed = false; } } catch (OperationCanceledException | IOException | AuthenticatorException e) { // handled below Log.w(TAG, "Remove account error: " + e); RemoveAccountFailureDialog.show(getTargetFragment()); } Log.i(TAG, "failed: " + failed); if (!failed) { targetActivity.finish(); } }, null, mUserHandle); } } /** * Dialog to tell user about account removal failure */ public static class RemoveAccountFailureDialog extends InstrumentedDialogFragment { private static final String FAILED_REMOVAL_DIALOG = "removeAccountFailed"; public static void show(Fragment parent) { if (!parent.isAdded()) { return; } final RemoveAccountFailureDialog dialog = new RemoveAccountFailureDialog(); dialog.setTargetFragment(parent, 0); try { dialog.show(parent.getFragmentManager(), FAILED_REMOVAL_DIALOG); } catch (IllegalStateException e) { Log.w(TAG, "Can't show RemoveAccountFailureDialog. " + e.getMessage()); } } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final Context context = getActivity(); return new AlertDialog.Builder(context) .setTitle(R.string.remove_account_label) .setMessage(R.string.remove_account_failed) .setPositiveButton(android.R.string.ok, null) .create(); } @Override public int getMetricsCategory() { return SettingsEnums.DIALOG_ACCOUNT_SYNC_FAILED_REMOVAL; } } }