• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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 
17 package com.android.contacts.ui;
18 
19 import com.android.contacts.ContactsListActivity;
20 import com.android.contacts.R;
21 import com.android.contacts.util.Constants;
22 import com.android.contacts.util.NotifyingAsyncQueryHandler;
23 
24 import android.app.Activity;
25 import android.app.AlertDialog;
26 import android.app.Dialog;
27 import android.content.ComponentName;
28 import android.content.ContentUris;
29 import android.content.DialogInterface;
30 import android.content.EntityIterator;
31 import android.content.Intent;
32 import android.database.Cursor;
33 import android.net.Uri;
34 import android.os.Bundle;
35 import android.provider.ContactsContract.Contacts;
36 import android.provider.ContactsContract.Intents;
37 import android.provider.ContactsContract.PhoneLookup;
38 import android.provider.ContactsContract.RawContacts;
39 import android.provider.ContactsContract.CommonDataKinds.Email;
40 import android.util.Log;
41 
42 /**
43  * Handle several edge cases around showing or possibly creating contacts in
44  * connected with a specific E-mail address or phone number. Will search based
45  * on incoming {@link Intent#getData()} as described by
46  * {@link Intents#SHOW_OR_CREATE_CONTACT}.
47  * <ul>
48  * <li>If no matching contacts found, will prompt user with dialog to add to a
49  * contact, then will use {@link Intent#ACTION_INSERT_OR_EDIT} to let create new
50  * contact or edit new data into an existing one.
51  * <li>If one matching contact found, directly show {@link Intent#ACTION_VIEW}
52  * that specific contact.
53  * <li>If more than one matching found, show list of matching contacts using
54  * {@link Intent#ACTION_SEARCH}.
55  * </ul>
56  */
57 public final class ShowOrCreateActivity extends Activity implements
58         NotifyingAsyncQueryHandler.AsyncQueryListener {
59     static final String TAG = "ShowOrCreateActivity";
60     static final boolean LOGD = false;
61 
62     static final String[] PHONES_PROJECTION = new String[] {
63         PhoneLookup._ID,
64     };
65 
66     static final String[] CONTACTS_PROJECTION = new String[] {
67         RawContacts.CONTACT_ID,
68     };
69 
70     static final int CONTACT_ID_INDEX = 0;
71 
72     static final int CREATE_CONTACT_DIALOG = 1;
73 
74     static final int QUERY_TOKEN = 42;
75 
76     private NotifyingAsyncQueryHandler mQueryHandler;
77 
78     private Bundle mCreateExtras;
79     private String mCreateDescrip;
80     private boolean mCreateForce;
81 
82     @Override
onCreate(Bundle icicle)83     protected void onCreate(Bundle icicle) {
84         super.onCreate(icicle);
85 
86         // Create handler if doesn't exist, otherwise cancel any running
87         if (mQueryHandler == null) {
88             mQueryHandler = new NotifyingAsyncQueryHandler(this, this);
89         } else {
90             mQueryHandler.cancelOperation(QUERY_TOKEN);
91         }
92 
93         final Intent intent = getIntent();
94         final Uri data = intent.getData();
95 
96         // Unpack scheme and target data from intent
97         String scheme = null;
98         String ssp = null;
99         if (data != null) {
100             scheme = data.getScheme();
101             ssp = data.getSchemeSpecificPart();
102         }
103 
104         // Build set of extras for possible use when creating contact
105         mCreateExtras = new Bundle();
106         Bundle originalExtras = intent.getExtras();
107         if (originalExtras != null) {
108             mCreateExtras.putAll(originalExtras);
109         }
110 
111         // Read possible extra with specific title
112         mCreateDescrip = intent.getStringExtra(Intents.EXTRA_CREATE_DESCRIPTION);
113         if (mCreateDescrip == null) {
114             mCreateDescrip = ssp;
115         }
116 
117         // Allow caller to bypass dialog prompt
118         mCreateForce = intent.getBooleanExtra(Intents.EXTRA_FORCE_CREATE, false);
119 
120         // Handle specific query request
121         if (Constants.SCHEME_MAILTO.equals(scheme)) {
122             mCreateExtras.putString(Intents.Insert.EMAIL, ssp);
123 
124             Uri uri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, Uri.encode(ssp));
125             mQueryHandler.startQuery(QUERY_TOKEN, null, uri, CONTACTS_PROJECTION, null, null, null);
126 
127         } else if (Constants.SCHEME_TEL.equals(scheme)) {
128             mCreateExtras.putString(Intents.Insert.PHONE, ssp);
129 
130             Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, ssp);
131             mQueryHandler.startQuery(QUERY_TOKEN, null, uri, PHONES_PROJECTION, null, null, null);
132 
133         } else {
134             Log.w(TAG, "Invalid intent:" + getIntent());
135             finish();
136         }
137     }
138 
139     @Override
onStop()140     protected void onStop() {
141         super.onStop();
142         if (mQueryHandler != null) {
143             mQueryHandler.cancelOperation(QUERY_TOKEN);
144         }
145     }
146 
147     /** {@inheritDoc} */
onQueryComplete(int token, Object cookie, Cursor cursor)148     public void onQueryComplete(int token, Object cookie, Cursor cursor) {
149         if (cursor == null) {
150             // Bail when problem running query in background
151             finish();
152             return;
153         }
154 
155         // Count contacts found by query
156         int count = 0;
157         long contactId = -1;
158         try {
159             count = cursor.getCount();
160             if (count == 1 && cursor.moveToFirst()) {
161                 // Try reading ID if only one contact returned
162                 contactId = cursor.getLong(CONTACT_ID_INDEX);
163             }
164         } finally {
165             cursor.close();
166         }
167 
168         if (count == 1 && contactId != -1) {
169             // If we only found one item, jump right to viewing it
170             final Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
171             final Intent viewIntent = new Intent(Intent.ACTION_VIEW, contactUri);
172             startActivity(viewIntent);
173             finish();
174 
175         } else if (count > 1) {
176             // If more than one, show pick list
177             Intent listIntent = new Intent(Intent.ACTION_SEARCH);
178             listIntent.setComponent(new ComponentName(this, ContactsListActivity.class));
179             listIntent.putExtras(mCreateExtras);
180             startActivity(listIntent);
181             finish();
182 
183         } else {
184             // No matching contacts found
185             if (mCreateForce) {
186                 // Forced to create new contact
187                 Intent createIntent = new Intent(Intent.ACTION_INSERT, RawContacts.CONTENT_URI);
188                 createIntent.putExtras(mCreateExtras);
189                 createIntent.setType(RawContacts.CONTENT_TYPE);
190 
191                 startActivity(createIntent);
192                 finish();
193 
194             } else {
195 	        showDialog(CREATE_CONTACT_DIALOG);
196            }
197         }
198     }
199 
200     @Override
onCreateDialog(int id)201     protected Dialog onCreateDialog(int id) {
202         switch(id) {
203 	    case CREATE_CONTACT_DIALOG:
204                 // Prompt user to insert or edit contact
205                 final Intent createIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
206                 createIntent.putExtras(mCreateExtras);
207                 createIntent.setType(RawContacts.CONTENT_ITEM_TYPE);
208 
209                 final CharSequence message = getResources().getString(
210                         R.string.add_contact_dlg_message_fmt, mCreateDescrip);
211 
212                 return new AlertDialog.Builder(this)
213                         .setTitle(R.string.add_contact_dlg_title)
214                         .setMessage(message)
215                         .setPositiveButton(android.R.string.ok,
216                                 new IntentClickListener(this, createIntent))
217                         .setNegativeButton(android.R.string.cancel,
218                                 new IntentClickListener(this, null))
219                         .create();
220         }
221 	return super.onCreateDialog(id);
222     }
223 
224     /** {@inheritDoc} */
onQueryEntitiesComplete(int token, Object cookie, EntityIterator iterator)225     public void onQueryEntitiesComplete(int token, Object cookie, EntityIterator iterator) {
226         // No actions
227     }
228 
229     /**
230      * Listener for {@link DialogInterface} that launches a given {@link Intent}
231      * when clicked. When clicked, this also closes the parent using
232      * {@link Activity#finish()}.
233      */
234     private static class IntentClickListener implements DialogInterface.OnClickListener {
235         private Activity mParent;
236         private Intent mIntent;
237 
238         /**
239          * @param parent {@link Activity} to use for launching target.
240          * @param intent Target {@link Intent} to launch when clicked.
241          */
IntentClickListener(Activity parent, Intent intent)242         public IntentClickListener(Activity parent, Intent intent) {
243             mParent = parent;
244             mIntent = intent;
245         }
246 
onClick(DialogInterface dialog, int which)247         public void onClick(DialogInterface dialog, int which) {
248             if (mIntent != null) {
249                 mParent.startActivity(mIntent);
250             }
251             mParent.finish();
252         }
253     }
254 }
255