/android/export-contacts

To get this branch, use:
bzr branch http://bzr.ed.am/android/export-contacts
5 by edam
- added ContactReader interface
1
/*
2
 * Exporter.java
3
 *
32 by Tim Marston
added support for birthdays
4
 * Copyright (C) 2011 to 2013 Tim Marston <tim@ed.am>
5 by edam
- added ContactReader interface
5
 *
6
 * This file is part of the Export Contacts program (hereafter referred
30 by Tim Marston
minor style tweaks
7
 * to as "this program").  For more information, see
12 by edam
changed all the URLs to ed.am, including copyrights, package names and project
8
 * http://ed.am/dev/android/export-contacts
5 by edam
- added ContactReader interface
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
12 by edam
changed all the URLs to ed.am, including copyrights, package names and project
24
package am.ed.exportcontacts;
5 by edam
- added ContactReader interface
25
26
import java.util.ArrayList;
27
28
import android.content.SharedPreferences;
29
import android.os.Message;
30
31
32
public class Exporter extends Thread
33
{
34
	public final static int ACTION_ABORT = 1;
35
	public final static int ACTION_ALLDONE = 2;
36
37
	public final static int RESPONSE_NEGATIVE = 0;
38
	public final static int RESPONSE_POSITIVE = 1;
39
40
	public final static int RESPONSEEXTRA_NONE = 0;
41
	public final static int RESPONSEEXTRA_ALWAYS = 1;
42
43
	private Doit _doit;
44
	private int _response;
45
	private boolean _abort = false;
46
	private boolean _is_finished = false;
47
48
	/**
49
	 * Data about a contact
50
	 */
51
	public class ContactData
