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