/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
 *
95 by Tim Marston
added suopport for birthdays
4
 * Copyright (C) 2009 to 2013 Tim Marston <tim@ed.am>
6 by edam
- added GPL header comments to all files
5
 *
6
 * This file is part of the Import Contacts program (hereafter referred
93 by Tim Marston
minor style tweaks
7
 * to as "this program").  For more information, see
50 by edam
updated all URLs, email addresses and package names to ed.am
8
 * http://ed.am/dev/android/import-contacts
6 by edam
- added GPL header comments to all files
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
50 by edam
updated all URLs, email addresses and package names to ed.am
24
package am.ed.importcontacts;
1 by edam
Initial import
25
46 by edam
- properly handle multiple TYPE= params in one entry in a v3.0 vCard
26
import java.util.Arrays;
1 by edam
Initial import
27
import java.util.HashMap;
46 by edam
- properly handle multiple TYPE= params in one entry in a v3.0 vCard
28
import java.util.HashSet;
1 by edam
Initial import
29
import java.util.Iterator;
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
30
import java.util.Locale;
1 by edam
Initial import
31
import java.util.Set;
32
import java.util.regex.Matcher;
33
import java.util.regex.Pattern;
34
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
35
import am.ed.importcontacts.Backend.ContactCreationException;
1 by edam
Initial import
36
import android.content.SharedPreferences;
37
import android.os.Message;
38
39
public class Importer extends Thread
40
{
3 by edam
- added "all done" message
41
	public final static int ACTION_ABORT = 1;
42
	public final static int ACTION_ALLDONE = 2;
1 by edam
Initial import
43
44
	public final static int RESPONSE_NEGATIVE = 0;
45
	public final static int RESPONSE_POSITIVE = 1;
46
47
	public final static int RESPONSEEXTRA_NONE = 0;
48
	public final static int RESPONSEEXTRA_ALWAYS = 1;
49
50
	private Doit _doit;
51
	private int _response;
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
52
	private int _response_extra;
53
	private int _merge_setting;
54
	private int _last_merge_decision;
1 by edam
Initial import
55
	private boolean _abort = false;
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
56
	private boolean _is_finished = false;
41 by edam
- updated TODO
57
	private ContactsCache _contacts_cache = null;
53 by edam
abstracted the android contacts API in to an interface, ready to be switched to
58
	private Backend _backend = null;
1 by edam
Initial import
59
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
60
	/**
61
	 * Data about a contact
62
	 */
1 by edam
Initial import
63
	public class ContactData
64
	{
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
65
		public final static int TYPE_HOME = 0;
66
		public final static int TYPE_WORK = 1;
67
		public final static int TYPE_MOBILE = 2;	// only used with phones
68
		public final static int TYPE_FAX_HOME = 3;	// only used with phones
69
		public final static int TYPE_FAX_WORK = 4;	// only used with phones
70
		public final static int TYPE_PAGER = 5;		// only used with phones
71
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
72
		class TypeDetail
73
		{
74
			protected int _type;
75
76
			public TypeDetail( int type )
77
			{
78
				_type = type;
79
			}
80
81
			public int getType()
82
			{
83
				return _type;
84
			}
85
		}
86
87
		class PreferredDetail extends TypeDetail
88
		{
89
			protected boolean _is_preferred;
90
91
			public PreferredDetail( int type, boolean is_preferred )
92
			{
93
				super( type );
94
				_is_preferred = is_preferred;
95
			}
96
97
			public boolean isPreferred()
98
			{
99
				return _is_preferred;
100
			}
101
		}
102
103
		class ExtraDetail extends PreferredDetail
104
		{
105
			protected String _extra;
106
107
			public ExtraDetail( int type, boolean is_preferred, String extra )
108
			{
109
				super( type, is_preferred );
110
111
				if( extra != null ) extra = extra.trim();
112
				_extra = extra;
113
			}
114
115
			public String getExtra()
116
			{
117
				return _extra;
118
			}
119
120
			public void setExtra( String extra )
121
			{
122
				if( extra != null ) extra = extra.trim();
123
				_extra = extra;
124
			}
125
		}
126
43 by edam
- refactored some code to do with how contacts are imported
127
		@SuppressWarnings("serial")
128
		protected class ContactNotIdentifiableException extends Exception
129
		{
130
		}
131
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
132
		protected String _name = null;
133
		protected String _primary_organisation = null;
46 by edam
- properly handle multiple TYPE= params in one entry in a v3.0 vCard
134
		protected boolean _primary_organisation_is_preferred;
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
135
		protected String _primary_number = null;
46 by edam
- properly handle multiple TYPE= params in one entry in a v3.0 vCard
136
		protected int _primary_number_type;
137
		protected boolean _primary_number_is_preferred;
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
138
		protected String _primary_email = null;
46 by edam
- properly handle multiple TYPE= params in one entry in a v3.0 vCard
139
		protected boolean _primary_email_is_preferred;
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
140
		protected HashMap< String, ExtraDetail > _organisations = null;
141
		protected HashMap< String, PreferredDetail > _numbers = null;
142
		protected HashMap< String, PreferredDetail > _emails = null;
143
		protected HashMap< String, TypeDetail > _addresses = null;
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
144
		protected HashSet< String > _notes = null;
95 by Tim Marston
added suopport for birthdays
145
		protected String _birthday = null;
1 by edam
Initial import
146
43 by edam
- refactored some code to do with how contacts are imported
147
		private ContactsCache.CacheIdentifier _cache_identifier = null;
148
1 by edam
Initial import
149
		protected void setName( String name )
150
		{
151
			_name = name;
152
		}
153
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
154
		public boolean hasName()
155
		{
156
			return _name != null;
157
		}
158
1 by edam
Initial import
159
		public String getName()
160
		{
161
			return _name;
162
		}
163
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
164
		protected void addOrganisation( String organisation, String title,
165
			boolean is_preferred )
166
		{
167
			organisation = organisation.trim();
168
			if( organisation.length() <= 0 )
169
			{
170
				// TODO: warn that an imported organisation is being ignored
171
				return;
172
			}
173
174
			if( title != null ) {
175
				title = title.trim();
176
				if( title.length() <= 0 ) title = null;
177
			}
178
179
			// add the organisation, as non-preferred (we prefer only one
180
			// organisation in finalise() after they're all imported)
181
			if( _organisations == null )
182
				_organisations = new HashMap< String, ExtraDetail >();
183
			if( !_organisations.containsKey( organisation ) )
184
				_organisations.put( organisation,
185
					new ExtraDetail( 0, false, title ) );
186
187
			// if this is the first organisation added, or it's a preferred
46 by edam
- properly handle multiple TYPE= params in one entry in a v3.0 vCard
188
			// organisation and the current primary organisation isn't, then
93 by Tim Marston
minor style tweaks
189
			// record this as the primary organisation
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
190
			if( _primary_organisation == null ||
191
				( is_preferred && !_primary_organisation_is_preferred ) )
192
			{
193
				_primary_organisation = organisation;
194
				_primary_organisation_is_preferred = is_preferred;
195
			}
196
		}
197
198
		public boolean hasOrganisations()
199
		{
200
			return _organisations != null && _organisations.size() > 0;
201
		}
202
203
		public HashMap< String, ExtraDetail > getOrganisations()
204
		{
205
			return _organisations;
206
		}
207
208
		public boolean hasPrimaryOrganisation()
209
		{
210
			return _primary_organisation != null;
211
		}
212
213
		public String getPrimaryOrganisation()
214
		{
215
			return _primary_organisation;
216
		}
217
218
		protected void addNumber( String number, int type,
219
			boolean is_preferred )
220
		{
221
			number = sanitisePhoneNumber( number );
222
			if( number == null )
223
			{
224
				// TODO: warn that an imported phone number is being ignored
225
				return;
226
			}
227
228
			// add the number, as non-preferred (we prefer only one number
229
			// in finalise() after they're all imported)
230
			if( _numbers == null )
231
				_numbers = new HashMap< String, PreferredDetail >();
232
			if( !_numbers.containsKey( number ) )
233
				_numbers.put( number,
234
					new PreferredDetail( type, false ) );
235
46 by edam
- properly handle multiple TYPE= params in one entry in a v3.0 vCard
236
			final Set< Integer > non_voice_types = new HashSet< Integer >(
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
237
				Arrays.asList( TYPE_FAX_HOME, TYPE_FAX_WORK, TYPE_PAGER ) );
46 by edam
- properly handle multiple TYPE= params in one entry in a v3.0 vCard
238
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
239
			// if this is the first number added, or it's a preferred number
46 by edam
- properly handle multiple TYPE= params in one entry in a v3.0 vCard
240
			// and the current primary number isn't, or this number is on equal
241
			// standing with the primary number in terms of preference and it is
242
			// a voice number and the primary number isn't, then record this as
93 by Tim Marston
minor style tweaks
243
			// the primary number
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
244
			if( _primary_number == null ||
46 by edam
- properly handle multiple TYPE= params in one entry in a v3.0 vCard
245
				( is_preferred && !_primary_number_is_preferred ) ||
246
				( is_preferred == _primary_number_is_preferred &&
247
					!non_voice_types.contains( type ) &&
248
					non_voice_types.contains( _primary_number_type ) ) )
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
249
			{
250
				_primary_number = number;
46 by edam
- properly handle multiple TYPE= params in one entry in a v3.0 vCard
251
				_primary_number_type = type;
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
252
				_primary_number_is_preferred = is_preferred;
253
			}
254
		}
255
256
		public boolean hasNumbers()
257
		{
258
			return _numbers != null && _numbers.size() > 0;
259
		}
260
261
		public HashMap< String, PreferredDetail > getNumbers()
262
		{
263
			return _numbers;
264
		}
265
266
		public boolean hasPrimaryNumber()
267
		{
268
			return _primary_number != null;
269
		}
270
271
		public String getPrimaryNumber()
272
		{
273
			return _primary_number;
274
		}
275
276
		protected void addEmail( String email, int type, boolean is_preferred )
277
		{
278
279
			email = sanitisesEmailAddress( email );
280
			if( email == null )
281
			{
57 by edam
cleanup; fixed some typos; updated TODO
282
				// TODO: warn that an imported email address is being ignored
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
283
				return;
284
			}
285
286
			// add the email, as non-preferred (we prefer only one email in
287
			// finalise() after they're all imported)
288
			if( _emails == null )
289
				_emails = new HashMap< String, PreferredDetail >();
1 by edam
Initial import
290
			if( !_emails.containsKey( email ) )
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
291
				_emails.put( email, new PreferredDetail( type, false ) );
292
46 by edam
- properly handle multiple TYPE= params in one entry in a v3.0 vCard
293
			// if this is the first email added, or it's a preferred email and
294
			// the current primary organisation isn't, then record this as the
93 by Tim Marston
minor style tweaks
295
			// primary email
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
296
			if( _primary_email == null ||
297
				( is_preferred && !_primary_email_is_preferred ) )
298
			{
299
				_primary_email = email;
300
				_primary_email_is_preferred = is_preferred;
301
			}
302
		}
303
304
		public boolean hasEmails()
305
		{
306
			return _emails != null && _emails.size() > 0;
307
		}
308
309
		public HashMap< String, PreferredDetail > getEmails()
310
		{
311
			return _emails;
312
		}
313
314
		public boolean hasPrimaryEmail()
315
		{
316
			return _primary_email != null;
317
		}
318
319
		public String getPrimaryEmail()
320
		{
321
			return _primary_email;
1 by edam
Initial import
322
		}
37 by edam
- updated TODO and NEWS
323
324
		protected void addAddress( String address, int type )
325
		{
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
326
			address = address.trim();
327
			if( address.length() <= 0 )
328
			{
329
				// TODO: warn that an imported address is being ignored
330
				return;
331
			}
332
37 by edam
- updated TODO and NEWS
333
			if( _addresses == null ) _addresses =
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
334
				new HashMap< String, TypeDetail >();
37 by edam
- updated TODO and NEWS
335
			if( !_addresses.containsKey( address ) )
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
336
				_addresses.put( address, new TypeDetail( type ) );
337
		}
338
339
		public boolean hasAddresses()
340
		{
341
			return _addresses != null && _addresses.size() > 0;
342
		}
343
344
		public HashMap< String, TypeDetail > getAddresses()
345
		{
346
			return _addresses;
347
		}
348
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
349
		protected void addNote( String note )
350
		{
351
			if( _notes == null ) _notes = new HashSet< String >();
352
			if( !_notes.contains( note ) )
353
				_notes.add( note );
354
		}
355
356
		public boolean hasNotes()
357
		{
358
			return _notes != null && _notes.size() > 0;
359
		}
360
361
		public HashSet< String > getNotes()
362
		{
363
			return _notes;
364
		}
365
95 by Tim Marston
added suopport for birthdays
366
		public void setBirthday( String birthday )
367
		{
368
			_birthday = birthday;
369
		}
370
371
		public boolean hasBirthday()
372
		{
373
			return _birthday != null;
374
		}
375
376
		public String getBirthday()
377
		{
378
			return _birthday;
379
		}
380
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
381
		protected void finalise()
43 by edam
- refactored some code to do with how contacts are imported
382
			throws ContactNotIdentifiableException
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
383
		{
93 by Tim Marston
minor style tweaks
384
			// Ensure that if there is a primary number, it is preferred so
385
			// that there is always one preferred number.  Android will assign
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
386
			// preference to one anyway so we might as well decide one sensibly.
387
			if( _primary_number != null ) {
388
				PreferredDetail data = _numbers.get( _primary_number );
389
				_numbers.put( _primary_number,
390
					new PreferredDetail( data.getType(), true ) );
391
			}
392
393
			// do the same for the primary email
394
			if( _primary_email != null ) {
395
				PreferredDetail data = _emails.get( _primary_email );
396
				_emails.put( _primary_email,
397
					new PreferredDetail( data.getType(), true ) );
398
			}
399
400
			// do the same for the primary organisation
401
			if( _primary_organisation != null ) {
402
				ExtraDetail data = _organisations.get( _primary_organisation );
403
				_organisations.put( _primary_organisation,
404
					new ExtraDetail( 0, true, data.getExtra() ) );
405
			}
43 by edam
- refactored some code to do with how contacts are imported
406
407
			// create a cache identifier from this contact data, which can be
408
			// used to look-up an existing contact
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
409
			_cache_identifier = ContactsCache.CacheIdentifier.factory( this );
43 by edam
- refactored some code to do with how contacts are imported
410
			if( _cache_identifier == null )
411
				throw new ContactNotIdentifiableException();
412
		}
413
414
		public ContactsCache.CacheIdentifier getCacheIdentifier()
415
		{
416
			return _cache_identifier;
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
417
		}
418
419
		private String sanitisePhoneNumber( String number )
420
		{
421
			number = number.trim();
422
			Pattern p = Pattern.compile( "^[-\\(\\) \\+0-9#*]+" );
423
			Matcher m = p.matcher( number );
424
			if( m.lookingAt() ) return m.group( 0 );
425
			return null;
426
		}
427
428
		private String sanitisesEmailAddress( String email )
429
		{
430
			email = email.trim();
431
			Pattern p = Pattern.compile(
432
				"^[^ @]+@[a-zA-Z]([-a-zA-Z0-9]*[a-zA-z0-9])?(\\.[a-zA-Z]([-a-zA-Z0-9]*[a-zA-z0-9])?)+$" );
433
			Matcher m = p.matcher( email );
434
			if( m.matches() ) {
435
				String[] bits = email.split( "@" );
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
436
				return bits[ 0 ] + "@" + bits[ 1 ].toLowerCase( Locale.US );
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
437
			}
438
			return null;
37 by edam
- updated TODO and NEWS
439
		}
1 by edam
Initial import
440
	}
441
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
442
	@SuppressWarnings("serial")
1 by edam
Initial import
443
	protected class AbortImportException extends Exception { };
444
445
	public Importer( Doit doit )
446
	{
447
		_doit = doit;
448
449
		SharedPreferences prefs = getSharedPreferences();
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
450
		_merge_setting = prefs.getInt( "merge_setting", Doit.ACTION_PROMPT );
1 by edam
Initial import
451
	}
452
453
	@Override
454
	public void run()
455
	{
456
		try
457
		{
39 by edam
- pulled contacts cache out in to seperate class
458
			// update UI
459
			setProgressMessage( R.string.doit_caching );
460
57 by edam
cleanup; fixed some typos; updated TODO
461
			// create the appropriate backend
62 by edam
added preliminary (buggy) ContactsContract backend
462
			if( Integer.parseInt( android.os.Build.VERSION.SDK ) >= 5 )
463
				_backend = new ContactsContractBackend( _doit );
464
			else
53 by edam
abstracted the android contacts API in to an interface, ready to be switched to
465
				_backend = new ContactsBackend( _doit );
466
467
			// create a cache of existing contacts and populate it
41 by edam
- updated TODO
468
			_contacts_cache = new ContactsCache();
53 by edam
abstracted the android contacts API in to an interface, ready to be switched to
469
			_backend.populateCache( _contacts_cache );
1 by edam
Initial import
470
471
			// do the import
472
			onImport();
473
474
			// done!
3 by edam
- added "all done" message
475
			finish( ACTION_ALLDONE );
1 by edam
Initial import
476
		}
477
		catch( AbortImportException e )
478
		{}
3 by edam
- added "all done" message
479
480
		// flag as finished to prevent interrupts
481
		setIsFinished();
482
	}
483
484
	synchronized private void setIsFinished()
485
	{
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
486
		_is_finished = true;
1 by edam
Initial import
487
	}
488
489
	protected void onImport() throws AbortImportException
490
	{
491
	}
492
493
	public void wake()
494
	{
495
		wake( 0, RESPONSEEXTRA_NONE );
496
	}
497
498
	public void wake( int response )
499
	{
500
		wake( response, RESPONSEEXTRA_NONE );
501
	}
502
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
503
	synchronized public void wake( int response, int response_extra )
1 by edam
Initial import
504
	{
505
		_response = response;
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
506
		_response_extra = response_extra;
1 by edam
Initial import
507
		notify();
508
	}
509
3 by edam
- added "all done" message
510
	synchronized public boolean setAbort()
1 by edam
Initial import
511
	{
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
512
		if( !_is_finished && !_abort ) {
3 by edam
- added "all done" message
513
			_abort = true;
514
			notify();
515
			return true;
516
		}
517
		return false;
1 by edam
Initial import
518
	}
519
520
	protected SharedPreferences getSharedPreferences()
521
	{
522
		return _doit.getSharedPreferences();
523
	}
524
525
	protected void showError( int res ) throws AbortImportException
526
	{
527
		showError( _doit.getText( res ).toString() );
528
	}
529
530
	synchronized protected void showError( String message )
531
			throws AbortImportException
532
	{
533
		checkAbort();
534
		_doit._handler.sendMessage( Message.obtain(
36 by edam
- formatting: removed some double-indents on overrunning lines
535
			_doit._handler, Doit.MESSAGE_ERROR, message ) );
1 by edam
Initial import
536
		try {
537
			wait();
538
		}
539
		catch( InterruptedException e ) { }
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
540
3 by edam
- added "all done" message
541
		// no need to check if an abortion happened during the wait, we are
542
		// about to finish anyway!
543
		finish( ACTION_ABORT );
1 by edam
Initial import
544
	}
545
546
	protected void showFatalError( int res ) throws AbortImportException
547
	{
548
		showFatalError( _doit.getText( res ).toString() );
549
	}
550
551
	synchronized protected void showFatalError( String message )
552
			throws AbortImportException
553
	{
554
		checkAbort();
555
		_doit._handler.sendMessage( Message.obtain(
36 by edam
- formatting: removed some double-indents on overrunning lines
556
			_doit._handler, Doit.MESSAGE_ERROR, message ) );
1 by edam
Initial import
557
		try {
558
			wait();
559
		}
560
		catch( InterruptedException e ) { }
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
561
3 by edam
- added "all done" message
562
		// no need to check if an abortion happened during the wait, we are
563
		// about to finish anyway!
564
		finish( ACTION_ABORT );
1 by edam
Initial import
565
	}
566
567
	protected boolean showContinue( int res ) throws AbortImportException
568
	{
569
		return showContinue( _doit.getText( res ).toString() );
570
	}
571
572
	synchronized protected boolean showContinue( String message )
573
			throws AbortImportException
574
	{
575
		checkAbort();
576
		_doit._handler.sendMessage( Message.obtain(
36 by edam
- formatting: removed some double-indents on overrunning lines
577
			_doit._handler, Doit.MESSAGE_CONTINUEORABORT, message ) );
1 by edam
Initial import
578
		try {
579
			wait();
580
		}
581
		catch( InterruptedException e ) { }
3 by edam
- added "all done" message
582
583
		// check if an abortion happened during the wait
584
		checkAbort();
585
1 by edam
Initial import
586
		return _response == RESPONSE_POSITIVE;
587
	}
588
589
	protected void setProgressMessage( int res ) throws AbortImportException
590
	{
591
		checkAbort();
592
		_doit._handler.sendMessage( Message.obtain( _doit._handler,
36 by edam
- formatting: removed some double-indents on overrunning lines
593
			Doit.MESSAGE_SETPROGRESSMESSAGE, getText( res ) ) );
1 by edam
Initial import
594
	}
595
41 by edam
- updated TODO
596
	protected void setProgressMax( int max_progress )
1 by edam
Initial import
597
			throws AbortImportException
598
	{
599
		checkAbort();
600
		_doit._handler.sendMessage( Message.obtain(
36 by edam
- formatting: removed some double-indents on overrunning lines
601
			_doit._handler, Doit.MESSAGE_SETMAXPROGRESS,
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
602
			Integer.valueOf( max_progress ) ) );
1 by edam
Initial import
603
	}
604
41 by edam
- updated TODO
605
	protected void setTmpProgress( int tmp_progress )
606
		throws AbortImportException
1 by edam
Initial import
607
	{
608
		checkAbort();
609
		_doit._handler.sendMessage( Message.obtain(
36 by edam
- formatting: removed some double-indents on overrunning lines
610
			_doit._handler, Doit.MESSAGE_SETTMPPROGRESS,
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
611
			Integer.valueOf( tmp_progress ) ) );
1 by edam
Initial import
612
	}
613
614
	protected void setProgress( int progress ) throws AbortImportException
615
	{
616
		checkAbort();
617
		_doit._handler.sendMessage( Message.obtain(
36 by edam
- formatting: removed some double-indents on overrunning lines
618
			_doit._handler, Doit.MESSAGE_SETPROGRESS,
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
619
			Integer.valueOf( progress ) ) );
1 by edam
Initial import
620
	}
621
3 by edam
- added "all done" message
622
	protected void finish( int action ) throws AbortImportException
1 by edam
Initial import
623
	{
3 by edam
- added "all done" message
624
		// update UI to reflect action
625
		int message;
626
		switch( action )
627
		{
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
628
		case ACTION_ALLDONE:	message = Doit.MESSAGE_ALLDONE; break;
3 by edam
- added "all done" message
629
		default:	// fall through
14 by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one)
630
		case ACTION_ABORT:		message = Doit.MESSAGE_ABORT; break;
3 by edam
- added "all done" message
631
		}
632
		_doit._handler.sendEmptyMessage( message );
1 by edam
Initial import
633
3 by edam
- added "all done" message
634
		// stop
635
		throw new AbortImportException();
1 by edam
Initial import
636
	}
