/android/import-contacts

To get this branch, use:
bzr branch http://bzr.ed.am/android/import-contacts

« back to all changes in this revision

Viewing changes to src/am/ed/importcontacts/ContactsContractBackend.java

  • Committer: edam
  • Date: 2009-01-13 06:35:26 UTC
  • Revision ID: edam@waxworlds.org-20090113063526-l9t1s9git4bav60a
- 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!?)

Show diffs side-by-side

added added

removed removed

1
 
/*
2
 
 * ContactsBackend.java
3
 
 *
4
 
 * Copyright (C) 2012 Tim Marston <tim@ed.am>
5
 
 *
6
 
 * This file is part of the Import Contacts program (hereafter referred
7
 
 * to as "this program"). For more information, see
8
 
 * http://ed.am/dev/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
 
 
24
 
package am.ed.importcontacts;
25
 
 
26
 
import java.util.HashMap;
27
 
import java.util.HashSet;
28
 
 
29
 
import am.ed.importcontacts.ContactsCache.CacheIdentifier;
30
 
import am.ed.importcontacts.Importer.ContactData;
31
 
import android.annotation.TargetApi;
32
 
import android.app.Activity;
33
 
import android.content.ContentUris;
34
 
import android.content.ContentValues;
35
 
import android.database.Cursor;
36
 
import android.net.Uri;
37
 
import android.provider.ContactsContract;
38
 
import android.provider.ContactsContract.CommonDataKinds;
39
 
 
40
 
@TargetApi(5)
41
 
public class ContactsContractBackend implements Backend
42
 
{
43
 
        private Activity _activity = null;
44
 
        private HashMap< Long, Long > _aggregate_to_raw_ids = null;
45
 
 
46
 
        ContactsContractBackend( Activity activity )
47
 
        {
48
 
                _activity = activity;
49
 
                _aggregate_to_raw_ids = new HashMap< Long, Long >();
50
 
        }
51
 
 
52
 
        @Override
53
 
        public void populateCache( ContactsCache cache )
54
 
        {
55
 
                Cursor cur;
56
 
 
57
 
                // build a set of aggregate contact ids that haven't been added to the
58
 
                // cache yet
59
 
                HashSet< Long > unadded_ids = new HashSet< Long >();
60
 
                cur = _activity.managedQuery( ContactsContract.Contacts.CONTENT_URI,
61
 
                        new String[] {
62
 
                                ContactsContract.Contacts._ID,
63
 
                        }, null, null, null );
64
 
                while( cur.moveToNext() ) {
65
 
                        Long id = cur.getLong(
66
 
                                cur.getColumnIndex( ContactsContract.Contacts._ID ) );
67
 
                        unadded_ids.add( id );
68
 
                }
69
 
 
70
 
                // build a mapping of the ids of raw contacts to the ids of their
71
 
                // aggregate contacts
72
 
                HashMap< Long, Long > raw_to_aggregate_ids =
73
 
                        new HashMap< Long, Long >();
74
 
                cur = _activity.managedQuery( ContactsContract.RawContacts.CONTENT_URI,
75
 
                        new String[] {
76
 
                                ContactsContract.RawContacts._ID,
77
 
                                ContactsContract.RawContacts.CONTACT_ID,
78
 
                        }, ContactsContract.RawContacts.DELETED + " != 0", null, null );
79
 
                while( cur.moveToNext() ) {
80
 
                        Long raw_id = cur.getLong(
81
 
                                cur.getColumnIndex( ContactsContract.RawContacts._ID ) );
82
 
                        Long id = cur.getLong(
83
 
                                cur.getColumnIndex( ContactsContract.RawContacts.CONTACT_ID ) );
84
 
                        raw_to_aggregate_ids.put( raw_id, id );
85
 
                }
86
 
 
87
 
                // get structured names, primary ones first
88
 
                cur = _activity.managedQuery( ContactsContract.Data.CONTENT_URI,
89
 
                        new String[] {
90
 
                                ContactsContract.Data.RAW_CONTACT_ID,
91
 
                                CommonDataKinds.StructuredName.DISPLAY_NAME,
92
 
                        },
93
 
                        ContactsContract.Data.MIMETYPE + " = '" +
94
 
                                CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE + "'",
95
 
                        null, ContactsContract.Data.IS_PRIMARY + " DESC" );
96
 
                while( cur.moveToNext() ) {
97
 
                        Long raw_id = cur.getLong( cur.getColumnIndex(
98
 
                                ContactsContract.Data.RAW_CONTACT_ID ) );
99
 
                        Long id = raw_to_aggregate_ids.get( raw_id );
100
 
                        String name = ContactsCache.normaliseName(
101
 
                                cur.getString( cur.getColumnIndex(
102
 
                                        CommonDataKinds.StructuredName.DISPLAY_NAME ) ) );
103
 
                        if( name != null && id != null )
104
 
                        {
105
 
                                // if this is a name for a contact for whom we have not added a
106
 
                                // lookup, add a lookup for the contact id by name
107
 
                                if( unadded_ids.contains( id ) ) {
108
 
                                        cache.addLookup( new CacheIdentifier(
109
 
                                                CacheIdentifier.Type.NAME, name ), id );
110
 
                                        unadded_ids.remove( id );
111
 
                                }
112
 
                        }
113
 
                }
114
 
 
115
 
                // get contact organisations, primary ones first
116
 
                cur = _activity.managedQuery( ContactsContract.Data.CONTENT_URI,
117
 
                        new String[] {
118
 
                                ContactsContract.Data.RAW_CONTACT_ID,
119
 
                                CommonDataKinds.Organization.COMPANY,
120
 
                        },
121
 
                        ContactsContract.Data.MIMETYPE + " = '" +
122
 
                                CommonDataKinds.Organization.CONTENT_ITEM_TYPE + "'",
123
 
                        null, ContactsContract.Data.IS_PRIMARY + " DESC" );
124
 
                while( cur.moveToNext() ) {
125
 
                        Long raw_id = cur.getLong( cur.getColumnIndex(
126
 
                                ContactsContract.Data.RAW_CONTACT_ID ) );
127
 
                        Long id = raw_to_aggregate_ids.get( raw_id );
128
 
                        String organisation = ContactsCache.normaliseOrganisation(
129
 
                                cur.getString( cur.getColumnIndex(
130
 
                                        CommonDataKinds.Organization.COMPANY ) ) );
131
 
                        if( organisation != null && id != null )
132
 
                        {
133
 
                                // if this is an organisation name for a contact for whom we
134
 
                                // have not added a lookup, add a lookup for the contact id
135
 
                                // by organisation
136
 
                                if( unadded_ids.contains( id ) ) {
137
 
                                        cache.addLookup( new CacheIdentifier(
138
 
                                                CacheIdentifier.Type.ORGANISATION, organisation ), id );
139
 
                                        unadded_ids.remove( id );
140
 
                                }
141
 
 
142
 
                                // add associated data
143
 
                                cache.addAssociatedOrganisation( id, organisation );
144
 
                        }
145
 
                }
146
 
 
147
 
                // get all phone numbers, primary ones first
148
 
                cur = _activity.managedQuery( ContactsContract.Data.CONTENT_URI,
149
 
                        new String[] {
150
 
                                ContactsContract.Data.RAW_CONTACT_ID,
151
 
                                CommonDataKinds.Phone.NUMBER,
152
 
                        },
153
 
                        ContactsContract.Data.MIMETYPE + " = '" +
154
 
                                CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "'",
155
 
                        null, ContactsContract.Data.IS_PRIMARY + " DESC" );
156
 
                while( cur.moveToNext() ) {
157
 
                        Long raw_id = cur.getLong( cur.getColumnIndex(
158
 
                                ContactsContract.Data.RAW_CONTACT_ID ) );
159
 
                        Long id = raw_to_aggregate_ids.get( raw_id );
160
 
                        String number = ContactsCache.normalisePhoneNumber(
161
 
                                cur.getString( cur.getColumnIndex(
162
 
                                        CommonDataKinds.Phone.NUMBER ) ) );
163
 
                        if( number != null && id != null )
164
 
                        {
165
 
                                // if this is a number for a contact for whom we have not
166
 
                                // added a lookup, add a lookup for the contact id by phone
167
 
                                // number
168
 
                                if( unadded_ids.contains( id ) ) {
169
 
                                        cache.addLookup( new CacheIdentifier(
170
 
                                                CacheIdentifier.Type.PRIMARY_NUMBER, number ), id );
171
 
                                        unadded_ids.remove( id );
172
 
                                }
173
 
 
174
 
                                // add associated data
175
 
                                cache.addAssociatedNumber( id, number );
176
 
                        }
177
 
                }
178
 
 
179
 
                // get all email addresses, primary ones first
180
 
                cur = _activity.managedQuery( ContactsContract.Data.CONTENT_URI,
181
 
                        new String[] {
182
 
                                ContactsContract.Data.RAW_CONTACT_ID,
183
 
                                CommonDataKinds.Email.DATA,
184
 
                        },
185
 
                        ContactsContract.Data.MIMETYPE + " = '" +
186
 
                                CommonDataKinds.Email.CONTENT_ITEM_TYPE + "'",
187
 
                        null, ContactsContract.Data.IS_PRIMARY + " DESC" );
188
 
                while( cur.moveToNext() ) {
189
 
                        Long raw_id = cur.getLong( cur.getColumnIndex(
190
 
                                ContactsContract.Data.RAW_CONTACT_ID ) );
191
 
                        Long id = raw_to_aggregate_ids.get( raw_id );
192
 
                        String email = ContactsCache.normaliseEmailAddress(
193
 
                                cur.getString( cur.getColumnIndex(
194
 
                                        CommonDataKinds.Email.DATA ) ) );
195
 
                        if( email != null && id != null )
196
 
                        {
197
 
                                // if this is an email address for a contact for whom we have
198
 
                                // not added a lookup, add a lookup for the contact id by email
199
 
                                // address
200
 
                                if( unadded_ids.contains( id ) ) {
201
 
                                        cache.addLookup( new CacheIdentifier(
202
 
                                                CacheIdentifier.Type.PRIMARY_EMAIL, email ), id );
203
 
                                        unadded_ids.remove( id );
204
 
                                }
205
 
 
206
 
                                // add associated data
207
 
                                cache.addAssociatedEmail( id, email );
208
 
                        }
209
 
                }
210
 
 
211
 
                // get all postal addresses, primary ones first
212
 
                cur = _activity.managedQuery( ContactsContract.Data.CONTENT_URI,
213
 
                        new String[] {
214
 
                                ContactsContract.Data.RAW_CONTACT_ID,
215
 
                                CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS,
216
 
                        },
217
 
                        ContactsContract.Data.MIMETYPE + " = '" +
218
 
                                CommonDataKinds.Organization.CONTENT_ITEM_TYPE + "'",
219
 
                        null, ContactsContract.Data.IS_PRIMARY + " DESC" );
220
 
                while( cur.moveToNext() ) {
221
 
                        Long raw_id = cur.getLong( cur.getColumnIndex(
222
 
                                ContactsContract.Data.RAW_CONTACT_ID ) );
223
 
                        Long id = raw_to_aggregate_ids.get( raw_id );
224
 
                        String address = ContactsCache.normaliseAddress(
225
 
                                cur.getString( cur.getColumnIndex(
226
 
                                        CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS ) ) );
227
 
                        if( address != null && id != null )
228
 
                        {
229
 
                                // add associated data
230
 
                                cache.addAssociatedAddress( id, address );
231
 
                        }
232
 
                }
233
 
        }
234
 
 
235
 
        @Override
236
 
        public void deleteContact( Long id )
237
 
        {
238
 
                Uri contact_uri = ContentUris.withAppendedId(
239
 
                        ContactsContract.Contacts.CONTENT_URI, id );
240
 
                _activity.getContentResolver().delete( contact_uri, null, null );
241
 
        }
242
 
 
243
 
        @Override
244
 
        public Long addContact( String name ) throws ContactCreationException
245
 
        {
246
 
                // create raw contact
247
 
                ContentValues values = new ContentValues();
248
 
                Uri contact_uri = _activity.getContentResolver().insert(
249
 
                        ContactsContract.RawContacts.CONTENT_URI, values);
250
 
                Long raw_id = ContentUris.parseId( contact_uri );
251
 
                if( raw_id == 0 ) throw new ContactCreationException();
252
 
 
253
 
                // add name data for this raw contact
254
 
                if( name != null ) {
255
 
                        values.put( ContactsContract.Data.RAW_CONTACT_ID, raw_id );
256
 
                        values.put( ContactsContract.Data.MIMETYPE,
257
 
                                CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE );
258
 
                        values.put( CommonDataKinds.StructuredName.DISPLAY_NAME, name );
259
 
                        _activity.getContentResolver().insert(
260
 
                                ContactsContract.Data.CONTENT_URI, values );
261
 
                }
262
 
 
263
 
                // find corresponding aggregate contact
264
 
                contact_uri = Uri.withAppendedPath(
265
 
                        ContentUris.withAppendedId(
266
 
                                ContactsContract.RawContacts.CONTENT_URI, raw_id ),
267
 
                        ContactsContract.RawContacts.Entity.CONTENT_DIRECTORY );
268
 
                Cursor cur = _activity.managedQuery( contact_uri,
269
 
                        new String[] {
270
 
                                ContactsContract.RawContacts.CONTACT_ID,
271
 
                        }, null, null, null );
272
 
                Long id = null;
273
 
                if( cur.moveToNext() )
274
 
                        id = cur.getLong(
275
 
                                cur.getColumnIndex( ContactsContract.RawContacts.CONTACT_ID ) );
276
 
                if( id == null || id == 0 )
277
 
                {
278
 
                        // we didn't find an aggregate contact id, so try to clean up (by
279
 
                        // deleting the raw contact we just created) before bailing
280
 
                        contact_uri = ContentUris.withAppendedId(
281
 
                                ContactsContract.RawContacts.CONTENT_URI, id );
282
 
                        _activity.getContentResolver().delete( contact_uri, null, null );
283
 
 
284
 
                        throw new ContactCreationException();
285
 
                }
286
 
 
287
 
                return id;
288
 
        }
289
 
 
290
 
        /**
291
 
         * Obtain the raw contact id for the phone-only raw contact that is
292
 
         * associated with the aggregate contact id.  One will be created if
293
 
         * necessary.
294
 
         * @param id the aggregate contact id
295
 
         * @return the raw contact id
296
 
         * @throws ContactCreationException
297
 
         */
