/android/import-contacts

To get this branch, use:
bzr branch http://bzr.ed.am/android/import-contacts
6 by edam
- added GPL header comments to all files
1
/*
2
 * Importer.java
3
 *
4
 * Copyright (C) 2009 Tim Marston <edam@waxworlds.org>
5
 *
6
 * This file is part of the Import Contacts program (hereafter referred
7
 * to as "this program"). For more information, see
8
 * http://www.waxworlds.org/edam/software/android/import-contacts
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, either version 3 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 */
23
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
24
package org.waxworlds.edam.importcontacts;
1 by edam
Initial import
25
26
import java.util.HashMap;
27
import java.util.HashSet;
28
import java.util.Iterator;
29
import java.util.Set;
30
import java.util.regex.Matcher;
31
import java.util.regex.Pattern;
32
33
import android.content.ContentUris;
34
import android.content.ContentValues;
35
import android.content.SharedPreferences;
36
import android.database.Cursor;
37
import android.net.Uri;
38
import android.os.Message;
39
import android.provider.Contacts;
40
41
public class Importer extends Thread
42
{
3 by edam
- added "all done" message
43
	public final static int ACTION_ABORT = 1;
44
	public final static int ACTION_ALLDONE = 2;
1 by edam
Initial import
45
46
	public final static int RESPONSE_NEGATIVE = 0;
47
	public final static int RESPONSE_POSITIVE = 1;
48
49
	public final static int RESPONSEEXTRA_NONE = 0;
50
	public final static int RESPONSEEXTRA_ALWAYS = 1;
51
52
	private Doit _doit;
53
	private int _response;
54
	private int _responseExtra;
55
	private HashMap< String, Long > _contacts;
56
	private HashMap< Long, HashSet< String > > _contactNumbers;
57
	private HashMap< Long, HashSet< String > > _contactEmails;
58
	private int _mergeSetting;
59
	private int _lastMergeDecision;
60
	private boolean _abort = false;
3 by edam
- added "all done" message
61
	private boolean _isFinished = false;
1 by edam
Initial import
62
63
	public class ContactData
64
	{
65
		class PhoneData
66
		{
67
			public String _number;
68
			public int _type;
69
			public boolean _isPreferred;
70
71
			public PhoneData( String number, int type, boolean isPreferred ) {
72
				_number = number;
73
				_type = type;
74
				_isPreferred = isPreferred;
75
			}
76
77
			public String getNumber() {
78
				return _number;
79
			}
80
81
			public int getType() {
82
				return _type;
83
			}
84
85
			public boolean isPreferred() {
86
				return _isPreferred;
87
			}
88
		}
89
90
		class EmailData
91
		{
92
			private String _email;
93
			public int _type;
94
			private boolean _isPreferred;
95
96
			public EmailData( String email, int type, boolean isPreferred ) {
97
				_email = email;
98
				_type = type;
99
				_isPreferred = isPreferred;
100
			}
101
102
			public String getAddress() {
103
				return _email;
104
			}
105
106
			public int getType() {
107
				return _type;
108
			}
109
110
			public boolean isPreferred() {
111
				return _isPreferred;
112
			}
113
		}
114
115
		public String _name = null;
116
		public HashMap< String, PhoneData > _phones = null;
117
		public HashMap< String, EmailData > _emails = null;
118
119
		protected void setName( String name )
120
		{
121
			_name = name;
122
		}
123
124
		public String getName()
125
		{
126
			return _name;
127
		}
128
129
		protected void addPhone( String number, int type, boolean isPreferred )
130
		{
131
			if( _phones == null ) _phones = new HashMap< String, PhoneData >();
132
			if( !_phones.containsKey( number ) )
133
				_phones.put( number,
36 by edam
- formatting: removed some double-indents on overrunning lines
134
					new PhoneData( number, type, isPreferred ) );
1 by edam
Initial import
135
		}
136
137
		protected void addEmail( String email, int type, boolean isPreferred )
138
		{
139
			if( _emails == null ) _emails = new HashMap< String, EmailData >();
140
			if( !_emails.containsKey( email ) )
141
				_emails.put( email, new EmailData( email, type, isPreferred ) );
142
		}
143
	}
144
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
145
	@SuppressWarnings("serial")
1 by edam
Initial import
146
	protected class AbortImportException extends Exception { };
147
148
	public Importer( Doit doit )
149
	{
150
		_doit = doit;
151
152
		SharedPreferences prefs = getSharedPreferences();
9 by edam
- added scroll view to all layouts
153
		_mergeSetting = prefs.getInt( "merge_setting", Doit.ACTION_PROMPT );
1 by edam
Initial import
154
	}
155
156
	@Override
157
	public void run()
158
	{
159
		try
160
		{
161
			// cache current contact names
162
			buildContactsCache();
163
164
			// do the import
165
			onImport();
166
167
			// done!
3 by edam
- added "all done" message
168
			finish( ACTION_ALLDONE );
1 by edam
Initial import
169
		}
170
		catch( AbortImportException e )
171
		{}
3 by edam
- added "all done" message
172
173
		// flag as finished to prevent interrupts
174
		setIsFinished();
175
	}
176
177
	synchronized private void setIsFinished()
178
	{
179
		_isFinished = true;
1 by edam
Initial import
180
	}
181
182
	protected void onImport() throws AbortImportException
183
	{
184
	}
185
186
	public void wake()
187
	{
188
		wake( 0, RESPONSEEXTRA_NONE );
189
	}
190
191
	public void wake( int response )
192
	{
193
		wake( response, RESPONSEEXTRA_NONE );
194
	}
195
196
	synchronized public void wake( int response, int responseExtra )
197
	{
198
		_response = response;
199
		_responseExtra = responseExtra;
200
		notify();
201
	}
202
3 by edam
- added "all done" message
203
	synchronized public boolean setAbort()
1 by edam
Initial import
204
	{
3 by edam
- added "all done" message
205
		if( !_isFinished && !_abort ) {
206
			_abort = true;
207
			notify();
208
			return true;
209
		}
210
		return false;
1 by edam
Initial import
211
	}
212
213
	protected SharedPreferences getSharedPreferences()
214
	{
215
		return _doit.getSharedPreferences();
216
	}
217
218
	protected void showError( int res ) throws AbortImportException
219
	{
220
		showError( _doit.getText( res ).toString() );
221
	}
222
223
	synchronized protected void showError( String message )
224
			throws AbortImportException
225
	{
226
		checkAbort();
227
		_doit._handler.sendMessage( Message.obtain(
36 by edam
- formatting: removed some double-indents on overrunning lines
228
			_doit._handler, Doit.MESSAGE_ERROR, message ) );
1 by edam
Initial import
229
		try {
230
			wait();
231
		}
232
		catch( InterruptedException e ) { }
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
233
3 by edam
- added "all done" message
234
		// no need to check if an abortion happened during the wait, we are
235
		// about to finish anyway!
236
		finish( ACTION_ABORT );
1 by edam
Initial import
237
	}
238
239
	protected void showFatalError( int res ) throws AbortImportException
240
	{
241
		showFatalError( _doit.getText( res ).toString() );
242
	}
243
244
	synchronized protected void showFatalError( String message )
245
			throws AbortImportException
246
	{
247
		checkAbort();
248
		_doit._handler.sendMessage( Message.obtain(
36 by edam
- formatting: removed some double-indents on overrunning lines
249
			_doit._handler, Doit.MESSAGE_ERROR, message ) );
1 by edam
Initial import
250
		try {
251
			wait();
252
		}
253
		catch( InterruptedException e ) { }
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
254
3 by edam
- added "all done" message
255
		// no need to check if an abortion happened during the wait, we are
256
		// about to finish anyway!
257
		finish( ACTION_ABORT );
1 by edam
Initial import
258
	}
259
260
	protected boolean showContinue( int res ) throws AbortImportException
261
	{
262
		return showContinue( _doit.getText( res ).toString() );
263
	}
264
265
	synchronized protected boolean showContinue( String message )
266
			throws AbortImportException
267
	{
268
		checkAbort();
269
		_doit._handler.sendMessage( Message.obtain(
36 by edam
- formatting: removed some double-indents on overrunning lines
270
			_doit._handler, Doit.MESSAGE_CONTINUEORABORT, message ) );
1 by edam
Initial import
271
		try {
272
			wait();
273
		}
274
		catch( InterruptedException e ) { }
3 by edam
- added "all done" message
275
276
		// check if an abortion happened during the wait
277
		checkAbort();
278
1 by edam
Initial import
279
		return _response == RESPONSE_POSITIVE;
280
	}
281
282
	protected void setProgressMessage( int res ) throws AbortImportException
283
	{
284
		checkAbort();
285
		_doit._handler.sendMessage( Message.obtain( _doit._handler,
36 by edam
- formatting: removed some double-indents on overrunning lines
286
			Doit.MESSAGE_SETPROGRESSMESSAGE, getText( res ) ) );
1 by edam
Initial import
287
	}
288
289
	protected void setProgressMax( int maxProgress )
290
			throws AbortImportException
291
	{
292
		checkAbort();
293
		_doit._handler.sendMessage( Message.obtain(
36 by edam
- formatting: removed some double-indents on overrunning lines
294
			_doit._handler, Doit.MESSAGE_SETMAXPROGRESS,
295
			new Integer( maxProgress ) ) );
1 by edam
Initial import
296
	}
297
298
	protected void setTmpProgress( int tmpProgress ) throws AbortImportException
299
	{
300
		checkAbort();
301
		_doit._handler.sendMessage( Message.obtain(
36 by edam
- formatting: removed some double-indents on overrunning lines
302
			_doit._handler, Doit.MESSAGE_SETTMPPROGRESS,
303
			new Integer( tmpProgress ) ) );
1 by edam
Initial import
304
	}
305
306
	protected void setProgress( int progress ) throws AbortImportException
307
	{
308
		checkAbort();
309
		_doit._handler.sendMessage( Message.obtain(
36 by edam
- formatting: removed some double-indents on overrunning lines
310
			_doit._handler, Doit.MESSAGE_SETPROGRESS,
311
			new Integer( progress ) ) );
1 by edam
Initial import
312
	}
313
3 by edam
- added "all done" message
314
	protected void finish( int action ) throws AbortImportException
1 by edam
Initial import
315
	{
3 by edam
- added "all done" message
316
		// update UI to reflect action
317
		int message;
318
		switch( action )
319
		{
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
320
		case ACTION_ALLDONE:	message = Doit.MESSAGE_ALLDONE; break;
3 by edam
- added "all done" message
321
		default:	// fall through
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
322
		case ACTION_ABORT:		message = Doit.MESSAGE_ABORT; break;
3 by edam
- added "all done" message
323
		}
324
		_doit._handler.sendEmptyMessage( message );
1 by edam
Initial import
325
3 by edam
- added "all done" message
326
		// stop
327
		throw new AbortImportException();
1 by edam
Initial import
328
	}
329
330
	protected CharSequence getText( int res )
331
	{
332
		return _doit.getText( res );
333
	}
334
335
	protected boolean isImportRequired( String name )
336
			throws AbortImportException
337
	{
338
		checkAbort();
339
		return isImportRequired( name, _mergeSetting );
340
	}
341
3 by edam
- added "all done" message
342
	synchronized private boolean isImportRequired( String name,
343
			int mergeSetting ) throws AbortImportException
1 by edam
Initial import
344
	{
345
		_lastMergeDecision = mergeSetting;
346
347
		// handle special cases
348
		switch( mergeSetting )
349
		{
9 by edam
- added scroll view to all layouts
350
		case Doit.ACTION_KEEP:
1 by edam
Initial import
351
			// if we keep contacts on duplicate, we better check for one
352
			return !_contacts.containsKey( name );
353
9 by edam
- added scroll view to all layouts
354
		case Doit.ACTION_PROMPT:
1 by edam
Initial import
355
			// if we are prompting on duplicate, we better check for one
356
			if( !_contacts.containsKey( name ) )
357
				return true;
358
359
			// ok, it exists, so do prompt
360
			_doit._handler.sendMessage( Message.obtain(
36 by edam
- formatting: removed some double-indents on overrunning lines
361
				_doit._handler, Doit.MESSAGE_MERGEPROMPT, name ) );
1 by edam
Initial import
362
			try {
363
				wait();
364
			}
365
			catch( InterruptedException e ) { }
366
3 by edam
- added "all done" message
367
			// check if an abortion happened during the wait
368
			checkAbort();
369
1 by edam
Initial import
370
			// if "always" was selected, make choice permenant
371
			if( _responseExtra == RESPONSEEXTRA_ALWAYS )
372
				_mergeSetting = _response;
373
36 by edam
- formatting: removed some double-indents on overrunning lines
374
			// recurse, with our new merge setting
1 by edam
Initial import
375
			return isImportRequired( name, _response );
376
		}
377
378
		// for all other cases (either overwriting or merging) we will need the
379
		// imported data
380
		return true;
381
	}
382
383
	protected void skipContact() throws AbortImportException
384
	{
385
		checkAbort();
3 by edam
- added "all done" message
386
		_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTSKIPPED );
1 by edam
Initial import
387
	}