637
638
	protected CharSequence getText( int res )
639
	{
640
		return _doit.getText( res );
641
	}
642
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
643
	/**
644
	 * Should we skip a contact, given whether it exists or not and the current
645
	 * merge setting?  This routine handles throwing up a prompt, if required.
93 by Tim Marston
minor style tweaks
646
	 *
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
647
	 * @param contact_detail the display name of the contact
648
	 * @param exists true if this contact matches one in the cache
649
	 * @param merge_setting the merge setting to use
650
	 * @return true if the contact should be skipped outright
651
	 * @throws AbortImportException
652
	 */
653
	synchronized private boolean shouldWeSkipContact( String contact_detail,
654
		boolean exists, int merge_setting ) throws AbortImportException
1 by edam
Initial import
655
	{
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
656
		_last_merge_decision = merge_setting;
657
1 by edam
Initial import
658
		// handle special cases
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
659
		switch( merge_setting )
1 by edam
Initial import
660
		{
9 by edam
- added scroll view to all layouts
661
		case Doit.ACTION_KEEP:
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
662
			// if we are skipping on a duplicate, check for one
663
			return exists;
1 by edam
Initial import
664
9 by edam
- added scroll view to all layouts
665
		case Doit.ACTION_PROMPT:
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
666
			// if we are prompting on duplicate, then we can say that we won't
667
			// skip if there isn't one
668
			if( !exists ) return false;
1 by edam
Initial import
669
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
670
			// ok, duplicate exists, so do prompt
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
671
			_doit._handler.sendMessage( Message.obtain( _doit._handler,
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
672
				Doit.MESSAGE_MERGEPROMPT, contact_detail ) );
1 by edam
Initial import
673
			try {
674
				wait();
675
			}
676
			catch( InterruptedException e ) { }
677
3 by edam
- added "all done" message
678
			// check if an abortion happened during the wait
679
			checkAbort();
680
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
681
			// if "always" was selected, make choice permanent
682
			if( _response_extra == RESPONSEEXTRA_ALWAYS )
683
				_merge_setting = _response;
1 by edam
Initial import
684
36 by edam
- formatting: removed some double-indents on overrunning lines
685
			// recurse, with our new merge setting
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
686
			return shouldWeSkipContact( contact_detail, exists, _response );
1 by edam
Initial import
687
		}
688
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
689
		// for all other cases (either overwriting or merging) we don't skip
690
		return false;
1 by edam
Initial import
691
	}