298
 
        Long obtainRawContact( Long id ) throws ContactCreationException
299
 
        {
300
 
                // attempt to lookup cached value
301
 
                Long raw_id = _aggregate_to_raw_ids.get( id );
302
 
                if( raw_id != null ) return raw_id;
303
 
 
304
 
                // find a corresponding raw contact that has no account name/type
305
 
                Cursor cur = _activity.managedQuery(
306
 
                        ContactsContract.RawContacts.CONTENT_URI,
307
 
                        new String[] {
308
 
                                ContactsContract.RawContacts._ID,
309
 
                        },
310
 
                        ContactsContract.RawContacts.DELETED + " != 0 AND " +
311
 
                                "IFNULL( " + ContactsContract.RawContacts.ACCOUNT_NAME +
312
 
                                        ", '' ) = '' AND " +
313
 
                                "IFNULL( " + ContactsContract.RawContacts.ACCOUNT_TYPE +
314
 
                                        ", '' ) = ''",
315
 
                        null, null );
316
 
                if( cur.moveToNext() )
317
 
                        raw_id = cur.getLong(
318
 
                                cur.getColumnIndex( ContactsContract.RawContacts._ID ) );
319
 
 
320
 
                // if one wasn't found, we'll need to create one
321
 
                if( raw_id == null ) {
322
 
                        ContentValues values = new ContentValues();
323
 
                        Uri contact_uri = _activity.getContentResolver().insert(
324
 
                                ContactsContract.RawContacts.CONTENT_URI, values);
325
 
                        raw_id = ContentUris.parseId( contact_uri );
326
 
                        if( raw_id == 0 ) throw new ContactCreationException();
327
 
                }
328
 
 
329
 
                // save value in our cache
330
 
                _aggregate_to_raw_ids.put( id, raw_id );
331
 
                return raw_id;
332
 
        }