388
389
	protected void importContact( ContactData contact )
390
			throws AbortImportException
391
	{
392
		checkAbort();
393
3 by edam
- added "all done" message
394
//		if( !showContinue( "====[ IMPORTING ]====\n: " + contact._name ) )
395
//			finish( ACTION_ABORT );
1 by edam
Initial import
396
397
		ContentValues values = new ContentValues();
398
		boolean uiInformed = false;
399
400
		// does contact exist already?
401
		Uri contactUri = null;
402
		Long id;
403
		if( ( id = (Long)_contacts.get( contact._name ) ) != null )
404
		{
405
			// should we skip this import altogether?
9 by edam
- added scroll view to all layouts
406
			if( _lastMergeDecision == Doit.ACTION_KEEP ) return;
1 by edam
Initial import
407
408
			// get contact's URI
409
			contactUri = ContentUris.withAppendedId(
36 by edam
- formatting: removed some double-indents on overrunning lines
410
				Contacts.People.CONTENT_URI, id );
1 by edam
Initial import
411
412
			// should we destroy the existing contact before importing?
9 by edam
- added scroll view to all layouts
413
			if( _lastMergeDecision == Doit.ACTION_OVERWRITE ) {
1 by edam
Initial import
414
				_doit.getContentResolver().delete( contactUri, null, null );
415
				contactUri = null;
416
36 by edam
- formatting: removed some double-indents on overrunning lines
417
				// update the UI
418
				_doit._handler.sendEmptyMessage(
419
						Doit.MESSAGE_CONTACTOVERWRITTEN );
1 by edam
Initial import
420
				uiInformed = true;
421
422
				// update cache
423
				_contacts.remove( contact._name );
424
			}
425
		}
426
427
		// if we don't have a contact URI it is because the contact never
428
		// existed or because we deleted it
429
		if( contactUri == null )
430
		{
431
			// create a new contact
432
			values.put( Contacts.People.NAME, contact._name );
433
			contactUri = _doit.getContentResolver().insert(
36 by edam
- formatting: removed some double-indents on overrunning lines
434
				Contacts.People.CONTENT_URI, values );
1 by edam
Initial import
435
			id = ContentUris.parseId( contactUri );
436
			if( id <= 0 ) return;	// shouldn't happen!
437
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
438
			// try to add them to the "My Contacts" group
439
			try {
440
				Contacts.People.addToMyContactsGroup(
12 by edam
- bugfix: add contacts to the "my contacts" group didn't actually work on a real device. So we're doing it a different way.
441
					_doit.getContentResolver(), id );
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
442
			}
36 by edam
- formatting: removed some double-indents on overrunning lines
443
			catch( IllegalStateException e ) {
444
				// ignore any failure
445
			}
1 by edam
Initial import
446
447
			// update cache
448
			_contacts.put( contact._name, id );
449
450
			// update UI
451
			if( !uiInformed ) {
3 by edam
- added "all done" message
452
				_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTCREATED );
1 by edam
Initial import
453
				uiInformed = true;
454
			}
455
		}