692
693
	protected void skipContact() throws AbortImportException
694
	{
695
		checkAbort();
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
696
697
		// show that we're skipping a new contact
3 by edam
- added "all done" message
698
		_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTSKIPPED );
1 by edam
Initial import
699
	}
700
701
	protected void importContact( ContactData contact )
702
			throws AbortImportException
703
	{
704
		checkAbort();
705
93 by Tim Marston
minor style tweaks
706
		// It is expected that we use contact.getCacheIdentifier() here.  The
43 by edam
- refactored some code to do with how contacts are imported
707
		// contact we are passed should have been successfully finalise()d,
708
		// which includes generating a valid cache identifier.
709
		ContactsCache.CacheIdentifier cache_identifier =
710
			contact.getCacheIdentifier();
711
3 by edam
- added "all done" message
712
//		if( !showContinue( "====[ IMPORTING ]====\n: " + contact._name ) )
713
//			finish( ACTION_ABORT );
1 by edam
Initial import
714
43 by edam
- refactored some code to do with how contacts are imported
715
		// attempt to lookup the id of an existing contact in the cache with
716
		// this contact data's cache identifier
717
		Long id = (Long)_contacts_cache.lookup( cache_identifier );
1 by edam
Initial import
718
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
719
		// check to see if this contact should be skipped
720
		if( shouldWeSkipContact( cache_identifier.getDetail(), id != null,
721
			_merge_setting ) )
722
		{
723
			// show that we're skipping a contact
724
			_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTSKIPPED );
725
			return;
726
		}