52
	{
18 by edam
added ContactsContract backend; removed references to Contacts types (conversion to/from backend types now done in backends); added support for exporting NOTEs
53
		public final static int TYPE_HOME = 0;
54
		public final static int TYPE_WORK = 1;
55
		public final static int TYPE_MOBILE = 2;	// only used with phones
56
		public final static int TYPE_FAX_HOME = 3;	// only used with phones
57
		public final static int TYPE_FAX_WORK = 4;	// only used with phones
58
		public final static int TYPE_PAGER = 5;		// only used with phones
59
5 by edam
- added ContactReader interface
60
		class OrganisationDetail
61
		{
62
			protected String _org;
63
			protected String _title;
64
65
			public OrganisationDetail( String org, String title )
66
			{
67
				_org = org != null && org.length() > 0? org : null;
68
				_title = title != null && title.length() > 0? title : null;
69
			}
70
71
			public String getOrganisation()
72
			{
73
				return _org;
74
			}
75
76
			public String getTitle()
77
			{
78
				return _title;
79
			}
80
		}
81
82
		class NumberDetail
83
		{
84
			protected int _type;
85
			protected String _num;
86
87
			public NumberDetail( int type, String num )
88
			{
89
				_type = type;
90
				_num = num != null && num.length() > 0? num : null;
91
			}
92
93
			public int getType()
94
			{
95
				return _type;
96
			}
97
98
			public String getNumber()
99
			{
100
				return _num;
101
			}
102
		}
103
104
		class EmailDetail
105
		{
106
			protected int _type;
107
			protected String _email;
108
109
			public EmailDetail( int type, String email )
110
			{
111
				_type = type;
112
				_email = email != null && email.length() > 0? email : null;
113
			}
114
115
			public int getType()
116
			{
117
				return _type;
118
			}
119
120
			public String getEmail()
121
			{
122
				return _email;
123
			}
124
		}
125
126
		class AddressDetail
127
		{
128
			protected int _type;
129
			protected String _addr;
130
131
			public AddressDetail( int type, String addr )
132
			{
133
				_type = type;
134
				_addr = addr != null && addr.length() > 0? addr : null;
135
			}
136
137
			public int getType()
138
			{
139
				return _type;
140
			}
141
142
			public String getAddress()
143
			{
144
				return _addr;
145
			}
146
		}
147
148
		protected String _name = null;
149
		protected ArrayList< OrganisationDetail > _organisations = null;
150
		protected ArrayList< NumberDetail > _numbers = null;
151
		protected ArrayList< EmailDetail > _emails = null;
152
		protected ArrayList< AddressDetail > _addresses = null;
18 by edam
added ContactsContract backend; removed references to Contacts types (conversion to/from backend types now done in backends); added support for exporting NOTEs
153
		protected ArrayList< String > _notes = null;
32 by Tim Marston
added support for birthdays
154
		protected String _birthday = null;
5 by edam
- added ContactReader interface
155
156
		public void setName( String name )
157
		{
158
			_name = name != null && name.length() > 0? name : null;
159
		}
160
161
		public String getName()
162
		{
163
			return _name;
164
		}
165
166
		public void addOrganisation( OrganisationDetail organisation )
167
		{
168
			if( organisation.getOrganisation() == null ) return;
169
			if( _organisations == null )
170
				_organisations = new ArrayList< OrganisationDetail >();
171
			_organisations.add( organisation );
172
		}
173
174
		public ArrayList< OrganisationDetail > getOrganisations()
175
		{
176
			return _organisations;
177
		}
178
179
		public void addNumber( NumberDetail number )
180
		{
181
			if( number.getNumber() == null ) return;
182
			if( _numbers == null )
183
				_numbers = new ArrayList< NumberDetail >();
184
			_numbers.add( number );
185
		}
186
187
		public ArrayList< NumberDetail > getNumbers()
188
		{
189
			return _numbers;
190
		}
191
192
		public void addEmail( EmailDetail email )
193
		{
194
			if( email.getEmail() == null ) return;
195
			if( _emails == null )
196
				_emails = new ArrayList< EmailDetail >();
197
			_emails.add( email );
198
		}
199
200
		public ArrayList< EmailDetail > getEmails()
201
		{
202
			return _emails;
203
		}
204
205
		public void addAddress( AddressDetail address )
206
		{
207
			if( address.getAddress() == null ) return;
208
			if( _addresses == null )
209
				_addresses = new ArrayList< AddressDetail >();
210
			_addresses.add( address );
211
		}
212
213
		public ArrayList< AddressDetail > getAddresses()
214
		{
215
			return _addresses;
216
		}
217
18 by edam
added ContactsContract backend; removed references to Contacts types (conversion to/from backend types now done in backends); added support for exporting NOTEs
218
		public void addNote( String note )
219
		{
220
			if( _notes == null )
221
				_notes = new ArrayList< String >();
222
			_notes.add( note );
223
		}
224
225
		public ArrayList< String > getNotes()
226
		{
227
			return _notes;
228
		}
229
32 by Tim Marston
added support for birthdays
230
		public void setBirthday( String birthday )
231
		{
232
			_birthday = birthday;
233
		}
234
235
		public String getBirthday()
236
		{
237
			return _birthday;
238
		}
239
5 by edam
- added ContactReader interface
240
		public String getPrimaryIdentifier()
241
		{
242
			if( _name != null )
243
				return _name;
244
245
			if( _organisations != null &&
246
				_organisations.get( 0 ).getOrganisation() != null )
247
				return _organisations.get( 0 ).getOrganisation();
248
249
			if( _numbers!= null &&
250
				_numbers.get( 0 ).getNumber() != null )
251
				return _numbers.get( 0 ).getNumber();
252
253
			if( _emails!= null &&
254
				_emails.get( 0 ).getEmail() != null )
255
				return _emails.get( 0 ).getEmail();
256
257
			// no primary identifier
258
			return null;
259
		}
260
	}
261
262
	@SuppressWarnings( "serial" )
263
	protected class AbortExportException extends Exception { };
264
265
	public Exporter( Doit doit )
266
	{
267
		_doit = doit;
268
	}
269
270
	@Override
271
	public void run()
272
	{
273
		try
274
		{
275
			// update UI
276
			setProgressMessage( R.string.doit_scanning );
277
278
			// do the export
279
			exportContacts();
280
281
			// done!
282
			finish( ACTION_ALLDONE );
283
		}
284
		catch( AbortExportException e )
285
		{}
286
287
		// flag as finished to prevent interrupts
288
		setIsFinished();
289
	}
290
291
	synchronized private void setIsFinished()