456
457
		// update UI
458
		if( !uiInformed )
3 by edam
- added "all done" message
459
			_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTMERGED );
1 by edam
Initial import
460
461
		// import contact parts
462
		if( contact._phones != null )
463
			importContactPhones( contactUri, contact._phones );
464
		if( contact._emails != null )
465
			importContactEmails( contactUri, contact._emails );
466
	}
467
468
	private void importContactPhones( Uri contactUri,
469
			HashMap< String, ContactData.PhoneData > phones )
470
	{
471
		Long contactId = ContentUris.parseId( contactUri );
472
		Uri contactPhonesUri = Uri.withAppendedPath( contactUri,
473
				Contacts.People.Phones.CONTENT_DIRECTORY );
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
474
		Set< String > phonesKeys = phones.keySet();
7 by edam
- new contact's phone numebrs and email addresses are added to the caches after those contacts are updated to account for the situation where the same contact is imported again from another file (or the contact exists twice in the same file!?)
475
476
		// add phone numbers
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
477
		Iterator< String > i = phonesKeys.iterator();
1 by edam
Initial import
478
		while( i.hasNext() ) {
479
			ContactData.PhoneData phone = phones.get( i.next() );
480
481
			// we don't want to add this number if it's crap, or it already
482
			// exists (which would cause a duplicate to be created). We don't
483
			// take in to account the type when checking for duplicates. This is
484
			// intentional: types aren't really very reliable. We assume that
485
			// if the number exists at all, it doesn't need importing. Because
486
			// of this, we also can't update the cache (which we don't need to
487
			// anyway, so it's not a problem).
488
			String number = sanitisePhoneNumber( phone._number );
489
			if( number == null ) continue;
490
			HashSet< String > numbers = _contactNumbers.get( contactId );
491
			if( numbers != null && numbers.contains( number ) ) continue;
492
493
			// add phone number
494
			ContentValues values = new ContentValues();
495
			values.put( Contacts.Phones.TYPE, phone._type );
496
			values.put( Contacts.Phones.NUMBER, phone._number );
497
			if( phone._isPreferred ) values.put( Contacts.Phones.ISPRIMARY, 1 );
498
			_doit.getContentResolver().insert( contactPhonesUri, values );
499
		}
7 by edam
- new contact's phone numebrs and email addresses are added to the caches after those contacts are updated to account for the situation where the same contact is imported again from another file (or the contact exists twice in the same file!?)
500
501
		// now add those phone numbers to the cache to prevent the addition of
502
		// duplicate data from another file
503
		i = phonesKeys.iterator();
504
		while( i.hasNext() ) {
505
			ContactData.PhoneData phone = phones.get( i.next() );
506
507
			String number = sanitisePhoneNumber( phone._number );
508
			if( number != null ) {
509
				HashSet< String > numbers = _contactNumbers.get( contactId );
510
				if( numbers == null ) {
511
					_contactNumbers.put( contactId, new HashSet< String >() );
512
					numbers = _contactNumbers.get( contactId );
513
				}
514
				numbers.add( number );
515
			}
516
		}
1 by edam
Initial import
517
	}
