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