333
 
 
334
 
        private int convertTypeToBackendType( Class< ? > cls, int type )
335
 
                throws ContactCreationException
336
 
        {
337
 
                if( cls == CommonDataKinds.Phone.class )
338
 
                {
339
 
                        switch( type )
340
 
                        {
341
 
                        case ContactData.TYPE_HOME:
342
 
                                return CommonDataKinds.Phone.TYPE_HOME;
343
 
                        case ContactData.TYPE_WORK:
344
 
                                return CommonDataKinds.Phone.TYPE_WORK;
345
 
                        case ContactData.TYPE_MOBILE:
346
 
                                return CommonDataKinds.Phone.TYPE_MOBILE;
347
 
                        case ContactData.TYPE_FAX_HOME:
348
 
                                return CommonDataKinds.Phone.TYPE_FAX_HOME;
349
 
                        case ContactData.TYPE_FAX_WORK:
350
 
                                return CommonDataKinds.Phone.TYPE_FAX_WORK;
351
 
                        case ContactData.TYPE_PAGER:
352
 
                                return CommonDataKinds.Phone.TYPE_PAGER;
353
 
                        }
354
 
                }
355
 
                else if( cls == CommonDataKinds.Email.class )
356
 
                {
357
 
                        switch( type )
358
 
                        {
359
 
                        case ContactData.TYPE_HOME:
360
 
                                return CommonDataKinds.Email.TYPE_HOME;
361
 
                        case ContactData.TYPE_WORK:
362
 
                                return CommonDataKinds.Email.TYPE_WORK;
363
 
                        }
364
 
                }
365
 
                else if( cls == CommonDataKinds.StructuredPostal.class )
366
 
                {
367
 
                        switch( type )
368
 
                        {
369
 
                        case ContactData.TYPE_HOME:
370
 
                                return CommonDataKinds.StructuredPostal.TYPE_HOME;
371
 
                        case ContactData.TYPE_WORK:
372
 
                                return CommonDataKinds.StructuredPostal.TYPE_WORK;
373
 
                        }
374
 
                }
375
 
 
376
 
                // still here?
377
 
                throw new ContactCreationException();
378
 
        }