727
728
		// if a contact exists, and we're overwriting, destroy the existing
729
		// contact before importing
730
		boolean contact_deleted = false;
731
		if( id != null && _last_merge_decision == Doit.ACTION_OVERWRITE )
732
		{
733
			contact_deleted = true;
734
735
			// remove from device
736
			_backend.deleteContact( id );
737
738
			// update cache
739
			_contacts_cache.removeLookup( cache_identifier );
740
			_contacts_cache.removeAssociatedData( id );
741
742
			// show that we're overwriting a contact
743
			_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTOVERWRITTEN );
744
745
			// discard the contact id
746
			id = null;
747
		}
748
749
		try {
750
			// if we don't have a contact id yet (or we did, but we destroyed it
751
			// when we deleted the contact), we'll have to create a new contact
752
			if( id == null )
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
753
			{
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
754
				// create a new contact
755
				id = _backend.addContact( contact._name );
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
756
757
				// update cache
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
758
				_contacts_cache.addLookup( cache_identifier, id );
759
760
				// if we haven't already shown that we're overwriting a contact,
761
				// show that we're creating a new contact
762
				if( !contact_deleted )
763
					_doit._handler.sendEmptyMessage(
764
						Doit.MESSAGE_CONTACTCREATED );
1 by edam
Initial import
765
			}
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
766
			else
767
				// show that we're merging with an existing contact
768
				_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTMERGED );
