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 */ VoiceContact(String name, long contactId, long primaryId, long homeId, long mobileId, long workId, long otherId, long fallbackId)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 hashCode()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 toString()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 */ getVoiceContacts(Activity activity)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 */ getVoiceContactsFromFile(File contactsFile)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 */ redialNumber(Activity activity)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