blob: f3db36c8fc3f7116246792a3798c190eb470a05e [file] [log] [blame]
Dmitri Plotnikov9692f262010-06-29 18:53:11 -07001/*
2 * Copyright (C) 2010 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
17package com.android.contacts.interactions;
18
Dmitri Plotnikov9692f262010-06-29 18:53:11 -070019import android.app.Activity;
20import android.app.AlertDialog;
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -080021import android.app.Fragment;
22import android.app.FragmentManager;
Flavio Lerda34ce5e92011-06-06 15:25:17 +010023import android.app.LoaderManager;
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -080024import android.app.LoaderManager.LoaderCallbacks;
Dmitri Plotnikov9692f262010-06-29 18:53:11 -070025import android.content.Context;
26import android.content.CursorLoader;
27import android.content.DialogInterface;
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -080028import android.content.DialogInterface.OnDismissListener;
Dmitri Plotnikov9692f262010-06-29 18:53:11 -070029import android.content.Loader;
30import android.database.Cursor;
31import android.net.Uri;
32import android.os.Bundle;
Dmitri Plotnikov355d0f42010-11-03 17:13:51 -070033import android.provider.ContactsContract.Contacts;
34import android.provider.ContactsContract.Contacts.Entity;
Chiao Chenge0b2f1e2012-06-12 13:07:56 -070035
36import com.android.contacts.ContactSaveService;
37import com.android.contacts.R;
Chiao Cheng0d5588d2012-11-26 15:34:14 -080038import com.android.contacts.common.model.AccountTypeManager;
Chiao Cheng428f0082012-11-13 18:38:56 -080039import com.android.contacts.common.model.account.AccountType;
Chiao Chenge0b2f1e2012-06-12 13:07:56 -070040import com.google.common.annotations.VisibleForTesting;
41import com.google.common.collect.Sets;
Dmitri Plotnikov9692f262010-06-29 18:53:11 -070042
Dmitri Plotnikov355d0f42010-11-03 17:13:51 -070043import java.util.HashSet;
44
Dmitri Plotnikov9692f262010-06-29 18:53:11 -070045/**
46 * An interaction invoked to delete a contact.
47 */
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -080048public class ContactDeletionInteraction extends Fragment
49 implements LoaderCallbacks<Cursor>, OnDismissListener {
Dmitri Plotnikov9692f262010-06-29 18:53:11 -070050
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -080051 private static final String FRAGMENT_TAG = "deleteContact";
Dmitri Plotnikov9692f262010-06-29 18:53:11 -070052
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -080053 private static final String KEY_ACTIVE = "active";
Dmitri Plotnikovcc42b452011-01-20 11:44:44 -080054 private static final String KEY_CONTACT_URI = "contactUri";
Dmitri Plotnikovc9eda372011-02-23 08:58:13 -080055 private static final String KEY_FINISH_WHEN_DONE = "finishWhenDone";
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -080056 public static final String ARG_CONTACT_URI = "contactUri";
Brian Attwell8a6f4ad2014-06-06 21:54:53 -070057 public static final int RESULT_CODE_DELETED = 3;
Dmitri Plotnikov9692f262010-06-29 18:53:11 -070058
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -080059 private static final String[] ENTITY_PROJECTION = new String[] {
60 Entity.RAW_CONTACT_ID, //0
61 Entity.ACCOUNT_TYPE, //1
Dave Santoro2b3f3c52011-07-26 17:35:42 -070062 Entity.DATA_SET, // 2
63 Entity.CONTACT_ID, // 3
64 Entity.LOOKUP_KEY, // 4
Dmitri Plotnikov9692f262010-06-29 18:53:11 -070065 };
66
Dmitri Plotnikov355d0f42010-11-03 17:13:51 -070067 private static final int COLUMN_INDEX_RAW_CONTACT_ID = 0;
68 private static final int COLUMN_INDEX_ACCOUNT_TYPE = 1;
Dave Santoro2b3f3c52011-07-26 17:35:42 -070069 private static final int COLUMN_INDEX_DATA_SET = 2;
70 private static final int COLUMN_INDEX_CONTACT_ID = 3;
71 private static final int COLUMN_INDEX_LOOKUP_KEY = 4;
Dmitri Plotnikov355d0f42010-11-03 17:13:51 -070072
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -080073 private boolean mActive;
74 private Uri mContactUri;
Dmitri Plotnikovc9eda372011-02-23 08:58:13 -080075 private boolean mFinishActivityWhenDone;
Dmitri Plotnikov9692f262010-06-29 18:53:11 -070076 private Context mContext;
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -080077 private AlertDialog mDialog;
78
Flavio Lerda34ce5e92011-06-06 15:25:17 +010079 /** This is a wrapper around the fragment's loader manager to be used only during testing. */
Brian Attwell09bfb912014-12-18 20:11:17 -080080 private TestLoaderManagerBase mTestLoaderManager;
Flavio Lerda34ce5e92011-06-06 15:25:17 +010081
82 @VisibleForTesting
Dmitri Plotnikov43fd1e82011-01-09 11:29:39 -080083 int mMessageId;
84
Flavio Lerda34ce5e92011-06-06 15:25:17 +010085 /**
86 * Starts the interaction.
87 *
88 * @param activity the activity within which to start the interaction
89 * @param contactUri the URI of the contact to delete
90 * @param finishActivityWhenDone whether to finish the activity upon completion of the
91 * interaction
92 * @return the newly created interaction
93 */
Dmitri Plotnikovc9eda372011-02-23 08:58:13 -080094 public static ContactDeletionInteraction start(
95 Activity activity, Uri contactUri, boolean finishActivityWhenDone) {
Flavio Lerda34ce5e92011-06-06 15:25:17 +010096 return startWithTestLoaderManager(activity, contactUri, finishActivityWhenDone, null);
97 }
98
99 /**
Brian Attwell09bfb912014-12-18 20:11:17 -0800100 * Starts the interaction and optionally set up a {@link TestLoaderManagerBase}.
Flavio Lerda34ce5e92011-06-06 15:25:17 +0100101 *
102 * @param activity the activity within which to start the interaction
103 * @param contactUri the URI of the contact to delete
104 * @param finishActivityWhenDone whether to finish the activity upon completion of the
105 * interaction
Brian Attwell09bfb912014-12-18 20:11:17 -0800106 * @param testLoaderManager the {@link TestLoaderManagerBase} to use to load the data, may be null
Flavio Lerda34ce5e92011-06-06 15:25:17 +0100107 * in which case the default {@link LoaderManager} is used
108 * @return the newly created interaction
109 */
110 @VisibleForTesting
111 static ContactDeletionInteraction startWithTestLoaderManager(
112 Activity activity, Uri contactUri, boolean finishActivityWhenDone,
Brian Attwell09bfb912014-12-18 20:11:17 -0800113 TestLoaderManagerBase testLoaderManager) {
Dmitri Plotnikovac0e18e2011-02-23 17:23:53 -0800114 if (contactUri == null) {
115 return null;
116 }
117
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -0800118 FragmentManager fragmentManager = activity.getFragmentManager();
119 ContactDeletionInteraction fragment =
120 (ContactDeletionInteraction) fragmentManager.findFragmentByTag(FRAGMENT_TAG);
121 if (fragment == null) {
122 fragment = new ContactDeletionInteraction();
Flavio Lerda34ce5e92011-06-06 15:25:17 +0100123 fragment.setTestLoaderManager(testLoaderManager);
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -0800124 fragment.setContactUri(contactUri);
Dmitri Plotnikovc9eda372011-02-23 08:58:13 -0800125 fragment.setFinishActivityWhenDone(finishActivityWhenDone);
Minh Pham4b25da72011-08-25 13:50:38 -0700126 fragmentManager.beginTransaction().add(fragment, FRAGMENT_TAG)
127 .commitAllowingStateLoss();
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -0800128 } else {
Flavio Lerda34ce5e92011-06-06 15:25:17 +0100129 fragment.setTestLoaderManager(testLoaderManager);
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -0800130 fragment.setContactUri(contactUri);
Dmitri Plotnikovc9eda372011-02-23 08:58:13 -0800131 fragment.setFinishActivityWhenDone(finishActivityWhenDone);
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -0800132 }
Dmitri Plotnikov43fd1e82011-01-09 11:29:39 -0800133 return fragment;
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -0800134 }
135
136 @Override
Flavio Lerda34ce5e92011-06-06 15:25:17 +0100137 public LoaderManager getLoaderManager() {
138 // Return the TestLoaderManager if one is set up.
139 LoaderManager loaderManager = super.getLoaderManager();
140 if (mTestLoaderManager != null) {
141 // Set the delegate: this operation is idempotent, so let's just do it every time.
142 mTestLoaderManager.setDelegate(loaderManager);
143 return mTestLoaderManager;
144 } else {
145 return loaderManager;
146 }
147 }
148
149 /** Sets the TestLoaderManager that is used to wrap the actual LoaderManager in tests. */
Brian Attwell09bfb912014-12-18 20:11:17 -0800150 private void setTestLoaderManager(TestLoaderManagerBase mockLoaderManager) {
Flavio Lerda34ce5e92011-06-06 15:25:17 +0100151 mTestLoaderManager = mockLoaderManager;
152 }
153
154 @Override
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -0800155 public void onAttach(Activity activity) {
156 super.onAttach(activity);
Dmitri Plotnikov43fd1e82011-01-09 11:29:39 -0800157 mContext = activity;
Dmitri Plotnikov9692f262010-06-29 18:53:11 -0700158 }
159
Dmitri Plotnikov0279fca2011-02-23 17:47:31 -0800160 @Override
161 public void onDestroyView() {
162 super.onDestroyView();
163 if (mDialog != null && mDialog.isShowing()) {
164 mDialog.setOnDismissListener(null);
165 mDialog.dismiss();
166 mDialog = null;
167 }
168 }
169
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -0800170 public void setContactUri(Uri contactUri) {
171 mContactUri = contactUri;
172 mActive = true;
173 if (isStarted()) {
174 Bundle args = new Bundle();
175 args.putParcelable(ARG_CONTACT_URI, mContactUri);
Dmitri Plotnikov43fd1e82011-01-09 11:29:39 -0800176 getLoaderManager().restartLoader(R.id.dialog_delete_contact_loader_id, args, this);
Dmitri Plotnikov9692f262010-06-29 18:53:11 -0700177 }
Dmitri Plotnikov9692f262010-06-29 18:53:11 -0700178 }
179
Dmitri Plotnikovc9eda372011-02-23 08:58:13 -0800180 private void setFinishActivityWhenDone(boolean finishActivityWhenDone) {
181 this.mFinishActivityWhenDone = finishActivityWhenDone;
182
183 }
184
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -0800185 /* Visible for testing */
186 boolean isStarted() {
187 return isAdded();
188 }
189
190 @Override
191 public void onStart() {
192 if (mActive) {
193 Bundle args = new Bundle();
194 args.putParcelable(ARG_CONTACT_URI, mContactUri);
Dmitri Plotnikov43fd1e82011-01-09 11:29:39 -0800195 getLoaderManager().initLoader(R.id.dialog_delete_contact_loader_id, args, this);
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -0800196 }
197 super.onStart();
198 }
199
200 @Override
Dmitri Plotnikov0279fca2011-02-23 17:47:31 -0800201 public void onStop() {
202 super.onStop();
203 if (mDialog != null) {
204 mDialog.hide();
205 }
206 }
207
208 @Override
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -0800209 public Loader<Cursor> onCreateLoader(int id, Bundle args) {
210 Uri contactUri = args.getParcelable(ARG_CONTACT_URI);
211 return new CursorLoader(mContext,
212 Uri.withAppendedPath(contactUri, Entity.CONTENT_DIRECTORY), ENTITY_PROJECTION,
213 null, null, null);
214 }
215
216 @Override
217 public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
218 if (mDialog != null) {
219 mDialog.dismiss();
220 mDialog = null;
221 }
222
223 if (!mActive) {
224 return;
225 }
226
Dmitri Plotnikov355d0f42010-11-03 17:13:51 -0700227 long contactId = 0;
228 String lookupKey = null;
229
230 // This cursor may contain duplicate raw contacts, so we need to de-dupe them first
231 HashSet<Long> readOnlyRawContacts = Sets.newHashSet();
232 HashSet<Long> writableRawContacts = Sets.newHashSet();
Dmitri Plotnikov9692f262010-06-29 18:53:11 -0700233
Dmitri Plotnikova07fa5f2011-01-09 11:56:50 -0800234 AccountTypeManager accountTypes = AccountTypeManager.getInstance(getActivity());
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -0800235 cursor.moveToPosition(-1);
236 while (cursor.moveToNext()) {
237 final long rawContactId = cursor.getLong(COLUMN_INDEX_RAW_CONTACT_ID);
238 final String accountType = cursor.getString(COLUMN_INDEX_ACCOUNT_TYPE);
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700239 final String dataSet = cursor.getString(COLUMN_INDEX_DATA_SET);
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -0800240 contactId = cursor.getLong(COLUMN_INDEX_CONTACT_ID);
241 lookupKey = cursor.getString(COLUMN_INDEX_LOOKUP_KEY);
Dave Santoro2b3f3c52011-07-26 17:35:42 -0700242 AccountType type = accountTypes.getAccountType(accountType, dataSet);
Daniel Lehmann96e87fd2011-09-20 21:31:37 -0700243 boolean writable = type == null || type.areContactsWritable();
244 if (writable) {
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -0800245 writableRawContacts.add(rawContactId);
Daniel Lehmann96e87fd2011-09-20 21:31:37 -0700246 } else {
247 readOnlyRawContacts.add(rawContactId);
Dmitri Plotnikov9692f262010-06-29 18:53:11 -0700248 }
Dmitri Plotnikov9692f262010-06-29 18:53:11 -0700249 }
250
Dmitri Plotnikov355d0f42010-11-03 17:13:51 -0700251 int readOnlyCount = readOnlyRawContacts.size();
252 int writableCount = writableRawContacts.size();
253 if (readOnlyCount > 0 && writableCount > 0) {
Dmitri Plotnikov43fd1e82011-01-09 11:29:39 -0800254 mMessageId = R.string.readOnlyContactDeleteConfirmation;
Dmitri Plotnikov355d0f42010-11-03 17:13:51 -0700255 } else if (readOnlyCount > 0 && writableCount == 0) {
Dmitri Plotnikov43fd1e82011-01-09 11:29:39 -0800256 mMessageId = R.string.readOnlyContactWarning;
Dmitri Plotnikov355d0f42010-11-03 17:13:51 -0700257 } else if (readOnlyCount == 0 && writableCount > 1) {
Dmitri Plotnikov43fd1e82011-01-09 11:29:39 -0800258 mMessageId = R.string.multipleContactDeleteConfirmation;
Dmitri Plotnikov9692f262010-06-29 18:53:11 -0700259 } else {
Dmitri Plotnikov43fd1e82011-01-09 11:29:39 -0800260 mMessageId = R.string.deleteConfirmation;
Dmitri Plotnikov9692f262010-06-29 18:53:11 -0700261 }
262
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -0800263 final Uri contactUri = Contacts.getLookupUri(contactId, lookupKey);
Dmitri Plotnikov43fd1e82011-01-09 11:29:39 -0800264 showDialog(mMessageId, contactUri);
Daisuke Miyakawad7c148b2011-03-31 18:33:35 -0700265
266 // We don't want onLoadFinished() calls any more, which may come when the database is
267 // updating.
268 getLoaderManager().destroyLoader(R.id.dialog_delete_contact_loader_id);
Dmitri Plotnikov9692f262010-06-29 18:53:11 -0700269 }
270
Daisuke Miyakawad7c148b2011-03-31 18:33:35 -0700271 @Override
Dianne Hackborn4ef95cc2010-12-16 00:44:33 -0800272 public void onLoaderReset(Loader<Cursor> loader) {
273 }
Dmitri Plotnikov69f9e6f2011-01-03 15:47:24 -0800274
Dmitri Plotnikov43fd1e82011-01-09 11:29:39 -0800275 private void showDialog(int messageId, final Uri contactUri) {
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -0800276 mDialog = new AlertDialog.Builder(getActivity())
Dmitri Plotnikov4292dfa2011-01-23 10:21:18 -0800277 .setIconAttribute(android.R.attr.alertDialogIcon)
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -0800278 .setMessage(messageId)
279 .setNegativeButton(android.R.string.cancel, null)
280 .setPositiveButton(android.R.string.ok,
281 new DialogInterface.OnClickListener() {
282 @Override
283 public void onClick(DialogInterface dialog, int whichButton) {
284 doDeleteContact(contactUri);
285 }
286 }
287 )
288 .create();
289
290 mDialog.setOnDismissListener(this);
291 mDialog.show();
292 }
293
294 @Override
295 public void onDismiss(DialogInterface dialog) {
296 mActive = false;
297 mDialog = null;
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -0800298 }
299
300 @Override
301 public void onSaveInstanceState(Bundle outState) {
302 super.onSaveInstanceState(outState);
303 outState.putBoolean(KEY_ACTIVE, mActive);
Dmitri Plotnikovcc42b452011-01-20 11:44:44 -0800304 outState.putParcelable(KEY_CONTACT_URI, mContactUri);
Dmitri Plotnikovc9eda372011-02-23 08:58:13 -0800305 outState.putBoolean(KEY_FINISH_WHEN_DONE, mFinishActivityWhenDone);
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -0800306 }
307
308 @Override
309 public void onActivityCreated(Bundle savedInstanceState) {
310 super.onActivityCreated(savedInstanceState);
311 if (savedInstanceState != null) {
312 mActive = savedInstanceState.getBoolean(KEY_ACTIVE);
Dmitri Plotnikovcc42b452011-01-20 11:44:44 -0800313 mContactUri = savedInstanceState.getParcelable(KEY_CONTACT_URI);
Dmitri Plotnikovc9eda372011-02-23 08:58:13 -0800314 mFinishActivityWhenDone = savedInstanceState.getBoolean(KEY_FINISH_WHEN_DONE);
Dmitri Plotnikov66bca8f2010-11-24 16:59:24 -0800315 }
316 }
317
318 protected void doDeleteContact(Uri contactUri) {
Dmitri Plotnikov7d8cabb2010-11-24 17:40:01 -0800319 mContext.startService(ContactSaveService.createDeleteContactIntent(mContext, contactUri));
Dmitri Plotnikovc9eda372011-02-23 08:58:13 -0800320 if (isAdded() && mFinishActivityWhenDone) {
Brian Attwell8a6f4ad2014-06-06 21:54:53 -0700321 getActivity().setResult(RESULT_CODE_DELETED);
Dmitri Plotnikovc9eda372011-02-23 08:58:13 -0800322 getActivity().finish();
323 }
Dmitri Plotnikov9692f262010-06-29 18:53:11 -0700324 }
Dmitri Plotnikov9692f262010-06-29 18:53:11 -0700325}