769
770
			// import contact parts
771
			if( contact.hasNumbers() )
772
				importContactPhones( id, contact.getNumbers() );
773
			if( contact.hasEmails() )
774
				importContactEmails( id, contact.getEmails() );
775
			if( contact.hasAddresses() )
776
				importContactAddresses( id, contact.getAddresses() );
777
			if( contact.hasOrganisations() )
778
				importContactOrganisations( id, contact.getOrganisations() );
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
779
			if( contact.hasNotes() )
780
				importContactNotes( id, contact.getNotes() );
95 by Tim Marston
added suopport for birthdays
781
			if( contact.hasBirthday() )
782
				importContactBirthday( id, contact.getBirthday() );
1 by edam
Initial import
783
		}
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
784
		catch( Backend.ContactCreationException e )
1 by edam
Initial import
785
		{
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
786
			showError( R.string.error_unabletoaddcontact );
1 by edam
Initial import
787
		}
788
	}
789
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
790
	private void importContactPhones( Long id,
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
791
		HashMap< String, ContactData.PreferredDetail > datas )
792
		throws ContactCreationException
1 by edam
Initial import
793
	{
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!?)
794
		// add phone numbers
53 by edam
abstracted the android contacts API in to an interface, ready to be switched to
795
		Set< String > datas_keys = datas.keySet();
