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