ContactDeletionInteraction.java revision d7c148bc7331ee405ea2932a63cb12bde259e93c
1/*
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
19import com.android.contacts.ContactSaveService;
20import com.android.contacts.R;
21import com.android.contacts.model.AccountType;
22import com.android.contacts.model.AccountTypeManager;
23import com.google.android.collect.Sets;
24
25import android.app.Activity;
26import android.app.AlertDialog;
27import android.app.Fragment;
28import android.app.FragmentManager;
29import android.app.LoaderManager.LoaderCallbacks;
30import android.content.Context;
31import android.content.CursorLoader;
32import android.content.DialogInterface;
33import android.content.DialogInterface.OnDismissListener;
34import android.content.Loader;
35import android.database.Cursor;
36import android.net.Uri;
37import android.os.Bundle;
38import android.provider.ContactsContract.Contacts;
39import android.provider.ContactsContract.Contacts.Entity;
40
41import java.util.HashSet;
42
43/**
44 * An interaction invoked to delete a contact.
45 */
46public class ContactDeletionInteraction extends Fragment
47        implements LoaderCallbacks<Cursor>, OnDismissListener {
48
49    private static final String FRAGMENT_TAG = "deleteContact";
50
51    private static final String KEY_ACTIVE = "active";
52    private static final String KEY_CONTACT_URI = "contactUri";
53    private static final String KEY_FINISH_WHEN_DONE = "finishWhenDone";
54    public static final String ARG_CONTACT_URI = "contactUri";
55
56    private static final String[] ENTITY_PROJECTION = new String[] {
57        Entity.RAW_CONTACT_ID, //0
58        Entity.ACCOUNT_TYPE, //1
59        Entity.CONTACT_ID, // 2
60        Entity.LOOKUP_KEY, // 3
61    };
62
63    private static final int COLUMN_INDEX_RAW_CONTACT_ID = 0;
64    private static final int COLUMN_INDEX_ACCOUNT_TYPE = 1;
65    private static final int COLUMN_INDEX_CONTACT_ID = 2;
66    private static final int COLUMN_INDEX_LOOKUP_KEY = 3;
67
68    private boolean mActive;
69    private Uri mContactUri;
70    private boolean mFinishActivityWhenDone;
71    private Context mContext;
72
73    private AlertDialog mDialog;
74
75    // Visible for testing
76    int mMessageId;
77
78    public static ContactDeletionInteraction start(
79            Activity activity, Uri contactUri, boolean finishActivityWhenDone) {
80        if (contactUri == null) {
81            return null;
82        }
83
84        FragmentManager fragmentManager = activity.getFragmentManager();
85        ContactDeletionInteraction fragment =
86                (ContactDeletionInteraction) fragmentManager.findFragmentByTag(FRAGMENT_TAG);
87        if (fragment == null) {
88            fragment = new ContactDeletionInteraction();
89            fragment.setContactUri(contactUri);
90            fragment.setFinishActivityWhenDone(finishActivityWhenDone);
91            fragmentManager.beginTransaction().add(fragment, FRAGMENT_TAG).commit();
92        } else {
93            fragment.setContactUri(contactUri);
94            fragment.setFinishActivityWhenDone(finishActivityWhenDone);
95        }
96        return fragment;
97    }
98
99    @Override
100    public void onAttach(Activity activity) {
101        super.onAttach(activity);
102        mContext = activity;
103    }
104
105    @Override
106    public void onDestroyView() {
107        super.onDestroyView();
108        if (mDialog != null && mDialog.isShowing()) {
109            mDialog.setOnDismissListener(null);
110            mDialog.dismiss();
111            mDialog = null;
112        }
113    }
114
115    public void setContactUri(Uri contactUri) {
116        mContactUri = contactUri;
117        mActive = true;
118        if (isStarted()) {
119            Bundle args = new Bundle();
120            args.putParcelable(ARG_CONTACT_URI, mContactUri);
121            getLoaderManager().restartLoader(R.id.dialog_delete_contact_loader_id, args, this);
122        }
123    }
124
125    private void setFinishActivityWhenDone(boolean finishActivityWhenDone) {
126        this.mFinishActivityWhenDone = finishActivityWhenDone;
127
128    }
129
130    /* Visible for testing */
131    boolean isStarted() {
132        return isAdded();
133    }
134
135    @Override
136    public void onStart() {
137        if (mActive) {
138            Bundle args = new Bundle();
139            args.putParcelable(ARG_CONTACT_URI, mContactUri);
140            getLoaderManager().initLoader(R.id.dialog_delete_contact_loader_id, args, this);
141        }
142        super.onStart();
143    }
144
145    @Override
146    public void onStop() {
147        super.onStop();
148        if (mDialog != null) {
149            mDialog.hide();
150        }
151    }
152
153    @Override
154    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
155        Uri contactUri = args.getParcelable(ARG_CONTACT_URI);
156        return new CursorLoader(mContext,
157                Uri.withAppendedPath(contactUri, Entity.CONTENT_DIRECTORY), ENTITY_PROJECTION,
158                null, null, null);
159    }
160
161    @Override
162    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
163        if (mDialog != null) {
164            mDialog.dismiss();
165            mDialog = null;
166        }
167
168        if (!mActive) {
169            return;
170        }
171
172        long contactId = 0;
173        String lookupKey = null;
174
175        // This cursor may contain duplicate raw contacts, so we need to de-dupe them first
176        HashSet<Long>  readOnlyRawContacts = Sets.newHashSet();
177        HashSet<Long>  writableRawContacts = Sets.newHashSet();
178
179        AccountTypeManager accountTypes = AccountTypeManager.getInstance(getActivity());
180        cursor.moveToPosition(-1);
181        while (cursor.moveToNext()) {
182            final long rawContactId = cursor.getLong(COLUMN_INDEX_RAW_CONTACT_ID);
183            final String accountType = cursor.getString(COLUMN_INDEX_ACCOUNT_TYPE);
184            contactId = cursor.getLong(COLUMN_INDEX_CONTACT_ID);
185            lookupKey = cursor.getString(COLUMN_INDEX_LOOKUP_KEY);
186            AccountType type = accountTypes.getAccountType(accountType);
187            boolean readonly = type != null && type.readOnly;
188            if (readonly) {
189                readOnlyRawContacts.add(rawContactId);
190            } else {
191                writableRawContacts.add(rawContactId);
192            }
193        }
194
195        int readOnlyCount = readOnlyRawContacts.size();
196        int writableCount = writableRawContacts.size();
197        if (readOnlyCount > 0 && writableCount > 0) {
198            mMessageId = R.string.readOnlyContactDeleteConfirmation;
199        } else if (readOnlyCount > 0 && writableCount == 0) {
200            mMessageId = R.string.readOnlyContactWarning;
201        } else if (readOnlyCount == 0 && writableCount > 1) {
202            mMessageId = R.string.multipleContactDeleteConfirmation;
203        } else {
204            mMessageId = R.string.deleteConfirmation;
205        }
206
207        final Uri contactUri = Contacts.getLookupUri(contactId, lookupKey);
208        showDialog(mMessageId, contactUri);
209
210        // We don't want onLoadFinished() calls any more, which may come when the database is
211        // updating.
212        getLoaderManager().destroyLoader(R.id.dialog_delete_contact_loader_id);
213    }
214
215    @Override
216    public void onLoaderReset(Loader<Cursor> loader) {
217    }
218
219    private void showDialog(int messageId, final Uri contactUri) {
220        mDialog = new AlertDialog.Builder(getActivity())
221                .setTitle(R.string.deleteConfirmation_title)
222                .setIconAttribute(android.R.attr.alertDialogIcon)
223                .setMessage(messageId)
224                .setNegativeButton(android.R.string.cancel, null)
225                .setPositiveButton(android.R.string.ok,
226                    new DialogInterface.OnClickListener() {
227                        @Override
228                        public void onClick(DialogInterface dialog, int whichButton) {
229                            doDeleteContact(contactUri);
230                        }
231                    }
232                )
233                .create();
234
235        mDialog.setOnDismissListener(this);
236        mDialog.show();
237    }
238
239    @Override
240    public void onDismiss(DialogInterface dialog) {
241        mActive = false;
242        mDialog = null;
243    }
244
245    @Override
246    public void onSaveInstanceState(Bundle outState) {
247        super.onSaveInstanceState(outState);
248        outState.putBoolean(KEY_ACTIVE, mActive);
249        outState.putParcelable(KEY_CONTACT_URI, mContactUri);
250        outState.putBoolean(KEY_FINISH_WHEN_DONE, mFinishActivityWhenDone);
251    }
252
253    @Override
254    public void onActivityCreated(Bundle savedInstanceState) {
255        super.onActivityCreated(savedInstanceState);
256        if (savedInstanceState != null) {
257            mActive = savedInstanceState.getBoolean(KEY_ACTIVE);
258            mContactUri = savedInstanceState.getParcelable(KEY_CONTACT_URI);
259            mFinishActivityWhenDone = savedInstanceState.getBoolean(KEY_FINISH_WHEN_DONE);
260        }
261    }
262
263    protected void doDeleteContact(Uri contactUri) {
264        mContext.startService(ContactSaveService.createDeleteContactIntent(mContext, contactUri));
265        if (isAdded() && mFinishActivityWhenDone) {
266            getActivity().finish();
267        }
268    }
269}
270