41 by edam
- updated TODO
796
		Iterator< String > i = datas_keys.iterator();
1 by edam
Initial import
797
		while( i.hasNext() ) {
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
798
			String number = i.next();
799
			ContactData.PreferredDetail data = datas.get( number );
1 by edam
Initial import
800
93 by Tim Marston
minor style tweaks
801
			// We don't want to add this number if it's crap, or it already
802
			// exists (which would cause a duplicate to be created).  We don't
803
			// take in to account the type when checking for duplicates.  This
804
			// is intentional: types aren't really very reliable.  We assume
805
			// that if the number exists at all, it doesn't need importing.
806
			// Because of this, we also can't update the cache (which we don't
807
			// need to anyway, so it's not a problem).
41 by edam
- updated TODO
808
			if( _contacts_cache.hasAssociatedNumber( id, number ) )
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
809
				continue;
1 by edam
Initial import
810
811
			// add phone number
53 by edam
abstracted the android contacts API in to an interface, ready to be switched to
812
			_backend.addContactPhone( id, number, data );
37 by edam
- updated TODO and NEWS
813
814
			// and add this address to the cache to prevent a addition of
815
			// duplicate date from another file
41 by edam
- updated TODO
816
			_contacts_cache.addAssociatedNumber( id, number );
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!?)
817
		}
