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 */
16package com.android.settings.accounts;
17
18import android.accounts.Account;
19import android.accounts.AccountManager;
20import android.accounts.AccountManagerCallback;
21import android.accounts.AccountManagerFuture;
22import android.accounts.AuthenticatorException;
23import android.accounts.OperationCanceledException;
24import android.app.admin.DevicePolicyManager;
25import android.app.Activity;
26import android.app.AlertDialog;
27import android.app.Dialog;
28import android.app.Fragment;
29import android.content.Context;
30import android.content.DialogInterface;
31import android.content.Intent;
32import android.os.Bundle;
33import android.os.UserHandle;
34import android.os.UserManager;
35import android.support.v7.preference.PreferenceScreen;
36import android.view.View;
37import android.view.View.OnClickListener;
38import android.widget.Button;
39
40import com.android.internal.annotations.VisibleForTesting;
41import com.android.internal.logging.nano.MetricsProto;
42import com.android.settings.R;
43import com.android.settings.applications.LayoutPreference;
44import com.android.settings.core.PreferenceControllerMixin;
45import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
46import com.android.settings.enterprise.DevicePolicyManagerWrapper;
47import com.android.settings.enterprise.DevicePolicyManagerWrapperImpl;
48import com.android.settingslib.core.AbstractPreferenceController;
49
50import java.io.IOException;
51
52public 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