292
	{
293
		_is_finished = true;
294
	}
295
296
	protected void exportContacts() throws AbortExportException
297
	{
298
		// set up a contact reader
21 by edam
fixed column values in Contacts backend; don't write-out empty notes; remember to close my queries; switched from wrappers to static valueOf() functions; fix line-endings (should be \r\n)
299
		Backend backend = null;
18 by edam
added ContactsContract backend; removed references to Contacts types (conversion to/from backend types now done in backends); added support for exporting NOTEs
300
		if( Integer.parseInt( android.os.Build.VERSION.SDK ) >= 5 )
21 by edam
fixed column values in Contacts backend; don't write-out empty notes; remember to close my queries; switched from wrappers to static valueOf() functions; fix line-endings (should be \r\n)
301
			backend = new ContactsContractBackend( _doit, this );
18 by edam
added ContactsContract backend; removed references to Contacts types (conversion to/from backend types now done in backends); added support for exporting NOTEs
302
		else
21 by edam
fixed column values in Contacts backend; don't write-out empty notes; remember to close my queries; switched from wrappers to static valueOf() functions; fix line-endings (should be \r\n)
303
			backend = new ContactsBackend( _doit, this );
18 by edam
added ContactsContract backend; removed references to Contacts types (conversion to/from backend types now done in backends); added support for exporting NOTEs
304
305
		// check we have contacts
21 by edam
fixed column values in Contacts backend; don't write-out empty notes; remember to close my queries; switched from wrappers to static valueOf() functions; fix line-endings (should be \r\n)
306
		int num_contacts = backend.getNumContacts();
5 by edam
- added ContactReader interface
307
		if( num_contacts == 0 )
308
			showError( R.string.error_nothingtodo );
309
310
		// count the number of contacts and set the progress bar max
311
		setProgress( 0 );
312
		setProgressMax( num_contacts );
313
314
		checkAbort();
315
		preExport();
316
317
		// loop through contacts
318
		int count = 0;
319
		while( true ) {
320
			checkAbort();
321
			ContactData contact = new ContactData();
21 by edam
fixed column values in Contacts backend; don't write-out empty notes; remember to close my queries; switched from wrappers to static valueOf() functions; fix line-endings (should be \r\n)
322
			if( !backend.getNextContact( contact ) )
5 by edam
- added ContactReader interface
323
				break;
324
325
			// export this one
326
			checkAbort();
327
			if( exportContact( contact ) )
328
				_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTWRITTEN );
329
			else
330
				_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTSKIPPED );
331
			setProgress( count++ );
332
		}
333
		setProgress( num_contacts );
334
335
		postExport();
336
	}
337
338
	public void wake()
339
	{
340
		wake( 0 );
341
	}
342
343
	synchronized public void wake( int response )
344
	{
345
		_response = response;
346
		notify();
347
	}
348
349
	synchronized public boolean setAbort()
350
	{
351
		if( !_is_finished && !_abort ) {
352
			_abort = true;
353
			notify();
354
			return true;
355
		}
356
		return false;
357
	}
358
359
	protected SharedPreferences getSharedPreferences()
360
	{
361
		return _doit.getSharedPreferences();
362
	}
363
364
	protected void showError( int res ) throws AbortExportException
365
	{
366
		showError( _doit.getText( res ).toString() );
367
	}
368
369
	synchronized protected void showError( String message )
370
			throws AbortExportException
371
	{
372
		checkAbort();
373
		_doit._handler.sendMessage( Message.obtain(
374
			_doit._handler, Doit.MESSAGE_ERROR, message ) );
375
		try {
376
			wait();
377
		}
378
		catch( InterruptedException e ) { }
379
380
		// no need to check if an abortion happened during the wait, we are
381
		// about to finish anyway!
382
		finish( ACTION_ABORT );
383
	}
384
385
	protected void showFatalError( int res ) throws AbortExportException
386
	{
387
		showFatalError( _doit.getText( res ).toString() );
388
	}
389
390
	synchronized protected void showFatalError( String message )
391
			throws AbortExportException
