bzr branch
http://bzr.ed.am/android/import-contacts
6
by edam
- added GPL header comments to all files |
1 |
/* |
2 |
* Importer.java |
|
3 |
* |
|
4 |
* Copyright (C) 2009 Tim Marston <edam@waxworlds.org> |
|
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.HashSet; |
|
28 |
import java.util.Iterator; |
|
29 |
import java.util.Set; |
|
30 |
import java.util.regex.Matcher; |
|
31 |
import java.util.regex.Pattern; |
|
32 |
||
33 |
import android.content.ContentUris; |
|
34 |
import android.content.ContentValues; |
|
35 |
import android.content.SharedPreferences; |
|
36 |
import android.database.Cursor; |
|
37 |
import android.net.Uri; |
|
38 |
import android.os.Message; |
|
39 |
import android.provider.Contacts; |
|
40 |
||
41 |
public class Importer extends Thread |
|
42 |
{ |
|
3
by edam
- added "all done" message |
43 |
public final static int ACTION_ABORT = 1; |
44 |
public final static int ACTION_ALLDONE = 2; |
|
1
by edam
Initial import |
45 |
|
46 |
public final static int RESPONSE_NEGATIVE = 0; |
|
47 |
public final static int RESPONSE_POSITIVE = 1; |
|
48 |
||
49 |
public final static int RESPONSEEXTRA_NONE = 0; |
|
50 |
public final static int RESPONSEEXTRA_ALWAYS = 1; |
|
51 |
||
52 |
private Doit _doit; |
|
53 |
private int _response; |
|
54 |
private int _responseExtra; |
|
55 |
private HashMap< String, Long > _contacts; |
|
56 |
private HashMap< Long, HashSet< String > > _contactNumbers; |
|
57 |
private HashMap< Long, HashSet< String > > _contactEmails; |
|
58 |
private int _mergeSetting; |
|
59 |
private int _lastMergeDecision; |
|
60 |
private boolean _abort = false; |
|
3
by edam
- added "all done" message |
61 |
private boolean _isFinished = false; |
1
by edam
Initial import |
62 |
|
63 |
public class ContactData |
|
64 |
{ |
|
65 |
class PhoneData |
|
66 |
{ |
|
67 |
public String _number; |
|
68 |
public int _type; |
|
69 |
public boolean _isPreferred; |
|
70 |
||
71 |
public PhoneData( String number, int type, boolean isPreferred ) { |
|
72 |
_number = number; |
|
73 |
_type = type; |
|
74 |
_isPreferred = isPreferred; |
|
75 |
} |
|
76 |
||
77 |
public String getNumber() { |
|
78 |
return _number; |
|
79 |
} |
|
80 |
||
81 |
public int getType() { |
|
82 |
return _type; |
|
83 |
} |
|
84 |
||
85 |
public boolean isPreferred() { |
|
86 |
return _isPreferred; |
|
87 |
} |
|
88 |
} |
|
89 |
||
90 |
class EmailData |
|
91 |
{ |
|
92 |
private String _email; |
|
93 |
public int _type; |
|
94 |
private boolean _isPreferred; |
|
95 |
||
96 |
public EmailData( String email, int type, boolean isPreferred ) { |
|
97 |
_email = email; |
|
98 |
_type = type; |
|
99 |
_isPreferred = isPreferred; |
|
100 |
} |
|
101 |
||
102 |
public String getAddress() { |
|
103 |
return _email; |
|
104 |
} |
|
105 |
||
106 |
public int getType() { |
|
107 |
return _type; |
|
108 |
} |
|
109 |
||
110 |
public boolean isPreferred() { |
|
111 |
return _isPreferred; |
|
112 |
} |
|
113 |
} |
|
114 |
||
115 |
public String _name = null; |
|
116 |
public HashMap< String, PhoneData > _phones = null; |
|
117 |
public HashMap< String, EmailData > _emails = null; |
|
118 |
||
119 |
protected void setName( String name ) |
|
120 |
{ |
|
121 |
_name = name; |
|
122 |
} |
|
123 |
||
124 |
public String getName() |
|
125 |
{ |
|
126 |
return _name; |
|
127 |
} |
|
128 |
||
129 |
protected void addPhone( String number, int type, boolean isPreferred ) |
|
130 |
{ |
|
131 |
if( _phones == null ) _phones = new HashMap< String, PhoneData >(); |
|
132 |
if( !_phones.containsKey( number ) ) |
|
133 |
_phones.put( number, |
|
134 |
new PhoneData( number, type, isPreferred ) ); |
|
135 |
} |
|
136 |
||
137 |
protected void addEmail( String email, int type, boolean isPreferred ) |
|
138 |
{ |
|
139 |
if( _emails == null ) _emails = new HashMap< String, EmailData >(); |
|
140 |
if( !_emails.containsKey( email ) ) |
|
141 |
_emails.put( email, new EmailData( email, type, isPreferred ) ); |
|
142 |
} |
|
143 |
} |
|
144 |
||
14
by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one) |
145 |
@SuppressWarnings("serial") |
1
by edam
Initial import |
146 |
protected class AbortImportException extends Exception { }; |
147 |
||
148 |
public Importer( Doit doit ) |
|
149 |
{ |
|
150 |
_doit = doit; |
|
151 |
||
152 |
SharedPreferences prefs = getSharedPreferences(); |
|
9
by edam
- added scroll view to all layouts |
153 |
_mergeSetting = prefs.getInt( "merge_setting", Doit.ACTION_PROMPT ); |
1
by edam
Initial import |
154 |
} |
155 |
||
156 |
@Override |
|
157 |
public void run() |
|
158 |
{ |
|
159 |
try |
|
160 |
{ |
|
161 |
// cache current contact names |
|
162 |
buildContactsCache(); |
|
163 |
||
164 |
// do the import |
|
165 |
onImport(); |
|
166 |
||
167 |
// done! |
|
3
by edam
- added "all done" message |
168 |
finish( ACTION_ALLDONE ); |
1
by edam
Initial import |
169 |
} |
170 |
catch( AbortImportException e ) |
|
171 |
{} |
|
3
by edam
- added "all done" message |
172 |
|
173 |
// flag as finished to prevent interrupts |
|
174 |
setIsFinished(); |
|
175 |
} |
|
176 |
||
177 |
synchronized private void setIsFinished() |
|
178 |
{ |
|
179 |
_isFinished = true; |
|
1
by edam
Initial import |
180 |
} |
181 |
||
182 |
protected void onImport() throws AbortImportException |
|
183 |
{ |
|
184 |
} |
|
185 |
||
186 |
public void wake() |
|
187 |
{ |
|
188 |
wake( 0, RESPONSEEXTRA_NONE ); |
|
189 |
} |
|
190 |
||
191 |
public void wake( int response ) |
|
192 |
{ |
|
193 |
wake( response, RESPONSEEXTRA_NONE ); |
|
194 |
} |
|
195 |
||
196 |
synchronized public void wake( int response, int responseExtra ) |
|
197 |
{ |
|
198 |
_response = response; |
|
199 |
_responseExtra = responseExtra; |
|
200 |
notify(); |
|
201 |
} |
|
202 |
||
3
by edam
- added "all done" message |
203 |
synchronized public boolean setAbort() |
1
by edam
Initial import |
204 |
{ |
3
by edam
- added "all done" message |
205 |
if( !_isFinished && !_abort ) { |
206 |
_abort = true; |
|
207 |
notify(); |
|
208 |
return true; |
|
209 |
} |
|
210 |
return false; |
|
1
by edam
Initial import |
211 |
} |
212 |
||
213 |
protected SharedPreferences getSharedPreferences() |
|
214 |
{ |
|
215 |
return _doit.getSharedPreferences(); |
|
216 |
} |
|
217 |
||
218 |
protected void showError( int res ) throws AbortImportException |
|
219 |
{ |
|
220 |
showError( _doit.getText( res ).toString() ); |
|
221 |
} |
|
222 |
||
223 |
synchronized protected void showError( String message ) |
|
224 |
throws AbortImportException |
|
225 |
{ |
|
226 |
checkAbort(); |
|
227 |
_doit._handler.sendMessage( Message.obtain( |
|
3
by edam
- added "all done" message |
228 |
_doit._handler, Doit.MESSAGE_ERROR, message ) ); |
1
by edam
Initial import |
229 |
try { |
230 |
wait(); |
|
231 |
} |
|
232 |
catch( InterruptedException e ) { } |
|
14
by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one) |
233 |
|
3
by edam
- added "all done" message |
234 |
// no need to check if an abortion happened during the wait, we are |
235 |
// about to finish anyway! |
|
236 |
finish( ACTION_ABORT ); |
|
1
by edam
Initial import |
237 |
} |
238 |
||
239 |
protected void showFatalError( int res ) throws AbortImportException |
|
240 |
{ |
|
241 |
showFatalError( _doit.getText( res ).toString() ); |
|
242 |
} |
|
243 |
||
244 |
synchronized protected void showFatalError( String message ) |
|
245 |
throws AbortImportException |
|
246 |
{ |
|
247 |
checkAbort(); |
|
248 |
_doit._handler.sendMessage( Message.obtain( |
|
3
by edam
- added "all done" message |
249 |
_doit._handler, Doit.MESSAGE_ERROR, message ) ); |
1
by edam
Initial import |
250 |
try { |
251 |
wait(); |
|
252 |
} |
|
253 |
catch( InterruptedException e ) { } |
|
14
by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one) |
254 |
|
3
by edam
- added "all done" message |
255 |
// no need to check if an abortion happened during the wait, we are |
256 |
// about to finish anyway! |
|
257 |
finish( ACTION_ABORT ); |
|
1
by edam
Initial import |
258 |
} |
259 |
||
260 |
protected boolean showContinue( int res ) throws AbortImportException |
|
261 |
{ |
|
262 |
return showContinue( _doit.getText( res ).toString() ); |
|
263 |
} |
|
264 |
||
265 |
synchronized protected boolean showContinue( String message ) |
|
266 |
throws AbortImportException |
|
267 |
{ |
|
268 |
checkAbort(); |
|
269 |
_doit._handler.sendMessage( Message.obtain( |
|
3
by edam
- added "all done" message |
270 |
_doit._handler, Doit.MESSAGE_CONTINUEORABORT, message ) ); |
1
by edam
Initial import |
271 |
try { |
272 |
wait(); |
|
273 |
} |
|
274 |
catch( InterruptedException e ) { } |
|
3
by edam
- added "all done" message |
275 |
|
276 |
// check if an abortion happened during the wait |
|
277 |
checkAbort(); |
|
278 |
||
1
by edam
Initial import |
279 |
return _response == RESPONSE_POSITIVE; |
280 |
} |
|
281 |
||
282 |
protected void setProgressMessage( int res ) throws AbortImportException |
|
283 |
{ |
|
284 |
checkAbort(); |
|
285 |
_doit._handler.sendMessage( Message.obtain( _doit._handler, |
|
3
by edam
- added "all done" message |
286 |
Doit.MESSAGE_SETPROGRESSMESSAGE, getText( res ) ) ); |
1
by edam
Initial import |
287 |
} |
288 |
||
289 |
protected void setProgressMax( int maxProgress ) |
|
290 |
throws AbortImportException |
|
291 |
{ |
|
292 |
checkAbort(); |
|
293 |
_doit._handler.sendMessage( Message.obtain( |
|
3
by edam
- added "all done" message |
294 |
_doit._handler, Doit.MESSAGE_SETMAXPROGRESS, |
1
by edam
Initial import |
295 |
new Integer( maxProgress ) ) ); |
296 |
} |
|
297 |
||
298 |
protected void setTmpProgress( int tmpProgress ) throws AbortImportException |
|
299 |
{ |
|
300 |
checkAbort(); |
|
301 |
_doit._handler.sendMessage( Message.obtain( |
|
3
by edam
- added "all done" message |
302 |
_doit._handler, Doit.MESSAGE_SETTMPPROGRESS, |
1
by edam
Initial import |
303 |
new Integer( tmpProgress ) ) ); |
304 |
} |
|
305 |
||
306 |
protected void setProgress( int progress ) throws AbortImportException |
|
307 |
{ |
|
308 |
checkAbort(); |
|
309 |
_doit._handler.sendMessage( Message.obtain( |
|
3
by edam
- added "all done" message |
310 |
_doit._handler, Doit.MESSAGE_SETPROGRESS, |
1
by edam
Initial import |
311 |
new Integer( progress ) ) ); |
312 |
} |
|
313 |
||
3
by edam
- added "all done" message |
314 |
protected void finish( int action ) throws AbortImportException |
1
by edam
Initial import |
315 |
{ |
3
by edam
- added "all done" message |
316 |
// update UI to reflect action |
317 |
int message; |
|
318 |
switch( action ) |
|
319 |
{ |
|
14
by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one) |
320 |
case ACTION_ALLDONE: message = Doit.MESSAGE_ALLDONE; break; |
3
by edam
- added "all done" message |
321 |
default: // fall through |
14
by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one) |
322 |
case ACTION_ABORT: message = Doit.MESSAGE_ABORT; break; |
3
by edam
- added "all done" message |
323 |
} |
324 |
_doit._handler.sendEmptyMessage( message ); |
|
1
by edam
Initial import |
325 |
|
3
by edam
- added "all done" message |
326 |
// stop |
327 |
throw new AbortImportException(); |
|
1
by edam
Initial import |
328 |
} |
329 |
||
330 |
protected CharSequence getText( int res ) |
|
331 |
{ |
|
332 |
return _doit.getText( res ); |
|
333 |
} |
|
334 |
||
335 |
protected boolean isImportRequired( String name ) |
|
336 |
throws AbortImportException |
|
337 |
{ |
|
338 |
checkAbort(); |
|
339 |
return isImportRequired( name, _mergeSetting ); |
|
340 |
} |
|
341 |
||
3
by edam
- added "all done" message |
342 |
synchronized private boolean isImportRequired( String name, |
343 |
int mergeSetting ) throws AbortImportException |
|
1
by edam
Initial import |
344 |
{ |
345 |
_lastMergeDecision = mergeSetting; |
|
346 |
||
347 |
// handle special cases |
|
348 |
switch( mergeSetting ) |
|
349 |
{ |
|
9
by edam
- added scroll view to all layouts |
350 |
case Doit.ACTION_KEEP: |
1
by edam
Initial import |
351 |
// if we keep contacts on duplicate, we better check for one |
352 |
return !_contacts.containsKey( name ); |
|
353 |
||
9
by edam
- added scroll view to all layouts |
354 |
case Doit.ACTION_PROMPT: |
1
by edam
Initial import |
355 |
// if we are prompting on duplicate, we better check for one |
356 |
if( !_contacts.containsKey( name ) ) |
|
357 |
return true; |
|
358 |
||
359 |
// ok, it exists, so do prompt |
|
360 |
_doit._handler.sendMessage( Message.obtain( |
|
3
by edam
- added "all done" message |
361 |
_doit._handler, Doit.MESSAGE_MERGEPROMPT, name ) ); |
1
by edam
Initial import |
362 |
try { |
363 |
wait(); |
|
364 |
} |
|
365 |
catch( InterruptedException e ) { } |
|
366 |
||
3
by edam
- added "all done" message |
367 |
// check if an abortion happened during the wait |
368 |
checkAbort(); |
|
369 |
||
1
by edam
Initial import |
370 |
// if "always" was selected, make choice permenant |
371 |
if( _responseExtra == RESPONSEEXTRA_ALWAYS ) |
|
372 |
_mergeSetting = _response; |
|
373 |
||
374 |
// recurse, with out new merge setting |
|
375 |
return isImportRequired( name, _response ); |
|
376 |
} |
|
377 |
||
378 |
// for all other cases (either overwriting or merging) we will need the |
|
379 |
// imported data |
|
380 |
return true; |
|
381 |
} |
|
382 |
||
383 |
protected void skipContact() throws AbortImportException |
|
384 |
{ |
|
385 |
checkAbort(); |
|
3
by edam
- added "all done" message |
386 |
_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTSKIPPED ); |
1
by edam
Initial import |
387 |
} |
388 |
||
389 |
protected void importContact( ContactData contact ) |
|
390 |
throws AbortImportException |
|
391 |
{ |
|
392 |
checkAbort(); |
|
393 |
||
3
by edam
- added "all done" message |
394 |
// if( !showContinue( "====[ IMPORTING ]====\n: " + contact._name ) ) |
395 |
// finish( ACTION_ABORT ); |
|
1
by edam
Initial import |
396 |
|
397 |
ContentValues values = new ContentValues(); |
|
398 |
boolean uiInformed = false; |
|
399 |
||
400 |
// does contact exist already? |
|
401 |
Uri contactUri = null; |
|
402 |
Long id; |
|
403 |
if( ( id = (Long)_contacts.get( contact._name ) ) != null ) |
|
404 |
{ |
|
405 |
// should we skip this import altogether? |
|
9
by edam
- added scroll view to all layouts |
406 |
if( _lastMergeDecision == Doit.ACTION_KEEP ) return; |
1
by edam
Initial import |
407 |
|
408 |
// get contact's URI |
|
409 |
contactUri = ContentUris.withAppendedId( |
|
410 |
Contacts.People.CONTENT_URI, id ); |
|
411 |
||
412 |
// should we destroy the existing contact before importing? |
|
9
by edam
- added scroll view to all layouts |
413 |
if( _lastMergeDecision == Doit.ACTION_OVERWRITE ) { |
1
by edam
Initial import |
414 |
_doit.getContentResolver().delete( contactUri, null, null ); |
415 |
contactUri = null; |
|
416 |
||
417 |
// upate the UI |
|
3
by edam
- added "all done" message |
418 |
_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTOVERWRITTEN ); |
1
by edam
Initial import |
419 |
uiInformed = true; |
420 |
||
421 |
// update cache |
|
422 |
_contacts.remove( contact._name ); |
|
423 |
} |
|
424 |
} |
|
425 |
||
426 |
// if we don't have a contact URI it is because the contact never |
|
427 |
// existed or because we deleted it |
|
428 |
if( contactUri == null ) |
|
429 |
{ |
|
430 |
// create a new contact |
|
431 |
values.put( Contacts.People.NAME, contact._name ); |
|
432 |
contactUri = _doit.getContentResolver().insert( |
|
433 |
Contacts.People.CONTENT_URI, values ); |
|
434 |
id = ContentUris.parseId( contactUri ); |
|
435 |
if( id <= 0 ) return; // shouldn't happen! |
|
436 |
||
14
by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one) |
437 |
// try to add them to the "My Contacts" group |
438 |
try { |
|
439 |
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. |
440 |
_doit.getContentResolver(), id ); |
14
by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one) |
441 |
} |
442 |
catch( IllegalStateException e ) { } |
|
1
by edam
Initial import |
443 |
|
444 |
// update cache |
|
445 |
_contacts.put( contact._name, id ); |
|
446 |
||
447 |
// update UI |
|
448 |
if( !uiInformed ) { |
|
3
by edam
- added "all done" message |
449 |
_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTCREATED ); |
1
by edam
Initial import |
450 |
uiInformed = true; |
451 |
} |
|
452 |
} |
|
453 |
||
454 |
// update UI |
|
455 |
if( !uiInformed ) |
|
3
by edam
- added "all done" message |
456 |
_doit._handler.sendEmptyMessage( Doit.MESSAGE_CONTACTMERGED ); |
1
by edam
Initial import |
457 |
|
458 |
// import contact parts |
|
459 |
if( contact._phones != null ) |
|
460 |
importContactPhones( contactUri, contact._phones ); |
|
461 |
if( contact._emails != null ) |
|
462 |
importContactEmails( contactUri, contact._emails ); |
|
463 |
} |
|
464 |
||
465 |
private void importContactPhones( Uri contactUri, |
|
466 |
HashMap< String, ContactData.PhoneData > phones ) |
|
467 |
{ |
|
468 |
Long contactId = ContentUris.parseId( contactUri ); |
|
469 |
Uri contactPhonesUri = Uri.withAppendedPath( contactUri, |
|
470 |
Contacts.People.Phones.CONTENT_DIRECTORY ); |
|
14
by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one) |
471 |
Set< String > phonesKeys = phones.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!?) |
472 |
|
473 |
// add phone numbers |
|
14
by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one) |
474 |
Iterator< String > i = phonesKeys.iterator(); |
1
by edam
Initial import |
475 |
while( i.hasNext() ) { |
476 |
ContactData.PhoneData phone = phones.get( i.next() ); |
|
477 |
||
478 |
// we don't want to add this number if it's crap, or it already |
|
479 |
// exists (which would cause a duplicate to be created). We don't |
|
480 |
// take in to account the type when checking for duplicates. This is |
|
481 |
// intentional: types aren't really very reliable. We assume that |
|
482 |
// if the number exists at all, it doesn't need importing. Because |
|
483 |
// of this, we also can't update the cache (which we don't need to |
|
484 |
// anyway, so it's not a problem). |
|
485 |
String number = sanitisePhoneNumber( phone._number ); |
|
486 |
if( number == null ) continue; |
|
487 |
HashSet< String > numbers = _contactNumbers.get( contactId ); |
|
488 |
if( numbers != null && numbers.contains( number ) ) continue; |
|
489 |
||
490 |
// add phone number |
|
491 |
ContentValues values = new ContentValues(); |
|
492 |
values.put( Contacts.Phones.TYPE, phone._type ); |
|
493 |
values.put( Contacts.Phones.NUMBER, phone._number ); |
|
494 |
if( phone._isPreferred ) values.put( Contacts.Phones.ISPRIMARY, 1 ); |
|
495 |
_doit.getContentResolver().insert( contactPhonesUri, values ); |
|
496 |
} |
|
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!?) |
497 |
|
498 |
// now add those phone numbers to the cache to prevent the addition of |
|
499 |
// duplicate data from another file |
|
500 |
i = phonesKeys.iterator(); |
|
501 |
while( i.hasNext() ) { |
|
502 |
ContactData.PhoneData phone = phones.get( i.next() ); |
|
503 |
||
504 |
String number = sanitisePhoneNumber( phone._number ); |
|
505 |
if( number != null ) { |
|
506 |
HashSet< String > numbers = _contactNumbers.get( contactId ); |
|
507 |
if( numbers == null ) { |
|
508 |
_contactNumbers.put( contactId, new HashSet< String >() ); |
|
509 |
numbers = _contactNumbers.get( contactId ); |
|
510 |
} |
|
511 |
numbers.add( number ); |
|
512 |
} |
|
513 |
} |
|
1
by edam
Initial import |
514 |
} |
515 |
||
516 |
private void importContactEmails( Uri contactUri, |
|
517 |
HashMap< String, ContactData.EmailData > emails ) |
|
518 |
{ |
|
519 |
Long contactId = ContentUris.parseId( contactUri ); |
|
520 |
Uri contactContactMethodsUri = Uri.withAppendedPath( contactUri, |
|
521 |
Contacts.People.ContactMethods.CONTENT_DIRECTORY ); |
|
14
by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one) |
522 |
Set< String > emailsKeys = emails.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!?) |
523 |
|
524 |
// add email addresses |
|
14
by edam
- got rid of the pretend ImportContacts activity alltogether (and made the Intro activity the startup one) |
525 |
Iterator< String > i = emailsKeys.iterator(); |
1
by edam
Initial import |
526 |
while( i.hasNext() ) { |
527 |
ContactData.EmailData email = emails.get( i.next() ); |
|
528 |
||
529 |
// like with phone numbers, we don't want to add this email address |
|
530 |
// if it exists already or we would introduce duplicates. |
|
531 |
String address = sanitiseEmailAddress( email.getAddress() ); |
|
532 |
if( address == null ) continue; |
|
533 |
HashSet< String > addresses = _contactEmails.get( contactId ); |
|
534 |
if( addresses != null && addresses.contains( address ) ) continue; |
|
535 |
||
536 |
// add phone number |
|
537 |
ContentValues values = new ContentValues(); |
|
538 |
values.put( Contacts.ContactMethods.KIND, Contacts.KIND_EMAIL ); |
|
539 |
values.put( Contacts.ContactMethods.DATA, email.getAddress() ); |
|
540 |
values.put( Contacts.ContactMethods.TYPE, email.getType() ); |
|
541 |
if( email.isPreferred() ) |
|
542 |
values.put( Contacts.ContactMethods.ISPRIMARY, 1 ); |
|
543 |
_doit.getContentResolver().insert( contactContactMethodsUri, |
|
544 |
values ); |
|
545 |
} |
|
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!?) |
546 |
|
547 |
// now add those email addresses to the cache to prevent the addition of |
|
548 |
// duplicate data from another file |
|
549 |
i = emailsKeys.iterator(); |
|
550 |
while( i.hasNext() ) { |
|
551 |
ContactData.EmailData email = emails.get( i.next() ); |
|
552 |
||
553 |
String address = sanitiseEmailAddress( email.getAddress() ); |
|
554 |
if( address != null ) { |
|
555 |
HashSet< String > addresses = _contactEmails.get( contactId ); |
|
556 |
if( addresses == null ) { |
|
557 |
_contactEmails.put( contactId, new HashSet< String >() ); |
|
558 |
addresses = _contactEmails.get( contactId ); |
|
559 |
} |
|
560 |
addresses.add( address ); |
|
561 |
} |
|
562 |
} |
|
1
by edam
Initial import |
563 |
} |
564 |
||
3
by edam
- added "all done" message |
565 |
synchronized protected void checkAbort() throws AbortImportException |
1
by edam
Initial import |
566 |
{ |
567 |
if( _abort ) { |
|
568 |
// stop |
|
569 |
throw new AbortImportException(); |
|
570 |
} |
|
571 |
} |
|
572 |
||
573 |
private void buildContactsCache() throws AbortImportException |
|
574 |
{ |
|
575 |
// update UI |
|
576 |
setProgressMessage( R.string.doit_caching ); |
|
577 |
||
578 |
String[] cols; |
|
579 |
Cursor cur; |
|
580 |
||
581 |
// init contacts caches |
|
582 |
_contacts = new HashMap< String, Long >(); |
|
583 |
_contactNumbers = new HashMap< Long, HashSet< String > >(); |
|
584 |
_contactEmails = new HashMap< Long, HashSet< String > >(); |
|
585 |
||
586 |
// query and store map of contact names to ids |
|
587 |
cols = new String[] { Contacts.People._ID, Contacts.People.NAME }; |
|
588 |
cur = _doit.managedQuery( Contacts.People.CONTENT_URI, |
|
589 |
cols, null, null, null); |
|
590 |
if( cur.moveToFirst() ) { |
|
591 |
int idCol = cur.getColumnIndex( Contacts.People._ID ); |
|
592 |
int nameCol = cur.getColumnIndex( Contacts.People.NAME ); |
|
593 |
do { |
|
594 |
_contacts.put( cur.getString( nameCol ), cur.getLong( idCol ) ); |
|
595 |
} while( cur.moveToNext() ); |
|
596 |
} |
|
597 |
||
598 |
// query and store map of contact ids to sets of phone numbers |
|
599 |
cols = new String[] { Contacts.Phones.PERSON_ID, |
|
600 |
Contacts.Phones.NUMBER }; |
|
601 |
cur = _doit.managedQuery( Contacts.Phones.CONTENT_URI, |
|
602 |
cols, null, null, null); |
|
603 |
if( cur.moveToFirst() ) { |
|
604 |
int personIdCol = cur.getColumnIndex( Contacts.Phones.PERSON_ID ); |
|
605 |
int numberCol = cur.getColumnIndex( Contacts.Phones.NUMBER ); |
|
606 |
do { |
|
607 |
Long id = cur.getLong( personIdCol ); |
|
608 |
String number = sanitisePhoneNumber( |
|
609 |
cur.getString( numberCol ) ); |
|
610 |
if( number != null ) { |
|
611 |
HashSet< String > numbers = _contactNumbers.get( id ); |
|
612 |
if( numbers == null ) { |
|
613 |
_contactNumbers.put( id, new HashSet< String >() ); |
|
614 |
numbers = _contactNumbers.get( id ); |
|
615 |
} |
|
616 |
numbers.add( number ); |
|
617 |
} |
|
618 |
} while( cur.moveToNext() ); |
|
619 |
} |
|
620 |
||
621 |
// query and store map of contact ids to sets of email addresses |
|
622 |
cols = new String[] { Contacts.ContactMethods.PERSON_ID, |
|
623 |
Contacts.ContactMethods.DATA }; |
|
624 |
cur = _doit.managedQuery( Contacts.ContactMethods.CONTENT_URI, |
|
625 |
cols, Contacts.ContactMethods.KIND + " = ?", |
|
626 |
new String[] { "" + Contacts.KIND_EMAIL }, null ); |
|
627 |
if( cur.moveToFirst() ) { |
|
628 |
int personIdCol = cur.getColumnIndex( |
|
629 |
Contacts.ContactMethods.PERSON_ID ); |
|
630 |
int addressCol = cur.getColumnIndex( |
|
631 |
Contacts.ContactMethods.DATA ); |
|
632 |
do { |
|
633 |
Long id = cur.getLong( personIdCol ); |
|
634 |
String address = sanitiseEmailAddress( |
|
635 |
cur.getString( addressCol ) ); |
|
636 |
if( address != null ) { |
|
637 |
HashSet< String > addresses = _contactEmails.get( id ); |
|
638 |
if( addresses == null ) { |
|
639 |
_contactEmails.put( id, new HashSet< String >() ); |
|
640 |
addresses = _contactEmails.get( id ); |
|
641 |
} |
|
642 |
addresses.add( address ); |
|
643 |
} |
|
644 |
} while( cur.moveToNext() ); |
|
645 |
} |
|
646 |
} |
|
647 |
||
648 |
private String sanitisePhoneNumber( String number ) |
|
649 |
{ |
|
650 |
number = number.replaceAll( "[-\\(\\) ]", "" ); |
|
651 |
Pattern p = Pattern.compile( "^\\+?[0-9]+" ); |
|
652 |
Matcher m = p.matcher( number ); |
|
653 |
if( m.lookingAt() ) return m.group( 0 ); |
|
654 |
return null; |
|
655 |
} |
|
656 |
||
657 |
private String sanitiseEmailAddress( String address ) |
|
658 |
{ |
|
659 |
address = address.trim(); |
|
660 |
Pattern p = Pattern.compile( |
|
661 |
"^[^ @]+@[a-zA-Z]([-a-zA-Z0-9]*[a-zA-z0-9])?(\\.[a-zA-Z]([-a-zA-Z0-9]*[a-zA-z0-9])?)+$" ); |
|
662 |
Matcher m = p.matcher( address ); |
|
663 |
if( m.matches() ) { |
|
664 |
String[] bits = address.split( "@" ); |
|
665 |
return bits[ 0 ] + "@" + bits[ 1 ].toLowerCase(); |
|
666 |
} |
|
667 |
return null; |
|
668 |
} |
|
669 |
} |