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