518
519
	private void importContactEmails( Uri contactUri,
520
			HashMap< String, ContactData.EmailData > emails )
521
	{
522
		Long contactId = ContentUris.parseId( contactUri );
523
		Uri contactContactMethodsUri = Uri.withAppendedPath( contactUri,
524
				Contacts.People.ContactMethods.CONTENT_DIRECTORY );
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
525
		Set< String > emailsKeys = emails.keySet();
7 by edam
- new contact's phone numebrs and email addresses are added to the caches after those contacts are updated to account for the situation where the same contact is imported again from another file (or the contact exists twice in the same file!?)
526
527
		// add email addresses
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
528
		Iterator< String > i = emailsKeys.iterator();
1 by edam
Initial import
529
		while( i.hasNext() ) {
530
			ContactData.EmailData email = emails.get( i.next() );
531
532
			// like with phone numbers, we don't want to add this email address
533
			// if it exists already or we would introduce duplicates.
534
			String address = sanitiseEmailAddress( email.getAddress() );
535
			if( address == null ) continue;
536
			HashSet< String > addresses = _contactEmails.get( contactId );
537
			if( addresses != null && addresses.contains( address ) ) continue;
538
539
			// add phone number
540
			ContentValues values = new ContentValues();
541
			values.put( Contacts.ContactMethods.KIND, Contacts.KIND_EMAIL );
542
			values.put( Contacts.ContactMethods.DATA, email.getAddress() );
543
			values.put( Contacts.ContactMethods.TYPE, email.getType() );
544
			if( email.isPreferred() )
545
				values.put( Contacts.ContactMethods.ISPRIMARY, 1 );
546
			_doit.getContentResolver().insert( contactContactMethodsUri,
36 by edam
- formatting: removed some double-indents on overrunning lines
547
				values );
1 by edam
Initial import
548
		}
7 by edam
- new contact's phone numebrs and email addresses are added to the caches after those contacts are updated to account for the situation where the same contact is imported again from another file (or the contact exists twice in the same file!?)
549
550
		// now add those email addresses to the cache to prevent the addition of
551
		// duplicate data from another file
552
		i = emailsKeys.iterator();
553
		while( i.hasNext() ) {
554
			ContactData.EmailData email = emails.get( i.next() );
555
556
			String address = sanitiseEmailAddress( email.getAddress() );
557
			if( address != null ) {
558
				HashSet< String > addresses = _contactEmails.get( contactId );
559
				if( addresses == null ) {
560
					_contactEmails.put( contactId, new HashSet< String >() );
561
					addresses = _contactEmails.get( contactId );
562
				}
563
				addresses.add( address );
564
			}
565
		}
1 by edam
Initial import
566
	}
