1 /* 2 * Copyright (C) 2007 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.voicedialer; 18 19 import android.app.Activity; 20 import android.database.Cursor; 21 import android.database.DatabaseUtils; 22 import android.provider.ContactsContract.CommonDataKinds.Phone; 23 import android.provider.CallLog; 24 import android.util.Log; 25 import java.io.BufferedReader; 26 import java.io.File; 27 import java.io.FileReader; 28 import java.io.IOException; 29 import java.util.ArrayList; 30 import java.util.List; 31 32 33 /** 34 * This class represents a person who may be called via the VoiceDialer app. 35 * The person has a name and a list of phones (home, mobile, work, other). 36 */ 37 public class VoiceContact { 38 private static final String TAG = "VoiceContact"; 39 40 /** 41 * Corresponding row doesn't exist. 42 */ 43 public static final long ID_UNDEFINED = -1; 44 45 public final String mName; 46 public final long mContactId; 47 public final long mPrimaryId; 48 public final long mHomeId; 49 public final long mMobileId; 50 public final long mWorkId; 51 public final long mOtherId; 52 /** 53 * Id for a phone number which doesn't belong to any other ids stored above. 54 */ 55 public final long mFallbackId; 56 57 /** 58 * Constructor. 59 * 60 * @param name person's name. 61 * @param contactId ID in person table. 62 * @param primaryId primary ID in phone table. 63 * @param homeId home ID in phone table. 64 * @param mobileId mobile ID in phone table. 65 * @param workId work ID in phone table. 66 * @param otherId other ID in phone table. 67 */ 68 private VoiceContact(String name, long contactId, long primaryId, 69 long homeId, long mobileId, long workId, long otherId, long fallbackId) { 70 mName = name; 71 mContactId = contactId; 72 mPrimaryId = primaryId; 73 mHomeId = homeId; 74 mMobileId = mobileId; 75 mWorkId = workId; 76 mOtherId = otherId; 77 mFallbackId = fallbackId; 78 } 79 80 @Override 81 public int hashCode() { 82 final int LARGE_PRIME = 1610612741; 83 int hash = 0; 84 hash = LARGE_PRIME * (hash + (int)mContactId); 85 hash = LARGE_PRIME * (hash + (int)mPrimaryId); 86 hash = LARGE_PRIME * (hash + (int)mHomeId); 87 hash = LARGE_PRIME * (hash + (int)mMobileId); 88 hash = LARGE_PRIME * (hash + (int)mWorkId); 89 hash = LARGE_PRIME * (hash + (int)mOtherId); 90 hash = LARGE_PRIME * (hash + (int)mFallbackId); 91 return mName.hashCode() + hash; 92 } 93 94 @Override 95 public String toString() { 96 return "mName=" + mName 97 + " mPersonId=" + mContactId 98 + " mPrimaryId=" + mPrimaryId 99 + " mHomeId=" + mHomeId 100 + " mMobileId=" + mMobileId 101 + " mWorkId=" + mWorkId 102 + " mOtherId=" + mOtherId 103 + " mFallbackId=" + mFallbackId; 104 } 105 106 /** 107 * @param activity The VoiceDialerActivity instance. 108 * @return List of {@link VoiceContact} from 109 * the contact list content provider. 110 */ 111 public static List<VoiceContact> getVoiceContacts(Activity activity) { 112 if (false) Log.d(TAG, "VoiceContact.getVoiceContacts"); 113 114 List<VoiceContact> contacts = new ArrayList<VoiceContact>(); 115 116 String[] phonesProjection = new String[] { 117 Phone._ID, 118 Phone.TYPE, 119 Phone.IS_PRIMARY, 120 // TODO: handle type != 0,1,2, and use LABEL 121 Phone.LABEL, 122 Phone.DISPLAY_NAME, 123 Phone.CONTACT_ID, 124 }; 125 126 // Table is sorted by number of times contacted and name. If we cannot fit all contacts 127 // in the recognizer, we will at least have the commonly used ones. 128 Cursor cursor = activity.getContentResolver().query( 129 Phone.CONTENT_URI, phonesProjection, 130 Phone.NUMBER + " NOT NULL", null, 131 Phone.LAST_TIME_CONTACTED + " DESC, " 132 + Phone.DISPLAY_NAME + " ASC, " 133 + Phone._ID + " DESC"); 134 135 final int phoneIdColumn = cursor.getColumnIndexOrThrow(Phone._ID); 136 final int typeColumn = cursor.getColumnIndexOrThrow(Phone.TYPE); 137 final int isPrimaryColumn = cursor.getColumnIndexOrThrow(Phone.IS_PRIMARY); 138 final int labelColumn = cursor.getColumnIndexOrThrow(Phone.LABEL); 139 final int nameColumn = cursor.getColumnIndexOrThrow(Phone.DISPLAY_NAME); 140 final int personIdColumn = cursor.getColumnIndexOrThrow(Phone.CONTACT_ID); 141 142 // pieces of next VoiceContact 143 String name = null; 144 long personId = ID_UNDEFINED; 145 long primaryId = ID_UNDEFINED; 146 long homeId = ID_UNDEFINED; 147 long mobileId = ID_UNDEFINED; 148 long workId = ID_UNDEFINED; 149 long otherId = ID_UNDEFINED; 150 long fallbackId = ID_UNDEFINED; 151 152 // loop over phone table 153 for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { 154 long phoneIdAtCursor = cursor.getLong(phoneIdColumn); 155 int typeAtCursor = cursor.getInt(typeColumn); 156 long isPrimaryAtCursor = cursor.getLong(isPrimaryColumn); 157 String labelAtCursor = cursor.getString(labelColumn); 158 String nameAtCursor = cursor.getString(nameColumn); 159 long personIdAtCursor = cursor.getLong(personIdColumn); 160 161 /* 162 if (false) { 163 Log.d(TAG, "phoneId=" + phoneIdAtCursor 164 + " type=" + typeAtCursor 165 + " isPrimary=" + isPrimaryAtCursor 166 + " label=" + labelAtCursor 167 + " name=" + nameAtCursor 168 + " personId=" + personIdAtCursor 169 ); 170 } 171 */ 172 173 // encountered a new name, so generate current VoiceContact 174 if (name != null && !name.equals(nameAtCursor)) { 175 contacts.add(new VoiceContact(name, personId, primaryId, 176 homeId, mobileId, workId, otherId, fallbackId)); 177 name = null; 178 } 179 180 // start accumulating pieces for a new VoiceContact 181 if (name == null) { 182 name = nameAtCursor; 183 personId = personIdAtCursor; 184 primaryId = ID_UNDEFINED; 185 homeId = ID_UNDEFINED; 186 mobileId = ID_UNDEFINED; 187 workId = ID_UNDEFINED; 188 otherId = ID_UNDEFINED; 189 fallbackId = ID_UNDEFINED; 190 } 191 192 // if labeled, then patch to HOME/MOBILE/WORK/OTHER 193 if (typeAtCursor == Phone.TYPE_CUSTOM && 194 labelAtCursor != null) { 195 String label = labelAtCursor.toLowerCase(); 196 if (label.contains("home") || label.contains("house")) { 197 typeAtCursor = Phone.TYPE_HOME; 198 } 199 else if (label.contains("mobile") || label.contains("cell")) { 200 typeAtCursor = Phone.TYPE_MOBILE; 201 } 202 else if (label.contains("work") || label.contains("office")) { 203 typeAtCursor = Phone.TYPE_WORK; 204 } 205 else if (label.contains("other")) { 206 typeAtCursor = Phone.TYPE_OTHER; 207 } 208 } 209 210 boolean idAtCursorWasUsed = false; 211 // save the home, mobile, or work phone id 212 switch (typeAtCursor) { 213 case Phone.TYPE_HOME: 214 homeId = phoneIdAtCursor; 215 if (isPrimaryAtCursor != 0) { 216 primaryId = phoneIdAtCursor; 217 } 218 idAtCursorWasUsed = true; 219 break; 220 case Phone.TYPE_MOBILE: 221 mobileId = phoneIdAtCursor; 222 if (isPrimaryAtCursor != 0) { 223 primaryId = phoneIdAtCursor; 224 } 225 idAtCursorWasUsed = true; 226 break; 227 case Phone.TYPE_WORK: 228 workId = phoneIdAtCursor; 229 if (isPrimaryAtCursor != 0) { 230 primaryId = phoneIdAtCursor; 231 } 232 idAtCursorWasUsed = true; 233 break; 234 case Phone.TYPE_OTHER: 235 otherId = phoneIdAtCursor; 236 if (isPrimaryAtCursor != 0) { 237 primaryId = phoneIdAtCursor; 238 } 239 idAtCursorWasUsed = true; 240 break; 241 } 242 243 if (fallbackId == ID_UNDEFINED && !idAtCursorWasUsed) { 244 fallbackId = phoneIdAtCursor; 245 } 246 } 247 248 // generate the last VoiceContact 249 if (name != null) { 250 contacts.add(new VoiceContact(name, personId, primaryId, 251 homeId, mobileId, workId, otherId, fallbackId)); 252 } 253 254 // clean up cursor 255 cursor.close(); 256 257 if (false) Log.d(TAG, "VoiceContact.getVoiceContacts " + contacts.size()); 258 259 return contacts; 260 } 261 262 /** 263 * @param contactsFile File containing a list of names, 264 * one per line. 265 * @return a List of {@link VoiceContact} in a File. 266 */ 267 public static List<VoiceContact> getVoiceContactsFromFile(File contactsFile) { 268 if (false) Log.d(TAG, "getVoiceContactsFromFile " + contactsFile); 269 270 List<VoiceContact> contacts = new ArrayList<VoiceContact>(); 271 272 // read from a file 273 BufferedReader br = null; 274 try { 275 br = new BufferedReader(new FileReader(contactsFile), 8192); 276 String name; 277 for (int id = 1; (name = br.readLine()) != null; id++) { 278 contacts.add(new VoiceContact(name, id, ID_UNDEFINED, 279 ID_UNDEFINED, ID_UNDEFINED, ID_UNDEFINED, ID_UNDEFINED, ID_UNDEFINED)); 280 } 281 } 282 catch (IOException e) { 283 if (false) Log.d(TAG, "getVoiceContactsFromFile failed " + e); 284 } 285 finally { 286 try { 287 br.close(); 288 } catch (IOException e) { 289 if (false) Log.d(TAG, "getVoiceContactsFromFile failed during close " + e); 290 } 291 } 292 293 if (false) Log.d(TAG, "getVoiceContactsFromFile " + contacts.size()); 294 295 return contacts; 296 } 297 298 /** 299 * @param activity The VoiceDialerActivity instance. 300 * @return String of last number dialed. 301 */ 302 public static String redialNumber(Activity activity) { 303 Cursor cursor = activity.getContentResolver().query( 304 CallLog.Calls.CONTENT_URI, 305 new String[] { CallLog.Calls.NUMBER }, 306 CallLog.Calls.TYPE + "=" + CallLog.Calls.OUTGOING_TYPE, 307 null, 308 CallLog.Calls.DEFAULT_SORT_ORDER + " LIMIT 1"); 309 String number = null; 310 if (cursor.getCount() >= 1) { 311 cursor.moveToNext(); 312 int column = cursor.getColumnIndexOrThrow(CallLog.Calls.NUMBER); 313 number = cursor.getString(column); 314 } 315 cursor.close(); 316 317 if (false) Log.d(TAG, "redialNumber " + number); 318 319 return number; 320 } 321 322 } 323