379
 
 
380
 
        @Override
381
 
        public void addContactPhone( Long id, String number,
382
 
                ContactData.PreferredDetail data ) throws ContactCreationException
383
 
        {
384
 
                ContentValues values = new ContentValues();
385
 
                values.put( ContactsContract.Data.RAW_CONTACT_ID,
386
 
                        obtainRawContact( id ) );
387
 
                values.put( ContactsContract.Data.MIMETYPE,
388
 
                        CommonDataKinds.Phone.CONTENT_ITEM_TYPE );
389
 
                values.put( CommonDataKinds.Phone.TYPE,
390
 
                        convertTypeToBackendType( CommonDataKinds.Phone.class,
391
 
                                data.getType() ) );
392
 
                values.put( CommonDataKinds.Phone.NUMBER, number );
393
 
                if( data.isPreferred() )
394
 
                        values.put( CommonDataKinds.Phone.IS_PRIMARY, 1 );
395
 
 
396
 
                _activity.getContentResolver().insert(
397
 
                        ContactsContract.Data.CONTENT_URI, values );
398
 
        }
399
 
 
400
 
        @Override
401
 
        public void addContactEmail( Long id, String email,
402
 
                ContactData.PreferredDetail data ) throws ContactCreationException
403
 
        {
404
 
                ContentValues values = new ContentValues();
405
 
                values.put( ContactsContract.Data.RAW_CONTACT_ID,
406
 
                        obtainRawContact( id ) );
407
 
                values.put( ContactsContract.Data.MIMETYPE,
408
 
                        CommonDataKinds.Email.CONTENT_ITEM_TYPE );
409
 
                values.put( CommonDataKinds.Email.TYPE,
410
 
                        convertTypeToBackendType( CommonDataKinds.Email.class,
411
 
                                data.getType() ) );
412
 
                values.put( CommonDataKinds.Email.DATA, email );
413
 
                if( data.isPreferred() )
414
 
                        values.put( CommonDataKinds.Email.IS_PRIMARY, 1 );
415
 
 
416
 
                _activity.getContentResolver().insert(
417
 
                        ContactsContract.Data.CONTENT_URI, values );
418
 
        }