567
3 by edam
- added "all done" message
568
	synchronized protected void checkAbort() throws AbortImportException
1 by edam
Initial import
569
	{
570
		if( _abort ) {
571
			// stop
572
			throw new AbortImportException();
573
		}
574
	}
575
576
	private void buildContactsCache() throws AbortImportException
577
	{
578
		// update UI
579
		setProgressMessage( R.string.doit_caching );
580
581
		String[] cols;
582
		Cursor cur;
583
584
		// init contacts caches
585
		_contacts = new HashMap< String, Long >();
586
		_contactNumbers = new HashMap< Long, HashSet< String > >();
587
		_contactEmails = new HashMap< Long, HashSet< String > >();
588
589
		// query and store map of contact names to ids
590
		cols = new String[] { Contacts.People._ID, Contacts.People.NAME };
591
		cur = _doit.managedQuery( Contacts.People.CONTENT_URI,
36 by edam
- formatting: removed some double-indents on overrunning lines
592
			cols, null, null, null);
1 by edam
Initial import
593
		if( cur.moveToFirst() ) {
594
			int idCol = cur.getColumnIndex( Contacts.People._ID );
595
			int nameCol = cur.getColumnIndex( Contacts.People.NAME );
596
			do {
597
				_contacts.put( cur.getString( nameCol ), cur.getLong( idCol ) );
598
			} while( cur.moveToNext() );
599
		}
600
601
		// query and store map of contact ids to sets of phone numbers
602
		cols = new String[] { Contacts.Phones.PERSON_ID,
603
				Contacts.Phones.NUMBER };
604
		cur = _doit.managedQuery( Contacts.Phones.CONTENT_URI,
36 by edam
- formatting: removed some double-indents on overrunning lines
605
			cols, null, null, null);
1 by edam
Initial import
606
		if( cur.moveToFirst() ) {
607
			int personIdCol = cur.getColumnIndex( Contacts.Phones.PERSON_ID );
608
			int numberCol = cur.getColumnIndex( Contacts.Phones.NUMBER );
609
			do {
610
				Long id = cur.getLong( personIdCol );
611
				String number = sanitisePhoneNumber(
612
						cur.getString( numberCol ) );
613
				if( number != null ) {
614
					HashSet< String > numbers = _contactNumbers.get( id );
615
					if( numbers == null ) {
616
						_contactNumbers.put( id, new HashSet< String >() );
617
						numbers = _contactNumbers.get( id );
618
					}
619
					numbers.add( number );
620
				}
621
			} while( cur.moveToNext() );
622
		}
623
624
		// query and store map of contact ids to sets of email addresses
625
		cols = new String[] { Contacts.ContactMethods.PERSON_ID,
626
				Contacts.ContactMethods.DATA };
627
		cur = _doit.managedQuery( Contacts.ContactMethods.CONTENT_URI,
628
				cols, Contacts.ContactMethods.KIND + " = ?",
629
				new String[] { "" + Contacts.KIND_EMAIL }, null );
630
		if( cur.moveToFirst() ) {
631
			int personIdCol = cur.getColumnIndex(
36 by edam
- formatting: removed some double-indents on overrunning lines
632
				Contacts.ContactMethods.PERSON_ID );
1 by edam
Initial import
633
			int addressCol = cur.getColumnIndex(
36 by edam
- formatting: removed some double-indents on overrunning lines
634
				Contacts.ContactMethods.DATA );
1 by edam
Initial import
635
			do {
636
				Long id = cur.getLong( personIdCol );
637
				String address = sanitiseEmailAddress(
36 by edam
- formatting: removed some double-indents on overrunning lines
638
					cur.getString( addressCol ) );
1 by edam
Initial import
639
				if( address != null ) {
640
					HashSet< String > addresses = _contactEmails.get( id );
641
					if( addresses == null ) {
642
						_contactEmails.put( id, new HashSet< String >() );
643
						addresses = _contactEmails.get( id );
644
					}
645
					addresses.add( address );
646
				}
647
			} while( cur.moveToNext() );
648
		}
649
	}
650
651
	private String sanitisePhoneNumber( String number )
652
	{
653
		number = number.replaceAll( "[-\\(\\) ]", "" );
24 by edam
- import phone numbers even when they have no specified type (default to mobile)
654
		Pattern p = Pattern.compile( "^[\\+0-9#*]+" );
1 by edam
Initial import
655
		Matcher m = p.matcher( number );
656
		if( m.lookingAt() ) return m.group( 0 );
657
		return null;
658
	}
659
660
	private String sanitiseEmailAddress( String address )
661
	{
662
		address = address.trim();
663
		Pattern p = Pattern.compile(
36 by edam
- formatting: removed some double-indents on overrunning lines
664
			"^[^ @]+@[a-zA-Z]([-a-zA-Z0-9]*[a-zA-z0-9])?(\\.[a-zA-Z]([-a-zA-Z0-9]*[a-zA-z0-9])?)+$" );
1 by edam
Initial import
665
		Matcher m = p.matcher( address );
666
		if( m.matches() ) {
667
			String[] bits = address.split( "@" );
668
			return bits[ 0 ] + "@" + bits[ 1 ].toLowerCase();
669
		}
670
		return null;
671
	}
672
}