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 TAG = "RemoveAccountPrefController";
57     private static final String KEY_REMOVE_ACCOUNT = "remove_account";
58 
59     private final MetricsFeatureProvider mMetricsFeatureProvider;
60     private Account mAccount;
61     private Fragment mParentFragment;
62     private UserHandle mUserHandle;
63     private LayoutPreference mRemoveAccountPreference;
64 
RemoveAccountPreferenceController(Context context, Fragment parent)65     public RemoveAccountPreferenceController(Context context, Fragment parent) {
66         super(context);
67         mParentFragment = parent;
68         mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
69     }
70 
71     @Override
displayPreference(PreferenceScreen screen)72     public void displayPreference(PreferenceScreen screen) {
73         super.displayPreference(screen);
74         mRemoveAccountPreference = screen.findPreference(KEY_REMOVE_ACCOUNT);
75         final Button removeAccountButton = mRemoveAccountPreference.findViewById(R.id.button);
76         removeAccountButton.setOnClickListener(this);
77     }
78 
79     @Override
isAvailable()80     public boolean isAvailable() {
81         return true;
82     }
83 
84     @Override
getPreferenceKey()85     public String getPreferenceKey() {
86         return KEY_REMOVE_ACCOUNT;
87     }
88 
89     @Override
onClick(View v)90     public void onClick(View v) {
91         mMetricsFeatureProvider.logClickedPreference(mRemoveAccountPreference,
92                 mMetricsFeatureProvider.getMetricsCategory(mParentFragment));
93         if (mUserHandle != null) {
94             final EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
95                     mContext, UserManager.DISALLOW_MODIFY_ACCOUNTS, mUserHandle.getIdentifier());
96             if (admin != null) {
97                 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, admin);
98                 return;
99             }
100         }
101 
102         ConfirmRemoveAccountDialog.show(mParentFragment, mAccount, mUserHandle);
103     }
104 
init(Account account, UserHandle userHandle)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 
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                             RemoveAccountFailureDialog.show(getTargetFragment());
181                         }
182                         Log.i(TAG, "failed: " + failed);
183                         if (!failed) {
184                             targetActivity.finish();
185                         }
186                     }, null, mUserHandle);
187         }
188     }
189 
190     /**
191      * Dialog to tell user about account removal failure
192      */
193     public static class RemoveAccountFailureDialog extends InstrumentedDialogFragment {
194 
195         private static final String FAILED_REMOVAL_DIALOG = "removeAccountFailed";
196 
show(Fragment parent)197         public static void show(Fragment parent) {
198             if (!parent.isAdded()) {
199                 return;
200             }
201             final RemoveAccountFailureDialog dialog = new RemoveAccountFailureDialog();
202             dialog.setTargetFragment(parent, 0);
203             try {
204                 dialog.show(parent.getFragmentManager(), FAILED_REMOVAL_DIALOG);
205             } catch (IllegalStateException e) {
206                 Log.w(TAG, "Can't show RemoveAccountFailureDialog. " +  e.getMessage());
207             }
208         }
209 
210         @Override
onCreateDialog(Bundle savedInstanceState)211         public Dialog onCreateDialog(Bundle savedInstanceState) {
212             final Context context = getActivity();
213 
214             return new AlertDialog.Builder(context)
215                     .setTitle(R.string.remove_account_label)
216                     .setMessage(R.string.remove_account_failed)
217                     .setPositiveButton(android.R.string.ok, null)
218                     .create();
219         }
220 
221         @Override
getMetricsCategory()222         public int getMetricsCategory() {
223             return SettingsEnums.DIALOG_ACCOUNT_SYNC_FAILED_REMOVAL;
224         }
225 
226     }
227 }
228