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