1 by edam
Initial import
818
	}
819
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
820
	private void importContactEmails( Long id,
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
821
		HashMap< String, ContactData.PreferredDetail > datas )
822
		throws ContactCreationException
1 by edam
Initial import
823
	{
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!?)
824
		// add email addresses
53 by edam
abstracted the android contacts API in to an interface, ready to be switched to
825
		Set< String > datas_keys = datas.keySet();
41 by edam
- updated TODO
826
		Iterator< String > i = datas_keys.iterator();
1 by edam
Initial import
827
		while( i.hasNext() ) {
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
828
			String email = i.next();
829
			ContactData.PreferredDetail data = datas.get( email );
1 by edam
Initial import
830
37 by edam
- updated TODO and NEWS
831
			// we don't want to add this email address if it exists already or
93 by Tim Marston
minor style tweaks
832
			// we would introduce duplicates
41 by edam
- updated TODO
833
			if( _contacts_cache.hasAssociatedEmail( id, email ) )
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
834
				continue;
1 by edam
Initial import
835
836
			// add phone number
53 by edam
abstracted the android contacts API in to an interface, ready to be switched to
837
			_backend.addContactEmail( id, email, data );
37 by edam
- updated TODO and NEWS
838
839
			// and add this address to the cache to prevent a addition of
840
			// duplicate date from another file
41 by edam
- updated TODO
841
			_contacts_cache.addAssociatedEmail( id, email );
1 by edam
Initial import
842
		}
