bzr branch
http://bzr.ed.am/android/import-contacts
6
by edam
- added GPL header comments to all files |
1 |
/* |
2 |
* Importer.java |
|
3 |
* |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
4 |
* Copyright (C) 2009 to 2011 Tim Marston <edam@waxworlds.org> |
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 |
|
8 |
* http://www.waxworlds.org/edam/software/android/import-contacts |
|
9 |
* |
|
10 |
* This program is free software: you can redistribute it and/or modify |
|
11 |
* it under the terms of the GNU General Public License as published by |
|
12 |
* the Free Software Foundation, either version 3 of the License, or |
|
13 |
* (at your option) any later version. |
|
14 |
* |
|
15 |
* This program is distributed in the hope that it will be useful, |
|
16 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
17 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
18 |
* GNU General Public License for more details. |
|
19 |
* |
|
20 |
* You should have received a copy of the GNU General Public License |
|
21 |
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
22 |
*/ |
|
23 |
||
14
by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one) |
24 |
package org.waxworlds.edam.importcontacts; |
1
by edam
Initial import |
25 |
|
26 |
import java.util.HashMap; |
|
27 |
import java.util.Iterator; |
|
28 |
import java.util.Set; |
|
29 |
import java.util.regex.Matcher; |
|
30 |
import java.util.regex.Pattern; |
|
31 |
||
32 |
import android.content.ContentUris; |
|
33 |
import android.content.ContentValues; |
|
34 |
import android.content.SharedPreferences; |
|
35 |
import android.net.Uri; |
|
36 |
import android.os.Message; |
|
37 |
import android.provider.Contacts; |
|
38 |
||
37
by edam
- updated TODO and NEWS |
39 |
|
1
by edam
Initial import |
40 |
public class Importer extends Thread |
41 |
{ |
|
3
by edam
- added "all done" message |
42 |
public final static int ACTION_ABORT = 1; |
43 |
public final static int ACTION_ALLDONE = 2; |
|
1
by edam
Initial import |
44 |
|
45 |
public final static int RESPONSE_NEGATIVE = 0; |
|
46 |
public final static int RESPONSE_POSITIVE = 1; |
|
47 |
||
48 |
public final static int RESPONSEEXTRA_NONE = 0; |
|
49 |
public final static int RESPONSEEXTRA_ALWAYS = 1; |
|
50 |
||
51 |
private Doit _doit; |
|
52 |
private int _response; |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
53 |
private int _response_extra; |
54 |
private int _merge_setting; |
|
55 |
private int _last_merge_decision; |
|
1
by edam
Initial import |
56 |
private boolean _abort = false; |
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
57 |
private boolean _is_finished = false; |
39
by edam
- pulled contacts cache out in to seperate class |
58 |
private ContactsCache _contactsCache = null; |
1
by edam
Initial import |
59 |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
60 |
@SuppressWarnings("serial") |
61 |
protected class ContactNeedsMoreInfoException extends Exception |
|
62 |
{ |
|
63 |
} |
|
64 |
||
65 |
/** |
|
66 |
* Data about a contact |
|
67 |
*/ |
|
1
by edam
Initial import |
68 |
public class ContactData |
69 |
{ |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
70 |
class TypeDetail |
71 |
{ |
|
72 |
protected int _type; |
|
73 |
||
74 |
public TypeDetail( int type ) |
|
75 |
{ |
|
76 |
_type = type; |
|
77 |
} |
|
78 |
||
79 |
public int getType() |
|
80 |
{ |
|
81 |
return _type; |
|
82 |
} |
|
83 |
} |
|
84 |
||
85 |
class PreferredDetail extends TypeDetail |
|
86 |
{ |
|
87 |
protected boolean _is_preferred; |
|
88 |
||
89 |
public PreferredDetail( int type, boolean is_preferred ) |
|
90 |
{ |
|
91 |
super( type ); |
|
92 |
_is_preferred = is_preferred; |
|
93 |
} |
|
94 |
||
95 |
public boolean isPreferred() |
|
96 |
{ |
|
97 |
return _is_preferred; |
|
98 |
} |
|
99 |
} |
|
100 |
||
101 |
class ExtraDetail extends PreferredDetail |
|
102 |
{ |
|
103 |
protected String _extra; |
|
104 |
||
105 |
public ExtraDetail( int type, boolean is_preferred, String extra ) |
|
106 |
{ |
|
107 |
super( type, is_preferred ); |
|
108 |
||
109 |
if( extra != null ) extra = extra.trim(); |
|
110 |
_extra = extra; |
|
111 |
} |
|
112 |
||
113 |
public String getExtra() |
|
114 |
{ |
|
115 |
return _extra; |
|
116 |
} |
|
117 |
||
118 |
public void setExtra( String extra ) |
|
119 |
{ |
|
120 |
if( extra != null ) extra = extra.trim(); |
|
121 |
_extra = extra; |
|
122 |
} |
|
123 |
} |
|
124 |
||
125 |
protected String _name = null; |
|
126 |
protected String _primary_organisation = null; |
|
127 |
protected boolean _primary_organisation_is_preferred = false; |
|
128 |
protected String _primary_number = null; |
|
129 |
protected boolean _primary_number_is_preferred = false; |
|
130 |
protected String _primary_email = null; |
|
131 |
protected boolean _primary_email_is_preferred = false; |
|
132 |
protected HashMap< String, ExtraDetail > _organisations = null; |
|
133 |
protected HashMap< String, PreferredDetail > _numbers = null; |
|
134 |
protected HashMap< String, PreferredDetail > _emails = null; |
|
135 |
protected HashMap< String, TypeDetail > _addresses = null; |
|
1
by edam
Initial import |
136 |
|
137 |
protected void setName( String name ) |
|
138 |
{ |
|
139 |
_name = name; |
|
140 |
} |
|
141 |
||
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
142 |
public boolean hasName() |
143 |
{ |
|
144 |
return _name != null; |
|
145 |
} |
|
146 |
||
1
by edam
Initial import |
147 |
public String getName() |
148 |
{ |
|
149 |
return _name; |
|
150 |
} |
|
151 |
||
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
152 |
protected void addOrganisation( String organisation, String title, |
153 |
boolean is_preferred ) |
|
154 |
{ |
|
155 |
organisation = organisation.trim(); |
|
156 |
if( organisation.length() <= 0 ) |
|
157 |
{ |
|
158 |
// TODO: warn that an imported organisation is being ignored |
|
159 |
return; |
|
160 |
} |
|
161 |
||
162 |
if( title != null ) { |
|
163 |
title = title.trim(); |
|
164 |
if( title.length() <= 0 ) title = null; |
|
165 |
} |
|
166 |
||
167 |
// add the organisation, as non-preferred (we prefer only one |
|
168 |
// organisation in finalise() after they're all imported) |
|
169 |
if( _organisations == null ) |
|
170 |
_organisations = new HashMap< String, ExtraDetail >(); |
|
171 |
if( !_organisations.containsKey( organisation ) ) |
|
172 |
_organisations.put( organisation, |
|
173 |
new ExtraDetail( 0, false, title ) ); |
|
174 |
||
175 |
// if this is the first organisation added, or it's a preferred |
|
176 |
// organisation and a previous organisation wasn't, then remember |
|
177 |
// that this is the "primary organisation". |
|
178 |
if( _primary_organisation == null || |
|
179 |
( is_preferred && !_primary_organisation_is_preferred ) ) |
|
180 |
{ |
|
181 |
_primary_organisation = organisation; |
|
182 |
_primary_organisation_is_preferred = is_preferred; |
|
183 |
} |
|
184 |
} |
|
185 |
||
186 |
public boolean hasOrganisations() |
|
187 |
{ |
|
188 |
return _organisations != null && _organisations.size() > 0; |
|
189 |
} |
|
190 |
||
191 |
public HashMap< String, ExtraDetail > getOrganisations() |
|
192 |
{ |
|
193 |
return _organisations; |
|
194 |
} |
|
195 |
||
196 |
public boolean hasPrimaryOrganisation() |
|
197 |
{ |
|
198 |
return _primary_organisation != null; |
|
199 |
} |
|
200 |
||
201 |
public String getPrimaryOrganisation() |
|
202 |
{ |
|
203 |
return _primary_organisation; |
|
204 |
} |
|
205 |
||
206 |
protected void addNumber( String number, int type, |
|
207 |
boolean is_preferred ) |
|
208 |
{ |
|
209 |
number = sanitisePhoneNumber( number ); |
|
210 |
if( number == null ) |
|
211 |
{ |
|
212 |
// TODO: warn that an imported phone number is being ignored |
|
213 |
return; |
|
214 |
} |
|
215 |
||
216 |
// add the number, as non-preferred (we prefer only one number |
|
217 |
// in finalise() after they're all imported) |
|
218 |
if( _numbers == null ) |
|
219 |
_numbers = new HashMap< String, PreferredDetail >(); |
|
220 |
if( !_numbers.containsKey( number ) ) |
|
221 |
_numbers.put( number, |
|
222 |
new PreferredDetail( type, false ) ); |
|
223 |
||
224 |
// if this is the first number added, or it's a preferred number |
|
225 |
// and a previous number wasn't, then remember that this is the |
|
226 |
// "primary number". |
|
227 |
if( _primary_number == null || |
|
228 |
( is_preferred && !_primary_number_is_preferred ) ) |
|
229 |
{ |
|
230 |
_primary_number = number; |
|
231 |
_primary_number_is_preferred = is_preferred; |
|
232 |
} |
|
233 |
} |
|
234 |
||
235 |
public boolean hasNumbers() |
|
236 |
{ |
|
237 |
return _numbers != null && _numbers.size() > 0; |
|
238 |
} |
|
239 |
||
240 |
public HashMap< String, PreferredDetail > getNumbers() |
|
241 |
{ |
|
242 |
return _numbers; |
|
243 |
} |
|
244 |
||
245 |
public boolean hasPrimaryNumber() |
|
246 |
{ |
|
247 |
return _primary_number != null; |
|
248 |
} |
|
249 |
||
250 |
public String getPrimaryNumber() |
|
251 |
{ |
|
252 |
return _primary_number; |
|
253 |
} |
|
254 |
||
255 |
protected void addEmail( String email, int type, boolean is_preferred ) |
|
256 |
{ |
|
257 |
||
258 |
email = sanitisesEmailAddress( email ); |
|
259 |
if( email == null ) |
|
260 |
{ |
|
261 |
// TODO: warn that an imported email addtrss is being ignored |
|
262 |
return; |
|
263 |
} |
|
264 |
||
265 |
// add the email, as non-preferred (we prefer only one email in |
|
266 |
// finalise() after they're all imported) |
|
267 |
if( _emails == null ) |
|
268 |
_emails = new HashMap< String, PreferredDetail >(); |
|
1
by edam
Initial import |
269 |
if( !_emails.containsKey( email ) ) |
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
270 |
_emails.put( email, new PreferredDetail( type, false ) ); |
271 |
||
272 |
// if this is the first email added, or it's a preferred email |
|
273 |
// and a previous email wasn't, then remember that this is the |
|
274 |
// "primary email". |
|
275 |
if( _primary_email == null || |
|
276 |
( is_preferred && !_primary_email_is_preferred ) ) |
|
277 |
{ |
|
278 |
_primary_email = email; |
|
279 |
_primary_email_is_preferred = is_preferred; |
|
280 |
} |
|
281 |
} |
|
282 |
||
283 |
public boolean hasEmails() |
|
284 |
{ |
|
285 |
return _emails != null && _emails.size() > 0; |
|
286 |
} |
|
287 |
||
288 |
public HashMap< String, PreferredDetail > getEmails() |
|
289 |
{ |
|
290 |
return _emails; |
|
291 |
} |
|
292 |
||
293 |
public boolean hasPrimaryEmail() |
|
294 |
{ |
|
295 |
return _primary_email != null; |
|
296 |
} |
|
297 |
||
298 |
public String getPrimaryEmail() |
|
299 |
{ |
|
300 |
return _primary_email; |
|
1
by edam
Initial import |
301 |
} |
37
by edam
- updated TODO and NEWS |
302 |
|
303 |
protected void addAddress( String address, int type ) |
|
304 |
{ |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
305 |
address = address.trim(); |
306 |
if( address.length() <= 0 ) |
|
307 |
{ |
|
308 |
// TODO: warn that an imported address is being ignored |
|
309 |
return; |
|
310 |
} |
|
311 |
||
37
by edam
- updated TODO and NEWS |
312 |
if( _addresses == null ) _addresses = |
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
313 |
new HashMap< String, TypeDetail >(); |
37
by edam
- updated TODO and NEWS |
314 |
if( !_addresses.containsKey( address ) ) |
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
315 |
_addresses.put( address, new TypeDetail( type ) ); |
316 |
} |
|
317 |
||
318 |
public boolean hasAddresses() |
|
319 |
{ |
|
320 |
return _addresses != null && _addresses.size() > 0; |
|
321 |
} |
|
322 |
||
323 |
public HashMap< String, TypeDetail > getAddresses() |
|
324 |
{ |
|
325 |
return _addresses; |
|
326 |
} |
|
327 |
||
328 |
protected void finalise() |
|
329 |
{ |
|
330 |
// ensure that if there is a primary number, it is preferred so |
|
331 |
// that there is always one preferred number. Android will assign |
|
332 |
// preference to one anyway so we might as well decide one sensibly. |
|
333 |
if( _primary_number != null ) { |
|
334 |
PreferredDetail data = _numbers.get( _primary_number ); |
|
335 |
_numbers.put( _primary_number, |
|
336 |
new PreferredDetail( data.getType(), true ) ); |
|
337 |
} |
|
338 |
||
339 |
// do the same for the primary email |
|
340 |
if( _primary_email != null ) { |
|
341 |
PreferredDetail data = _emails.get( _primary_email ); |
|
342 |
_emails.put( _primary_email, |
|
343 |
new PreferredDetail( data.getType(), true ) ); |
|
344 |
} |
|
345 |
||
346 |
// do the same for the primary organisation |
|
347 |
if( _primary_organisation != null ) { |
|
348 |
ExtraDetail data = _organisations.get( _primary_organisation ); |
|
349 |
_organisations.put( _primary_organisation, |
|
350 |
new ExtraDetail( 0, true, data.getExtra() ) ); |
|
351 |
} |
|
352 |
} |
|
353 |
||
354 |
private String sanitisePhoneNumber( String number ) |
|
355 |
{ |
|
356 |
number = number.trim(); |
|
357 |
Pattern p = Pattern.compile( "^[-\\(\\) \\+0-9#*]+" ); |
|
358 |
Matcher m = p.matcher( number ); |
|
359 |
if( m.lookingAt() ) return m.group( 0 ); |
|
360 |
return null; |
|
361 |
} |
|
362 |
||
363 |
private String sanitisesEmailAddress( String email ) |
|
364 |
{ |
|
365 |
email = email.trim(); |
|
366 |
Pattern p = Pattern.compile( |
|
367 |
"^[^ @]+@[a-zA-Z]([-a-zA-Z0-9]*[a-zA-z0-9])?(\\.[a-zA-Z]([-a-zA-Z0-9]*[a-zA-z0-9])?)+$" ); |
|
368 |
Matcher m = p.matcher( email ); |
|
369 |
if( m.matches() ) { |
|
370 |
String[] bits = email.split( "@" ); |
|
371 |
return bits[ 0 ] + "@" + bits[ 1 ].toLowerCase(); |
|
372 |
} |
|
373 |
return null; |
|
37
by edam
- updated TODO and NEWS |
374 |
} |
1
by edam
Initial import |
375 |
} |
376 |
||
14
by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one) |
377 |
@SuppressWarnings("serial") |
1
by edam
Initial import |
378 |
protected class AbortImportException extends Exception { }; |
379 |
||
380 |
public Importer( Doit doit ) |
|
381 |
{ |
|
382 |
_doit = doit; |
|
383 |
||
384 |
SharedPreferences prefs = getSharedPreferences(); |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
385 |
_merge_setting = prefs.getInt( "merge_setting", Doit.ACTION_PROMPT ); |
1
by edam
Initial import |
386 |
} |
387 |
||
388 |
@Override |
|
389 |
public void run() |
|
390 |
{ |
|
391 |
try |
|
392 |
{ |
|
39
by edam
- pulled contacts cache out in to seperate class |
393 |
// update UI |
394 |
setProgressMessage( R.string.doit_caching ); |
|
395 |
||
396 |
// build a cache of existing contacts |
|
397 |
_contactsCache = new ContactsCache(); |
|
398 |
_contactsCache.buildCache( _doit ); |
|
1
by edam
Initial import |
399 |
|
400 |
// do the import |
|
401 |
onImport(); |
|
402 |
||
403 |
// done! |
|
3
by edam
- added "all done" message |
404 |
finish( ACTION_ALLDONE ); |
1
by edam
Initial import |
405 |
} |
406 |
catch( AbortImportException e ) |
|
407 |
{} |
|
3
by edam
- added "all done" message |
408 |
|
409 |
// flag as finished to prevent interrupts |
|
410 |
setIsFinished(); |
|
411 |
} |
|
412 |
||
413 |
synchronized private void setIsFinished() |
|
414 |
{ |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
415 |
_is_finished = true; |
1
by edam
Initial import |
416 |
} |
417 |
||
418 |
protected void onImport() throws AbortImportException |
|
419 |
{ |
|
420 |
} |
|
421 |
||
422 |
public void wake() |
|
423 |
{ |
|
424 |
wake( 0, RESPONSEEXTRA_NONE ); |
|
425 |
} |
|
426 |
||
427 |
public void wake( int response ) |
|
428 |
{ |
|
429 |
wake( response, RESPONSEEXTRA_NONE ); |
|
430 |
} |
|
431 |
||
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
432 |
synchronized public void wake( int response, int response_extra ) |
1
by edam
Initial import |
433 |
{ |
434 |
_response = response; |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
435 |
_response_extra = response_extra; |
1
by edam
Initial import |
436 |
notify(); |
437 |
} |
|
438 |
||
3
by edam
- added "all done" message |
439 |
synchronized public boolean setAbort() |
1
by edam
Initial import |
440 |
{ |
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
441 |
if( !_is_finished && !_abort ) { |
3
by edam
- added "all done" message |
442 |
_abort = true; |
443 |
notify(); |
|
444 |
return true; |
|
445 |
} |
|
446 |
return false; |
|
1
by edam
Initial import |
447 |
} |
448 |
||
449 |
protected SharedPreferences getSharedPreferences() |
|
450 |
{ |
|
451 |
return _doit.getSharedPreferences(); |
|
452 |
} |
|
453 |
||
454 |
protected void showError( int res ) throws AbortImportException |
|
455 |
{ |
|
456 |
showError( _doit.getText( res ).toString() ); |
|
457 |
} |
|
458 |
||
459 |
synchronized protected void showError( String message ) |
|
460 |
throws AbortImportException |
|
461 |
{ |
|
462 |
checkAbort(); |
|
463 |
_doit._handler.sendMessage( Message.obtain( |
|
36
by edam
- formatting: removed some double-indents on overrunning lines |
464 |
_doit._handler, Doit.MESSAGE_ERROR, message ) ); |
1
by edam
Initial import |
465 |
try { |
466 |
wait(); |
|
467 |
} |
|
468 |
catch( InterruptedException e ) { } |
|
14
by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one) |
469 |
|
3
by edam
- added "all done" message |
470 |
// no need to check if an abortion happened during the wait, we are |
471 |
// about to finish anyway! |
|
472 |
finish( ACTION_ABORT ); |
|
1
by edam
Initial import |
473 |
} |
474 |
||
475 |
protected void showFatalError( int res ) throws AbortImportException |
|
476 |
{ |
|
477 |
showFatalError( _doit.getText( res ).toString() ); |
|
478 |
} |
|
479 |
||
480 |
synchronized protected void showFatalError( String message ) |
|
481 |
throws AbortImportException |
|
482 |
{ |
|
483 |
checkAbort(); |
|
484 |
_doit._handler.sendMessage( Message.obtain( |
|
36
by edam
- formatting: removed some double-indents on overrunning lines |
485 |
_doit._handler, Doit.MESSAGE_ERROR, message ) ); |
1
by edam
Initial import |
486 |
try { |
487 |
wait(); |
|
488 |
} |
|
489 |
catch( InterruptedException e ) { } |
|
14
by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one) |
490 |
|
3
by edam
- added "all done" message |
491 |
// no need to check if an abortion happened during the wait, we are |
492 |
// about to finish anyway! |
|
493 |
finish( ACTION_ABORT ); |
|
1
by edam
Initial import |
494 |
} |
495 |
||
496 |
protected boolean showContinue( int res ) throws AbortImportException |
|
497 |
{ |
|
498 |
return showContinue( _doit.getText( res ).toString() ); |
|
499 |
} |
|
500 |
||
501 |
synchronized protected boolean showContinue( String message ) |
|
502 |
throws AbortImportException |
|
503 |
{ |
|
504 |
checkAbort(); |
|
505 |
_doit._handler.sendMessage( Message.obtain( |
|
36
by edam
- formatting: removed some double-indents on overrunning lines |
506 |
_doit._handler, Doit.MESSAGE_CONTINUEORABORT, message ) ); |
1
by edam
Initial import |
507 |
try { |
508 |
wait(); |
|
509 |
} |
|
510 |
catch( InterruptedException e ) { } |
|
3
by edam
- added "all done" message |
511 |
|
512 |
// check if an abortion happened during the wait |
|
513 |
checkAbort(); |
|
514 |
||
1
by edam
Initial import |
515 |
return _response == RESPONSE_POSITIVE; |
516 |
} |
|
517 |
||
518 |
protected void setProgressMessage( int res ) throws AbortImportException |
|
519 |
{ |
|
520 |
checkAbort(); |
|
521 |
_doit._handler.sendMessage( Message.obtain( _doit._handler, |
|
36
by edam
- formatting: removed some double-indents on overrunning lines |
522 |
Doit.MESSAGE_SETPROGRESSMESSAGE, getText( res ) ) ); |
1
by edam
Initial import |
523 |
} |
524 |
||
525 |
protected void setProgressMax( int maxProgress ) |
|
526 |
throws AbortImportException |
|
527 |
{ |
|
528 |
checkAbort(); |
|
529 |
_doit._handler.sendMessage( Message.obtain( |
|
36
by edam
- formatting: removed some double-indents on overrunning lines |
530 |
_doit._handler, Doit.MESSAGE_SETMAXPROGRESS, |
531 |
new Integer( maxProgress ) ) ); |
|
1
by edam
Initial import |
532 |
} |
533 |
||
534 |
protected void setTmpProgress( int tmpProgress ) throws AbortImportException |
|
535 |
{ |
|
536 |
checkAbort(); |
|
537 |
_doit._handler.sendMessage( Message.obtain( |
|
36
by edam
- formatting: removed some double-indents on overrunning lines |
538 |
_doit._handler, Doit.MESSAGE_SETTMPPROGRESS, |
539 |
new Integer( tmpProgress ) ) ); |
|
1
by edam
Initial import |
540 |
} |
541 |
||
542 |
protected void setProgress( int progress ) throws AbortImportException |
|
543 |
{ |
|
544 |
checkAbort(); |
|
545 |
_doit._handler.sendMessage( Message.obtain( |
|
36
by edam
- formatting: removed some double-indents on overrunning lines |
546 |
_doit._handler, Doit.MESSAGE_SETPROGRESS, |
547 |
new Integer( progress ) ) ); |
|
1
by edam
Initial import |
548 |
} |
549 |
||
3
by edam
- added "all done" message |
550 |
protected void finish( int action ) throws AbortImportException |
1
by edam
Initial import |
551 |
{ |
3
by edam
- added "all done" message |
552 |
// update UI to reflect action |
553 |
int message; |
|
554 |
switch( action ) |
|
555 |
{ |
|
14
by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one) |
556 |
case ACTION_ALLDONE: message = Doit.MESSAGE_ALLDONE; break; |
3
by edam
- added "all done" message |
557 |
default: // fall through |
14
by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one) |
558 |
case ACTION_ABORT: message = Doit.MESSAGE_ABORT; break; |
3
by edam
- added "all done" message |
559 |
} |
560 |
_doit._handler.sendEmptyMessage( message ); |
|
1
by edam
Initial import |
561 |
|
3
by edam
- added "all done" message |
562 |
// stop |
563 |
throw new AbortImportException(); |
|
1
by edam
Initial import |
564 |
} |
565 |
||
566 |
protected CharSequence getText( int res ) |
|
567 |
{ |
|
568 |
return _doit.getText( res ); |
|
569 |
} |
|
570 |
||
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
571 |
protected boolean isImportRequired( ContactData contact ) |
572 |
throws AbortImportException, ContactNeedsMoreInfoException |
|
1
by edam
Initial import |
573 |
{ |
574 |
checkAbort(); |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
575 |
return isImportRequired( contact, _merge_setting ); |
1
by edam
Initial import |
576 |
} |
577 |
||
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
578 |
synchronized private boolean isImportRequired( |
579 |
ContactData contact, int merge_setting ) |
|
580 |
throws AbortImportException, ContactNeedsMoreInfoException |
|
1
by edam
Initial import |
581 |
{ |
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
582 |
_last_merge_decision = merge_setting; |
583 |
||
584 |
// create a cache identifier which we can use to detect if this contact |
|
585 |
// is valid for importing |
|
586 |
ContactsCache.CacheIdentifier identifier = |
|
587 |
ContactsCache.createIdentifier( contact ); |
|
588 |
if( identifier == null ) |
|
589 |
throw new ContactNeedsMoreInfoException(); |
|
1
by edam
Initial import |
590 |
|
591 |
// handle special cases |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
592 |
switch( merge_setting ) |
1
by edam
Initial import |
593 |
{ |
9
by edam
- added scroll view to all layouts |
594 |
case Doit.ACTION_KEEP: |
1
by edam
Initial import |
595 |
// if we keep contacts on duplicate, we better check for one |
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
596 |
return !_contactsCache.canLookup( identifier ); |
1
by edam
Initial import |
597 |
|
9
by edam
- added scroll view to all layouts |
598 |
case Doit.ACTION_PROMPT: |
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
599 |
// if we are prompting on duplicate, we better check for one and if |
600 |
// the contact doesn'te exist, we want to import it |
|
601 |
if( !_contactsCache.canLookup( identifier ) ) |
|
1
by edam
Initial import |
602 |
return true; |
603 |
||
604 |
// ok, it exists, so do prompt |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
605 |
_doit._handler.sendMessage( Message.obtain( _doit._handler, |
606 |
Doit.MESSAGE_MERGEPROMPT, identifier.getDetail() ) ); |
|
1
by edam
Initial import |
607 |
try { |
608 |
wait(); |
|
609 |
} |
|
610 |
catch( InterruptedException e ) { } |
|
611 |
||
3
by edam
- added "all done" message |
612 |
// check if an abortion happened during the wait |
613 |
checkAbort(); |
|
614 |
||
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
615 |
// if "always" was selected, make choice permanent |
616 |
if( _response_extra == RESPONSEEXTRA_ALWAYS ) |
|
617 |
_merge_setting = _response; |
|
1
by edam
Initial import |
618 |
|
36
by edam
- formatting: removed some double-indents on overrunning lines |
619 |
// recurse, with our new merge setting |
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
620 |
return isImportRequired( contact, _response ); |
1
by edam
Initial import |
621 |
} |
622 |
||
623 |
// for all other cases (either overwriting or merging) we will need the |
|
624 |
// imported data |
|
625 |
return true; |
|
626 |
} |
|
627 |
||
628 |
protected void skipContact() throws AbortImportException |
|
629 |
{ |
|
630 |
checkAbort(); |
|
3
by edam
- added "all done" message |
631 |
_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTSKIPPED ); |
1
by edam
Initial import |
632 |
} |
633 |
||
634 |
protected void importContact( ContactData contact ) |
|
635 |
throws AbortImportException |
|
636 |
{ |
|
637 |
checkAbort(); |
|
638 |
||
3
by edam
- added "all done" message |
639 |
// if( !showContinue( "====[ IMPORTING ]====\n: " + contact._name ) ) |
640 |
// finish( ACTION_ABORT ); |
|
1
by edam
Initial import |
641 |
|
642 |
ContentValues values = new ContentValues(); |
|
643 |
boolean uiInformed = false; |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
644 |
Long id = null; |
645 |
||
646 |
// give the contact a chance to finalise it's data |
|
647 |
contact.finalise(); |
|
648 |
||
649 |
// create something, from the contact data, that we can use to identify |
|
650 |
// a cache entry and attempt to lookup the id of an existing contact in |
|
651 |
// the cache with it |
|
652 |
ContactsCache.CacheIdentifier identifier = |
|
653 |
ContactsCache.createIdentifier( contact ); |
|
654 |
if( identifier != null ) id = (Long)_contactsCache.lookup( identifier ); |
|
1
by edam
Initial import |
655 |
|
656 |
// does contact exist already? |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
657 |
if( id != null ) |
1
by edam
Initial import |
658 |
{ |
659 |
// should we skip this import altogether? |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
660 |
if( _last_merge_decision == Doit.ACTION_KEEP ) return; |
1
by edam
Initial import |
661 |
|
662 |
// get contact's URI |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
663 |
Uri contactUri = ContentUris.withAppendedId( |
36
by edam
- formatting: removed some double-indents on overrunning lines |
664 |
Contacts.People.CONTENT_URI, id ); |
1
by edam
Initial import |
665 |
|
666 |
// should we destroy the existing contact before importing? |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
667 |
if( _last_merge_decision == Doit.ACTION_OVERWRITE ) |
668 |
{ |
|
669 |
// remove from device |
|
1
by edam
Initial import |
670 |
_doit.getContentResolver().delete( contactUri, null, null ); |
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
671 |
|
672 |
// update cache |
|
673 |
_contactsCache.removeLookup( identifier ); |
|
674 |
_contactsCache.removeAssociatedData( id ); |
|
675 |
||
676 |
// show that we're overwriting a contact |
|
36
by edam
- formatting: removed some double-indents on overrunning lines |
677 |
_doit._handler.sendEmptyMessage( |
678 |
Doit.MESSAGE_CONTACTOVERWRITTEN ); |
|
1
by edam
Initial import |
679 |
uiInformed = true; |
680 |
||
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
681 |
// discard the contact id |
682 |
id = null; |
|
1
by edam
Initial import |
683 |
} |
684 |
} |
|
685 |
||
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
686 |
// if we don't have a contact id yet (or if we did, but we destroyed it |
687 |
// when we deleted the contact), we'll have to create a new contact |
|
688 |
if( id == null ) |
|
1
by edam
Initial import |
689 |
{ |
690 |
// create a new contact |
|
691 |
values.put( Contacts.People.NAME, contact._name ); |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
692 |
Uri contactUri = _doit.getContentResolver().insert( |
36
by edam
- formatting: removed some double-indents on overrunning lines |
693 |
Contacts.People.CONTENT_URI, values ); |
1
by edam
Initial import |
694 |
id = ContentUris.parseId( contactUri ); |
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
695 |
if( id == null || id <= 0 ) |
696 |
showError( R.string.error_unabletoaddcontact ); |
|
1
by edam
Initial import |
697 |
|
14
by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one) |
698 |
// try to add them to the "My Contacts" group |
699 |
try { |
|
700 |
Contacts.People.addToMyContactsGroup( |
|
12
by edam
- bugfix: add contacts to the "my contacts" group didn't actually work on a real device. So we're doing it a different way. |
701 |
_doit.getContentResolver(), id ); |
14
by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one) |
702 |
} |
36
by edam
- formatting: removed some double-indents on overrunning lines |
703 |
catch( IllegalStateException e ) { |
704 |
// ignore any failure |
|
705 |
} |
|
1
by edam
Initial import |
706 |
|
707 |
// update cache |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
708 |
_contactsCache.addLookup( |
709 |
ContactsCache.createIdentifier( contact ), id ); |
|
1
by edam
Initial import |
710 |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
711 |
// if we haven't already shown that we're overwriting a contact, |
712 |
// show that we're creating a new contact |
|
1
by edam
Initial import |
713 |
if( !uiInformed ) { |
3
by edam
- added "all done" message |
714 |
_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTCREATED ); |
1
by edam
Initial import |
715 |
uiInformed = true; |
716 |
} |
|
717 |
} |
|
718 |
||
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
719 |
// if we haven't already shown that we're overwriting or creating a |
720 |
// contact show that we're merging a contact |
|
1
by edam
Initial import |
721 |
if( !uiInformed ) |
3
by edam
- added "all done" message |
722 |
_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTMERGED ); |
1
by edam
Initial import |
723 |
|
724 |
// import contact parts |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
725 |
if( contact.hasNumbers() ) |
726 |
importContactPhones( id, contact.getNumbers() ); |
|
727 |
if( contact.hasEmails() ) |
|
728 |
importContactEmails( id, contact.getEmails() ); |
|
729 |
if( contact.hasAddresses() ) |
|
730 |
importContactAddresses( id, contact.getAddresses() ); |
|
731 |
if( contact.hasOrganisations() ) |
|
732 |
importContactOrganisations( id, contact.getOrganisations() ); |
|
1
by edam
Initial import |
733 |
} |
734 |
||
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
735 |
private void importContactPhones( Long id, |
736 |
HashMap< String, ContactData.PreferredDetail > datas ) |
|
1
by edam
Initial import |
737 |
{ |
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
738 |
// get URI to contact's phones |
739 |
Uri contactPhonesUri = Uri.withAppendedPath( |
|
740 |
ContentUris.withAppendedId( Contacts.People.CONTENT_URI, id ), |
|
741 |
Contacts.People.Phones.CONTENT_DIRECTORY ); |
|
742 |
Set< String > datasKeys = datas.keySet(); |
|
7
by edam
- new contact's phone numebrs and email addresses are added to the caches after those contacts are updated to account for the situation where the same contact is imported again from another file (or the contact exists twice in the same file!?) |
743 |
|
744 |
// add phone numbers |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
745 |
Iterator< String > i = datasKeys.iterator(); |
1
by edam
Initial import |
746 |
while( i.hasNext() ) { |
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
747 |
String number = i.next(); |
748 |
ContactData.PreferredDetail data = datas.get( number ); |
|
1
by edam
Initial import |
749 |
|
750 |
// we don't want to add this number if it's crap, or it already |
|
751 |
// exists (which would cause a duplicate to be created). We don't |
|
752 |
// take in to account the type when checking for duplicates. This is |
|
753 |
// intentional: types aren't really very reliable. We assume that |
|
754 |
// if the number exists at all, it doesn't need importing. Because |
|
755 |
// of this, we also can't update the cache (which we don't need to |
|
756 |
// anyway, so it's not a problem). |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
757 |
if( _contactsCache.hasAssociatedNumber( id, number ) ) |
758 |
continue; |
|
1
by edam
Initial import |
759 |
|
760 |
// add phone number |
|
761 |
ContentValues values = new ContentValues(); |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
762 |
values.put( Contacts.Phones.TYPE, data.getType() ); |
763 |
values.put( Contacts.Phones.NUMBER, number ); |
|
764 |
if( data.isPreferred() ) |
|
765 |
values.put( Contacts.Phones.ISPRIMARY, 1 ); |
|
1
by edam
Initial import |
766 |
_doit.getContentResolver().insert( contactPhonesUri, values ); |
37
by edam
- updated TODO and NEWS |
767 |
|
768 |
// and add this address to the cache to prevent a addition of |
|
769 |
// duplicate date from another file |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
770 |
_contactsCache.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!?) |
771 |
} |
1
by edam
Initial import |
772 |
} |
773 |
||
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
774 |
private void importContactEmails( Long id, |
775 |
HashMap< String, ContactData.PreferredDetail > datas ) |
|
1
by edam
Initial import |
776 |
{ |
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
777 |
// get URI to contact's contact methods |
778 |
Uri contactContactMethodsUri = Uri.withAppendedPath( |
|
779 |
ContentUris.withAppendedId( Contacts.People.CONTENT_URI, id ), |
|
780 |
Contacts.People.ContactMethods.CONTENT_DIRECTORY ); |
|
781 |
Set< String > datasKeys = datas.keySet(); |
|
7
by edam
- new contact's phone numebrs and email addresses are added to the caches after those contacts are updated to account for the situation where the same contact is imported again from another file (or the contact exists twice in the same file!?) |
782 |
|
783 |
// add email addresses |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
784 |
Iterator< String > i = datasKeys.iterator(); |
1
by edam
Initial import |
785 |
while( i.hasNext() ) { |
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
786 |
String email = i.next(); |
787 |
ContactData.PreferredDetail data = datas.get( email ); |
|
1
by edam
Initial import |
788 |
|
37
by edam
- updated TODO and NEWS |
789 |
// we don't want to add this email address if it exists already or |
790 |
// we would introduce duplicates. |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
791 |
if( _contactsCache.hasAssociatedEmail( id, email ) ) |
792 |
continue; |
|
1
by edam
Initial import |
793 |
|
794 |
// add phone number |
|
795 |
ContentValues values = new ContentValues(); |
|
796 |
values.put( Contacts.ContactMethods.KIND, Contacts.KIND_EMAIL ); |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
797 |
values.put( Contacts.ContactMethods.DATA, email ); |
798 |
values.put( Contacts.ContactMethods.TYPE, data.getType() ); |
|
799 |
if( data.isPreferred() ) |
|
1
by edam
Initial import |
800 |
values.put( Contacts.ContactMethods.ISPRIMARY, 1 ); |
801 |
_doit.getContentResolver().insert( contactContactMethodsUri, |
|
36
by edam
- formatting: removed some double-indents on overrunning lines |
802 |
values ); |
37
by edam
- updated TODO and NEWS |
803 |
|
804 |
// and add this address to the cache to prevent a addition of |
|
805 |
// duplicate date from another file |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
806 |
_contactsCache.addAssociatedEmail( id, email ); |
1
by edam
Initial import |
807 |
} |
37
by edam
- updated TODO and NEWS |
808 |
} |
809 |
||
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
810 |
private void importContactAddresses( Long id, |
811 |
HashMap< String, ContactData.TypeDetail > datas ) |
|
37
by edam
- updated TODO and NEWS |
812 |
{ |
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
813 |
// get URI to contact's contact methods |
814 |
Uri contactContactMethodsUri = Uri.withAppendedPath( |
|
815 |
ContentUris.withAppendedId( Contacts.People.CONTENT_URI, id ), |
|
816 |
Contacts.People.ContactMethods.CONTENT_DIRECTORY ); |
|
37
by edam
- updated TODO and NEWS |
817 |
|
818 |
// add addresses |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
819 |
Set< String > datasKeys = datas.keySet(); |
820 |
Iterator< String > i = datasKeys.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!?) |
821 |
while( i.hasNext() ) { |
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
822 |
String address = i.next(); |
823 |
ContactData.TypeDetail data = datas.get( address ); |
|
37
by edam
- updated TODO and NEWS |
824 |
|
825 |
// we don't want to add this address if it exists already or we |
|
826 |
// would introduce duplicates |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
827 |
if( _contactsCache.hasAssociatedAddress( id, address ) ) |
37
by edam
- updated TODO and NEWS |
828 |
continue; |
829 |
||
830 |
// add postal address |
|
831 |
ContentValues values = new ContentValues(); |
|
832 |
values.put( Contacts.ContactMethods.KIND, Contacts.KIND_POSTAL ); |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
833 |
values.put( Contacts.ContactMethods.DATA, address ); |
834 |
values.put( Contacts.ContactMethods.TYPE, data.getType() ); |
|
37
by edam
- updated TODO and NEWS |
835 |
_doit.getContentResolver().insert( contactContactMethodsUri, |
836 |
values ); |
|
837 |
||
838 |
// and add this address to the cache to prevent a addition of |
|
839 |
// duplicate date from another file |
|
40
by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works |
840 |
_contactsCache.addAssociatedAddress( id, address ); |
841 |
} |
|
842 |
} |
|
843 |
||
844 |
private void importContactOrganisations( Long id, |
|
845 |
HashMap< String, ContactData.ExtraDetail > datas ) |
|
846 |
{ |
|
847 |
// add addresses |
|
848 |
Set< String > datasKeys = datas.keySet(); |
|
849 |
Iterator< String > i = datasKeys.iterator(); |
|
850 |
while( i.hasNext() ) { |
|
851 |
String organisation = i.next(); |
|
852 |
ContactData.ExtraDetail data = datas.get( organisation ); |
|
853 |
||
854 |
// we don't want to add this address if it exists already or we |
|
855 |
// would introduce duplicates |
|
856 |
if( _contactsCache.hasAssociatedOrganisation( id, organisation ) ) |
|
857 |
continue; |
|
858 |
||
859 |
// add organisation address |
|
860 |
ContentValues values = new ContentValues(); |
|
861 |
values.put( Contacts.Organizations.PERSON_ID, id ); |
|
862 |
values.put( Contacts.Organizations.COMPANY, organisation ); |
|
863 |
values.put( Contacts.ContactMethods.TYPE, |
|
864 |
Contacts.OrganizationColumns.TYPE_WORK ); |
|
865 |
if( data.getExtra() != null ) |
|
866 |
values.put( Contacts.Organizations.TITLE, data.getExtra() ); |
|
867 |
_doit.getContentResolver().insert( |
|
868 |
Contacts.Organizations.CONTENT_URI, values ); |
|
869 |
||
870 |
// and add this address to the cache to prevent a addition of |
|
871 |
// duplicate date from another file |
|
872 |
_contactsCache.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!?) |
873 |
} |
1
by edam
Initial import |
874 |
} |
875 |
||
3
by edam
- added "all done" message |
876 |
synchronized protected void checkAbort() throws AbortImportException |
1
by edam
Initial import |
877 |
{ |
878 |
if( _abort ) { |
|
879 |
// stop |
|
880 |
throw new AbortImportException(); |
|
881 |
} |
|
882 |
} |
|
883 |
} |