419
 
 
420
 
        @Override
421
 
        public void addContactAddresses( Long id, String address,
422
 
                ContactData.TypeDetail data ) throws ContactCreationException
423
 
        {
424
 
                ContentValues values = new ContentValues();
425
 
                values.put( ContactsContract.Data.RAW_CONTACT_ID,
426
 
                        obtainRawContact( id ) );
427
 
                values.put( ContactsContract.Data.MIMETYPE,
428
 
                        CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE );
429
 
                values.put( CommonDataKinds.StructuredPostal.TYPE,
430
 
                        convertTypeToBackendType( CommonDataKinds.StructuredPostal.class,
431
 
                                data.getType() ) );
432
 
                values.put(
433
 
                        CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, address );
434
 
 
435
 
                _activity.getContentResolver().insert(
436
 
                        ContactsContract.Data.CONTENT_URI, values );
437
 
        }
438
 
 
439
 
        @Override
440
 
        public void addContactOrganisation( Long id, String organisation,
441
 
                ContactData.ExtraDetail data ) throws ContactCreationException
442
 
        {
443
 
                ContentValues values = new ContentValues();
444
 
                values.put( ContactsContract.Data.RAW_CONTACT_ID,
445
 
                        obtainRawContact( id ) );
446
 
                values.put( ContactsContract.Data.MIMETYPE,
447
 
                        CommonDataKinds.Organization.CONTENT_ITEM_TYPE );
448
 
                values.put( CommonDataKinds.Organization.TYPE,
449
 
                        CommonDataKinds.Organization.TYPE_WORK );
450
 
                values.put(
451
 
                        CommonDataKinds.Organization.COMPANY, organisation );
452
 
                if( data.getExtra() != null )
453
 
                        values.put( CommonDataKinds.Organization.TITLE, data.getExtra() );
454
 
 
455
 
                _activity.getContentResolver().insert(
456
 
                        ContactsContract.Data.CONTENT_URI, values );
457
 
        }
458
 
}