37 by edam
- updated TODO and NEWS
843
	}
844
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
845
	private void importContactAddresses( Long id,
846
		HashMap< String, ContactData.TypeDetail > datas )
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
847
		throws ContactCreationException
37 by edam
- updated TODO and NEWS
848
	{
849
		// add addresses
41 by edam
- updated TODO
850
		Set< String > datas_keys = datas.keySet();
851
		Iterator< String > i = datas_keys.iterator();
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!?)
852
		while( i.hasNext() ) {
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
853
			String address = i.next();
854
			ContactData.TypeDetail data = datas.get( address );
37 by edam
- updated TODO and NEWS
855
856
			// we don't want to add this address if it exists already or we
857
			// would introduce duplicates
41 by edam
- updated TODO
858
			if( _contacts_cache.hasAssociatedAddress( id, address ) )
37 by edam
- updated TODO and NEWS
859
				continue;
860
861
			// add postal address
53 by edam
abstracted the android contacts API in to an interface, ready to be switched to
862
			_backend.addContactAddresses( id, address, data );
37 by edam
- updated TODO and NEWS
863
864
			// and add this address to the cache to prevent a addition of
865
			// duplicate date from another file
41 by edam
- updated TODO
866
			_contacts_cache.addAssociatedAddress( id, address );
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
867
		}
868
	}
869
870
	private void importContactOrganisations( Long id,
871
		HashMap< String, ContactData.ExtraDetail > datas )
61 by edam
made contacts backend throw exceptions when it can't create contacts on the device; specified Locale in lower/upper case conversions; stripped old Contacts types from Importer (and replaced with our own types, which are now converted in the backend; switched using Integer() constructors to Integer.valueOf(); rewrote the main importer routine a bit
872
		throws ContactCreationException
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
873
	{
874
		// add addresses
41 by edam
- updated TODO
875
		Set< String > datas_keys = datas.keySet();
876
		Iterator< String > i = datas_keys.iterator();
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
877
		while( i.hasNext() ) {
878
			String organisation = i.next();
879
			ContactData.ExtraDetail data = datas.get( organisation );
880
881
			// we don't want to add this address if it exists already or we
882
			// would introduce duplicates
41 by edam
- updated TODO
883
			if( _contacts_cache.hasAssociatedOrganisation( id, organisation ) )
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
884
				continue;
885
886
			// add organisation address
53 by edam
abstracted the android contacts API in to an interface, ready to be switched to
887
			_backend.addContactOrganisation( id, organisation, data );
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
888
889
			// and add this address to the cache to prevent a addition of
890
			// duplicate date from another file
41 by edam
- updated TODO
891
			_contacts_cache.addAssociatedOrganisation( id, organisation );
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!?)
892
		}
1 by edam
Initial import
893
	}
894
95 by Tim Marston
added suopport for birthdays
895
	private void importContactNotes( Long id, HashSet< String > datas )
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
896
		throws ContactCreationException
897
	{
898
		// add notes
899
		Iterator< String > i = datas.iterator();
900
		while( i.hasNext() ) {
901
			String note = i.next();
902
903
			// we don't want to add this note if it exists already or we would
904
			// introduce duplicates
905
			if( _contacts_cache.hasAssociatedNote( id, note ) )
906
				continue;
907
908
			// add note
909
			_backend.addContactNote( id, note );
910
911
			// and add this note to the cache to prevent a addition of duplicate
912
			// date from another file
913
			_contacts_cache.addAssociatedNote( id, note );
914
		}
95 by Tim Marston
added suopport for birthdays
915
	}
916
917
	private void importContactBirthday( Long id, String birthday )
918
		throws ContactCreationException
919
	{
920
		// we don't want to import this birthday if it already exists
921
		if( _contacts_cache.hasAssociatedBirthday( id, birthday ) )
922
			return;
923
924
		// add birthday
925
		_backend.addContactBirthday( id, birthday );
926
927
		// and update the cache
928
		_contacts_cache.addAssociatedBirthday( id, birthday );
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
929
	}
930
3 by edam
- added "all done" message
931
	synchronized protected void checkAbort() throws AbortImportException
1 by edam
Initial import
932
	{
933
		if( _abort ) {
934
			// stop
935
			throw new AbortImportException();
936
		}
937
	}
938
}