21
21
* along with this program. If not, see <http://www.gnu.org/licenses/>.
24
package am.ed.importcontacts;
24
package org.waxworlds.edam.importcontacts;
26
26
import java.util.HashMap;
27
27
import java.util.HashSet;
28
import java.util.Locale;
29
import org.waxworlds.edam.importcontacts.Importer.AbortImportException;
31
import android.app.Activity;
32
import android.database.Cursor;
33
import android.provider.Contacts;
30
36
public class ContactsCache
33
* A thing that can be used to identify (or lookup) a contact within the
34
* contacts cache. It is not a reference to a cache entry and may not
35
* identify an existing contact in the cache.
39
* Information that can be used to identify a contact within the cache
37
public static class CacheIdentifier
41
static public class CacheIdentifier
39
public enum Type { NAME, ORGANISATION, PRIMARY_NUMBER, PRIMARY_EMAIL }
44
NONE, NAME, ORGANISATION, PRIMARY_NUMBER, PRIMARY_EMAIL }
41
46
private Type _type;
42
47
private String _detail;
45
* Obtain a cache identifier. This routine is designed to be as robust
46
* as possible (in terms of bad or null detail values), and to return
47
* null when a cache identifier can not be created.
48
* @param type the detail type
49
* @param detail the detail
50
* @return the cache identifier, or null
52
public static CacheIdentifier factory( Type type, String detail )
56
case NAME: detail = normaliseName( detail ); break;
57
case ORGANISATION: detail = normaliseOrganisation( detail ); break;
58
case PRIMARY_NUMBER: detail = normalisePhoneNumber( detail ); break;
59
case PRIMARY_EMAIL: detail = normaliseEmailAddress( detail ); break;
62
if( detail == null ) return null;
63
return new CacheIdentifier( type, detail );
67
* Obtain a cache identifier from contact data. This routine is
68
* designed to be as robust as possible and may return null when a cache
69
* identifier can not be created.
70
* @param contact the contact data
71
* @return the cache identifier, or null
73
public static CacheIdentifier factory( Importer.ContactData contact )
75
CacheIdentifier ret = null;
77
if( contact.hasName() )
78
ret = factory( CacheIdentifier.Type.NAME,
80
if( ret == null && contact.hasPrimaryOrganisation() )
81
ret = factory( CacheIdentifier.Type.ORGANISATION,
82
contact.getPrimaryOrganisation() );
83
if( ret == null && contact.hasPrimaryNumber() )
84
ret = factory( CacheIdentifier.Type.PRIMARY_NUMBER,
85
contact.getPrimaryNumber() );
86
if( ret == null && contact.hasPrimaryEmail() )
87
ret = factory( CacheIdentifier.Type.PRIMARY_EMAIL,
88
contact.getPrimaryEmail() );
49
protected CacheIdentifier()
93
54
protected CacheIdentifier( Type type, String detail )
110
71
// mappings of contact names, organisations and primary numbers to ids
111
private HashMap< String, Long > _contactsByName
112
= new HashMap< String, Long >();
113
private HashMap< String, Long > _contactsByOrg
114
= new HashMap< String, Long >();
115
private HashMap< String, Long > _contactsByNumber
116
= new HashMap< String, Long >();
117
private HashMap< String, Long > _contactsByEmail
118
= new HashMap< String, Long >();
72
private HashMap< String, Long > _contactsByName;
73
private HashMap< String, Long > _contactsByOrg;
74
private HashMap< String, Long > _contactsByNumber;
75
private HashMap< String, Long > _contactsByEmail;
120
77
// mapping of contact ids to sets of associated data
121
private HashMap< Long, HashSet< String > > _contactNumbers
122
= new HashMap< Long, HashSet< String > >();
123
private HashMap< Long, HashSet< String > > _contactEmails
124
= new HashMap< Long, HashSet< String > >();
125
private HashMap< Long, HashSet< String > > _contactAddresses
126
= new HashMap< Long, HashSet< String > >();
127
private HashMap< Long, HashSet< String > > _contactOrganisations
128
= new HashMap< Long, HashSet< String > >();
129
private HashMap< Long, HashSet< String > > _contactNotes
130
= new HashMap< Long, HashSet< String > >();
78
private HashMap< Long, HashSet< String > > _contactNumbers;
79
private HashMap< Long, HashSet< String > > _contactEmails;
80
private HashMap< Long, HashSet< String > > _contactAddresses;
81
private HashMap< Long, HashSet< String > > _contactOrganisations;
83
public static CacheIdentifier createIdentifier(
84
Importer.ContactData contact )
86
if( contact.hasName() ) {
87
String name = normaliseName( contact.getName() );
89
return new CacheIdentifier(
90
CacheIdentifier.Type.NAME, name );
93
if( contact.hasPrimaryOrganisation() ) {
94
String organisation = normaliseOrganisation(
95
contact.getPrimaryOrganisation() );
96
if( organisation != null )
97
return new CacheIdentifier(
98
CacheIdentifier.Type.ORGANISATION, organisation );
101
if( contact.hasPrimaryNumber() ) {
102
String number = normalisePhoneNumber( contact.getPrimaryNumber() );
104
return new CacheIdentifier(
105
CacheIdentifier.Type.PRIMARY_NUMBER, number );
108
if( contact.hasPrimaryEmail() ) {
109
String email = normaliseEmailAddress( contact.getPrimaryEmail() );
111
return new CacheIdentifier(
112
CacheIdentifier.Type.PRIMARY_EMAIL, email );
132
118
public boolean canLookup( CacheIdentifier identifier )
134
120
return lookup( identifier ) != null;
138
* Retrieve the contact id of a contact identified by the specified cache
139
* identifier, if it exists.
140
* @param identifier the cache identifier
141
* @return a contact id, or null
143
123
public Long lookup( CacheIdentifier identifier )
145
125
switch( identifier.getType() )
299
265
set = new HashSet< String >();
300
266
_contactOrganisations.put( id, set );
302
set.add( organisation );
305
public boolean hasAssociatedNote( Long id, String note )
307
note = normaliseNote( note );
308
if( note == null ) return false;
310
HashSet< String > set = _contactNotes.get( id );
311
return set != null && set.contains( note );
314
public void addAssociatedNote( Long id, String note )
316
note = normaliseNote( note );
317
if( note == null ) return;
319
HashSet< String > set = _contactNotes.get( id );
321
set = new HashSet< String >();
322
_contactNotes.put( id, set );
327
static public String normaliseName( String name )
268
set.add( normaliseOrganisation( organisation ) );
271
public void buildCache( Activity activity )
272
throws AbortImportException
277
_contactsByName = new HashMap< String, Long >();
278
_contactsByOrg = new HashMap< String, Long >();
279
_contactsByNumber = new HashMap< String, Long >();
280
_contactsByEmail = new HashMap< String, Long >();
282
// init associated data cache
283
_contactNumbers = new HashMap< Long, HashSet< String > >();
284
_contactEmails = new HashMap< Long, HashSet< String > >();
285
_contactAddresses = new HashMap< Long, HashSet< String > >();
286
_contactOrganisations = new HashMap< Long, HashSet< String > >();
288
// set of contact ids that we have not yet added
289
HashSet< Long > unadded = new HashSet< Long >();
292
cur = activity.managedQuery( Contacts.People.CONTENT_URI,
295
Contacts.People.NAME,
296
}, null, null, null );
297
while( cur.moveToNext() ) {
298
Long id = cur.getLong(
299
cur.getColumnIndex( Contacts.People._ID ) );
300
String name = normaliseName( cur.getString(
301
cur.getColumnIndex( Contacts.People.NAME ) ) );
304
// if we can, add a lookup for the contact id by name
305
if( name.length() > 0 ) {
306
addLookup( new CacheIdentifier(
307
CacheIdentifier.Type.NAME, name ), id );
312
// record that a lookup for this contact's id still needs to be
313
// added by some other means
317
// get contact organisations, primary ones first
318
cur = activity.managedQuery( Contacts.Organizations.CONTENT_URI,
320
Contacts.Phones.PERSON_ID,
321
Contacts.Organizations.COMPANY,
322
}, null, null, Contacts.Organizations.ISPRIMARY + " DESC" );
323
while( cur.moveToNext() ) {
324
Long id = cur.getLong( cur.getColumnIndex(
325
Contacts.Organizations.PERSON_ID ) );
326
String organisation = normaliseOrganisation( cur.getString(
327
cur.getColumnIndex( Contacts.Organizations.COMPANY ) ) );
328
if( organisation != null )
330
// if this is an organisation name for a contact for whom we
331
// have not added a lookup, add a lookup for the contact id
333
if( unadded.contains( id ) ) {
334
addLookup( new CacheIdentifier(
335
CacheIdentifier.Type.ORGANISATION, organisation ), id );
336
unadded.remove( id );
339
// add associated data
340
addAssociatedOrganisation( id, organisation );
344
// get all phone numbers, primary ones first
345
cur = activity.managedQuery( Contacts.Phones.CONTENT_URI,
347
Contacts.Phones.PERSON_ID,
348
Contacts.Phones.NUMBER,
349
}, null, null, Contacts.Phones.ISPRIMARY + " DESC" );
350
while( cur.moveToNext() ) {
351
Long id = cur.getLong(
352
cur.getColumnIndex( Contacts.Phones.PERSON_ID ) );
353
String number = normalisePhoneNumber( cur.getString(
354
cur.getColumnIndex( Contacts.Phones.NUMBER ) ) );
357
// if this is a number for a contact for whom we have not
358
// added a lookup, add a lookup for the contact id by phone
360
if( unadded.contains( id ) ) {
361
addLookup( new CacheIdentifier(
362
CacheIdentifier.Type.PRIMARY_NUMBER, number ), id );
363
unadded.remove( id );
366
// add associated data
367
addAssociatedNumber( id, number );
371
// now get all email addresses, primary ones first, and postal addresses
372
cur = activity.managedQuery( Contacts.ContactMethods.CONTENT_URI,
374
Contacts.ContactMethods.PERSON_ID,
375
Contacts.ContactMethods.DATA,
376
Contacts.ContactMethods.KIND,
377
}, Contacts.ContactMethods.KIND + " IN( ?, ? )", new String[] {
378
"" + Contacts.KIND_EMAIL,
379
"" + Contacts.KIND_POSTAL,
380
}, Contacts.ContactMethods.ISPRIMARY + " DESC" );
381
while( cur.moveToNext() ) {
382
Long id = cur.getLong(
383
cur.getColumnIndex( Contacts.ContactMethods.PERSON_ID ) );
384
int kind = cur.getInt(
385
cur.getColumnIndex( Contacts.ContactMethods.KIND ) );
386
if( kind == Contacts.KIND_EMAIL )
388
String email = normaliseEmailAddress( cur.getString(
389
cur.getColumnIndex( Contacts.ContactMethods.DATA ) ) );
392
// if this is an email address for a contact for whom we
393
// have not added a lookup, add a lookup for the contact
394
// id by email address
395
if( unadded.contains( id ) ) {
396
addLookup( new CacheIdentifier(
397
CacheIdentifier.Type.PRIMARY_EMAIL, email ), id );
398
unadded.remove( id );
401
// add associated data
402
addAssociatedEmail( id, email );
405
else if( kind == Contacts.KIND_POSTAL )
407
String address = normaliseAddress( cur.getString(
408
cur.getColumnIndex( Contacts.ContactMethods.DATA ) ) );
409
if( address != null )
411
// add associated data
412
addAssociatedAddress( id, address );
418
static private String normaliseName( String name )
329
420
if( name == null ) return null;
330
421
name = name.trim();
331
422
return name.length() > 0? name : null;
334
static public String normalisePhoneNumber( String number )
425
static private String normalisePhoneNumber( String number )
336
427
if( number == null ) return null;
337
428
number = number.trim().replaceAll( "[-\\(\\) ]", "" );
338
429
return number.length() > 0? number : null;
341
static public String normaliseEmailAddress( String email )
432
static private String normaliseEmailAddress( String email )
343
434
if( email == null ) return null;
344
email = email.trim().toLowerCase( Locale.US );
435
email = email.trim().toLowerCase();
345
436
return email.length() > 0? email : null;
348
static public String normaliseOrganisation( String organisation )
439
static private String normaliseOrganisation( String organisation )
350
441
if( organisation == null ) return null;
351
442
organisation = organisation.trim();
352
443
return organisation.length() > 0? organisation : null;
355
static public String normaliseAddress( String address )
446
static private String normaliseAddress( String address )
357
448
if( address == null ) return null;
358
449
address = address.trim();
359
450
return address.length() > 0? address : null;
362
static public String normaliseNote( String note )
364
if( note == null ) return null;
366
return note.length() > 0? note : null;