392
	{
393
		checkAbort();
394
		_doit._handler.sendMessage( Message.obtain(
395
			_doit._handler, Doit.MESSAGE_ERROR, message ) );
396
		try {
397
			wait();
398
		}
399
		catch( InterruptedException e ) { }
400
401
		// no need to check if an abortion happened during the wait, we are
402
		// about to finish anyway!
403
		finish( ACTION_ABORT );
404
	}
405
406
	protected boolean showContinue( int res ) throws AbortExportException
407
	{
408
		return showContinue( _doit.getText( res ).toString() );
409
	}
410
411
	synchronized protected boolean showContinue( String message )
412
			throws AbortExportException
413
	{
414
		checkAbort();
415
		_doit._handler.sendMessage( Message.obtain(
416
			_doit._handler, Doit.MESSAGE_CONTINUEORABORT, message ) );
417
		try {
418
			wait();
419
		}
420
		catch( InterruptedException e ) { }
421
422
		// check if an abortion happened during the wait
423
		checkAbort();
424
425
		return _response == RESPONSE_POSITIVE;
426
	}
427
428
	protected void setProgressMessage( int res ) throws AbortExportException
429
	{
430
		checkAbort();
431
		_doit._handler.sendMessage( Message.obtain( _doit._handler,
432
			Doit.MESSAGE_SETPROGRESSMESSAGE, getText( res ) ) );
433
	}
434
435
	protected void setProgressMax( int max_progress )
436
			throws AbortExportException
437
	{
438
		checkAbort();
439
		_doit._handler.sendMessage( Message.obtain(
440
			_doit._handler, Doit.MESSAGE_SETMAXPROGRESS,
21 by edam
fixed column values in Contacts backend; don't write-out empty notes; remember to close my queries; switched from wrappers to static valueOf() functions; fix line-endings (should be \r\n)
441
			Integer.valueOf( max_progress ) ) );
5 by edam
- added ContactReader interface
442
	}
443
444
	protected void setTmpProgress( int tmp_progress )
445
		throws AbortExportException
446
	{
447
		checkAbort();
448
		_doit._handler.sendMessage( Message.obtain(
449
			_doit._handler, Doit.MESSAGE_SETTMPPROGRESS,
21 by edam
fixed column values in Contacts backend; don't write-out empty notes; remember to close my queries; switched from wrappers to static valueOf() functions; fix line-endings (should be \r\n)
450
			Integer.valueOf( tmp_progress ) ) );
5 by edam
- added ContactReader interface
451
	}
452
453
	protected void setProgress( int progress ) throws AbortExportException
454
	{
455
		checkAbort();
456
		_doit._handler.sendMessage( Message.obtain(
457
			_doit._handler, Doit.MESSAGE_SETPROGRESS,
21 by edam
fixed column values in Contacts backend; don't write-out empty notes; remember to close my queries; switched from wrappers to static valueOf() functions; fix line-endings (should be \r\n)
458
			Integer.valueOf( progress ) ) );
5 by edam
- added ContactReader interface
459
	}
460
461
	protected void finish( int action ) throws AbortExportException
462
	{
463
		// update UI to reflect action
464
		int message;
465
		switch( action )
466
		{
467
		case ACTION_ALLDONE:	message = Doit.MESSAGE_ALLDONE; break;
468
		default:	// fall through
469
		case ACTION_ABORT:		message = Doit.MESSAGE_ABORT; break;
470
		}
471
		_doit._handler.sendEmptyMessage( message );
472
473
		// stop
474
		throw new AbortExportException();
475
	}
476
477
	protected CharSequence getText( int res )
478
	{
479
		return _doit.getText( res );
480
	}
481
482
	protected void skipContact() throws AbortExportException
483
	{
484
		checkAbort();
485
		_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTSKIPPED );
486
	}
487
488
	synchronized protected void checkAbort() throws AbortExportException
489
	{
490
		if( _abort ) {
491
			// stop
492
			throw new AbortExportException();
493
		}
494
	}
495
496
	protected void preExport() throws AbortExportException
497
	{
498
	}
499
500
	protected boolean exportContact( ContactData contact )
501
		throws AbortExportException
502
	{
503
		throw new UnsupportedOperationException();
504
	}
505
506
	protected void postExport() throws AbortExportException
507
	{
508
	}
509
510
}