/android/import-contacts

To get this branch, use:
bzr branch http://bzr.ed.am/android/import-contacts
39 by edam
- pulled contacts cache out in to seperate class
1
/*
2
 * ContactsCache.java
3
 *
57 by edam
cleanup; fixed some typos; updated TODO
4
 * Copyright (C) 2011 to 2012 Tim Marston <tim@ed.am>
39 by edam
- pulled contacts cache out in to seperate class
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
39 by edam
- pulled contacts cache out in to seperate class
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;
39 by edam
- pulled contacts cache out in to seperate class
25
26
import java.util.HashMap;
27
import java.util.HashSet;
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
28
import java.util.Locale;
39 by edam
- pulled contacts cache out in to seperate class
29
30
public class ContactsCache
31
{
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
32
	/**
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
33
	 * A thing that can be used to identify (or lookup) a contact within the
34
	 * contacts cache.  It is not a reference to a cache entry and may not
35
	 * identify an existing contact in the cache.
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
36
	 */
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
37
	public static class CacheIdentifier
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
38
	{
66 by edam
removed redundant CacheIdentifier constructor and NONE type
39
		public enum Type { NAME, ORGANISATION, PRIMARY_NUMBER, PRIMARY_EMAIL }
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
40
41
		private Type _type;
42
		private String _detail;
43
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
44
		/**
45
		 * Obtain a cache identifier.  This routine is designed to be as robust
46
		 * as possible (in terms of bad or null detail values), and to return
47
		 * null when a cache identifier can not be created.
48
		 * @param type the detail type
49
		 * @param detail the detail
50
		 * @return the cache identifier, or null
51
		 */
52
		public static CacheIdentifier factory( Type type, String detail )
53
		{
54
			switch( type )
55
			{
56
			case NAME: detail = normaliseName( detail ); break;
57
			case ORGANISATION: detail = normaliseOrganisation( detail ); break;
58
			case PRIMARY_NUMBER: detail = normalisePhoneNumber( detail ); break;
59
			case PRIMARY_EMAIL: detail = normaliseEmailAddress( detail ); break;
60
			default: return null;
61
			}
62
			if( detail == null ) return null;
63
			return new CacheIdentifier( type, detail );
64
		}
65
66
		/**
67
		 * Obtain a cache identifier from contact data.  This routine is
68
		 * designed to be as robust as possible and may return null when a cache
69
		 * identifier can not be created.
70
		 * @param contact the contact data
71
		 * @return the cache identifier, or null
72
		 */
73
		public static CacheIdentifier factory( Importer.ContactData contact )
74
		{
67 by edam
slightly improved the efficiency of the cache identifier factory function
75
			CacheIdentifier identifier = null;
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
76
77
			if( contact.hasName() )
67 by edam
slightly improved the efficiency of the cache identifier factory function
78
				identifier = factory( CacheIdentifier.Type.NAME,
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
79
					contact.getName() );
67 by edam
slightly improved the efficiency of the cache identifier factory function
80
			if( identifier != null ) return identifier;
81
82
			if( contact.hasPrimaryOrganisation() )
83
				identifier = factory( CacheIdentifier.Type.ORGANISATION,
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
84
					contact.getPrimaryOrganisation() );
67 by edam
slightly improved the efficiency of the cache identifier factory function
85
			if( identifier != null ) return identifier;
86
87
			if( contact.hasPrimaryNumber() )
88
				identifier = factory( CacheIdentifier.Type.PRIMARY_NUMBER,
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
89
					contact.getPrimaryNumber() );
67 by edam
slightly improved the efficiency of the cache identifier factory function
90
			if( identifier != null ) return identifier;
91
92
			if( contact.hasPrimaryEmail() )
93
				identifier = factory( CacheIdentifier.Type.PRIMARY_EMAIL,
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
94
					contact.getPrimaryEmail() );
67 by edam
slightly improved the efficiency of the cache identifier factory function
95
			if( identifier != null ) return identifier;
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
96
67 by edam
slightly improved the efficiency of the cache identifier factory function
97
			return null;
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
98
		}
99
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
100
		protected CacheIdentifier( Type type, String detail )
101
		{
102
			_type = type;
103
			_detail = detail;
104
		}
105
106
		public Type getType()
107
		{
108
			return _type;
109
		}
110
111
		public String getDetail()
112
		{
113
			return _detail;
114
		}
115
	}
116
117
	// mappings of contact names, organisations and primary numbers to ids
53 by edam
abstracted the android contacts API in to an interface, ready to be switched to
118
	private HashMap< String, Long > _contactsByName
119
		= new HashMap< String, Long >();
120
	private HashMap< String, Long > _contactsByOrg
121
		= new HashMap< String, Long >();
122
	private HashMap< String, Long > _contactsByNumber
123
		= new HashMap< String, Long >();
124
	private HashMap< String, Long > _contactsByEmail
125
		= new HashMap< String, Long >();
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
126
127
	// mapping of contact ids to sets of associated data
53 by edam
abstracted the android contacts API in to an interface, ready to be switched to
128
	private HashMap< Long, HashSet< String > > _contactNumbers
129
		= new HashMap< Long, HashSet< String > >();
130
	private HashMap< Long, HashSet< String > > _contactEmails
131
		= new HashMap< Long, HashSet< String > >();
132
	private HashMap< Long, HashSet< String > > _contactAddresses
133
		= new HashMap< Long, HashSet< String > >();
134
	private HashMap< Long, HashSet< String > > _contactOrganisations
135
		= new HashMap< Long, HashSet< String > >();
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
136
	private HashMap< Long, HashSet< String > > _contactNotes
137
		= new HashMap< Long, HashSet< String > >();
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
138
139
	public boolean canLookup( CacheIdentifier identifier )
140
	{
141
		return lookup( identifier ) != null;
142
	}
143
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
144
	/**
145
	 * Retrieve the contact id of a contact identified by the specified cache
146
	 * identifier, if it exists.
147
	 * @param identifier the cache identifier
148
	 * @return a contact id, or null
149
	 */
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
150
	public Long lookup( CacheIdentifier identifier )
151
	{
152
		switch( identifier.getType() )
153
		{
154
		case NAME:
155
			return _contactsByName.get( identifier.getDetail() );
156
		case ORGANISATION:
157
			return _contactsByOrg.get( identifier.getDetail() );
158
		case PRIMARY_NUMBER:
159
			return _contactsByNumber.get( identifier.getDetail() );
160
		case PRIMARY_EMAIL:
161
			return _contactsByEmail.get( identifier.getDetail() );
162
		}
163
		return null;
164
	}
165
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
166
	/**
167
	 * Remove any cache entry that is identified by the cache identifier.
168
	 * @param identifier the cache identifier
169
	 * @return the contact id of the contact that was removed, or null
170
	 */
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
171
	public Long removeLookup( CacheIdentifier identifier )
172
	{
173
		switch( identifier.getType() )
174
		{
175
		case NAME:
176
			return _contactsByName.remove( identifier.getDetail() );
177
		case ORGANISATION:
178
			return _contactsByOrg.remove( identifier.getDetail() );
179
		case PRIMARY_NUMBER:
180
			return _contactsByNumber.remove( identifier.getDetail() );
181
		case PRIMARY_EMAIL:
182
			return _contactsByEmail.remove( identifier.getDetail() );
183
		}
184
		return null;
185
	}
186
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
187
	/**
188
	 * Add a lookup from a contact identifier to a contact id to the cache.
189
	 * @param identifier the cache identifier
190
	 * @param id teh contact id
191
	 */
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
192
	public void addLookup( CacheIdentifier identifier, Long id )
193
	{
194
		switch( identifier.getType() )
195
		{
196
		case NAME:
197
			_contactsByName.put( identifier.getDetail(), id );
198
			break;
199
		case ORGANISATION:
200
			_contactsByOrg.put( identifier.getDetail(), id );
201
			break;
202
		case PRIMARY_NUMBER:
203
			_contactsByNumber.put( identifier.getDetail(), id );
204
			break;
205
		case PRIMARY_EMAIL:
206
			_contactsByEmail.put( identifier.getDetail(), id );
207
			break;
208
		}
209
	}
210
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
211
	/**
212
	 * Remove any data that is associated with an contact id.
213
	 * @param id
214
	 */
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
215
	public void removeAssociatedData( Long id )
216
	{
217
		_contactNumbers.remove( id );
218
		_contactEmails.remove( id );
219
		_contactAddresses.remove( id );
220
		_contactOrganisations.remove( id );
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
221
		_contactNotes.remove( id );
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
222
	}
223
224
	public boolean hasAssociatedNumber( Long id, String number )
225
	{
226
		number = normalisePhoneNumber( number );
227
		if( number == null ) return false;
228
229
		HashSet< String > set = _contactNumbers.get( id );
230
		return set != null && set.contains( number );
231
	}
232
233
	public void addAssociatedNumber( Long id, String number )
234
	{
235
		number = normalisePhoneNumber( number );
236
		if( number == null ) return;
237
238
		HashSet< String > set = _contactNumbers.get( id );
239
		if( set == null ) {
240
			set = new HashSet< String >();
241
			_contactNumbers.put( id, set );
242
		}
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
243
		set.add( number );
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
244
	}
245
246
	public boolean hasAssociatedEmail( Long id, String email )
247
	{
248
		email = normaliseEmailAddress( email );
249
		if( email == null ) return false;
250
251
		HashSet< String > set = _contactEmails.get( id );
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
252
		return set != null && set.contains( email );
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
253
	}
254
255
	public void addAssociatedEmail( Long id, String email )
256
	{
257
		email = normaliseEmailAddress( email );
258
		if( email == null ) return;
259
260
		HashSet< String > set = _contactEmails.get( id );
261
		if( set == null ) {
262
			set = new HashSet< String >();
263
			_contactEmails.put( id, set );
264
		}
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
265
		set.add( email );
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
266
	}
267
268
	public boolean hasAssociatedAddress( Long id, String address )
269
	{
270
		address = normaliseAddress( address );
271
		if( address == null ) return false;
272
273
		HashSet< String > set = _contactAddresses.get( id );
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
274
		return set != null && set.contains( address );
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
275
	}
276
277
	public void addAssociatedAddress( Long id, String address )
278
	{
279
		address = normaliseAddress( address );
280
		if( address == null ) return;
281
282
		HashSet< String > set = _contactAddresses.get( id );
283
		if( set == null ) {
284
			set = new HashSet< String >();
285
			_contactAddresses.put( id, set );
286
		}
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
287
		set.add( address );
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
288
	}
289
290
	public boolean hasAssociatedOrganisation( Long id, String organisation )
291
	{
292
		organisation = normaliseOrganisation( organisation );
293
		if( organisation == null ) return false;
294
295
		HashSet< String > set = _contactOrganisations.get( id );
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
296
		return set != null && set.contains( organisation );
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
297
	}
298
299
	public void addAssociatedOrganisation( Long id, String organisation )
300
	{
301
		organisation = normaliseOrganisation( organisation );
302
		if( organisation == null ) return;
303
304
		HashSet< String > set = _contactOrganisations.get( id );
305
		if( set == null ) {
306
			set = new HashSet< String >();
307
			_contactOrganisations.put( id, set );
308
		}
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
309
		set.add( organisation );
310
	}
311
312
	public boolean hasAssociatedNote( Long id, String note )
313
	{
314
		note = normaliseNote( note );
315
		if( note == null ) return false;
316
317
		HashSet< String > set = _contactNotes.get( id );
318
		return set != null && set.contains( note );
319
	}
320
321
	public void addAssociatedNote( Long id, String note )
322
	{
323
		note = normaliseNote( note );
324
		if( note == null ) return;
325
326
		HashSet< String > set = _contactNotes.get( id );
327
		if( set == null ) {
328
			set = new HashSet< String >();
329
			_contactNotes.put( id, set );
330
		}
331
		set.add( note );
39 by edam
- pulled contacts cache out in to seperate class
332
	}
333
53 by edam
abstracted the android contacts API in to an interface, ready to be switched to
334
	static public String normaliseName( String name )
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
335
	{
336
		if( name == null ) return null;
337
		name = name.trim();
338
		return name.length() > 0? name : null;
339
	}
340
53 by edam
abstracted the android contacts API in to an interface, ready to be switched to
341
	static public String normalisePhoneNumber( String number )
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
342
	{
343
		if( number == null ) return null;
344
		number = number.trim().replaceAll( "[-\\(\\) ]", "" );
345
		return number.length() > 0? number : null;
346
	}
347
53 by edam
abstracted the android contacts API in to an interface, ready to be switched to
348
	static public String normaliseEmailAddress( String email )
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
349
	{
350
		if( email == null ) return null;
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
351
		email = email.trim().toLowerCase( Locale.US );
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
352
		return email.length() > 0? email : null;
353
	}
354
53 by edam
abstracted the android contacts API in to an interface, ready to be switched to
355
	static public String normaliseOrganisation( String organisation )
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
356
	{
357
		if( organisation == null ) return null;
358
		organisation = organisation.trim();
359
		return organisation.length() > 0? organisation : null;
360
	}
361
53 by edam
abstracted the android contacts API in to an interface, ready to be switched to
362
	static public String normaliseAddress( String address )
40 by edam
- fixed logic for vcard field types (home, work, cell, etc) so it works
363
	{
364
		if( address == null ) return null;
365
		address = address.trim();
366
		return address.length() > 0? address : null;
39 by edam
- pulled contacts cache out in to seperate class
367
	}
65 by edam
added support for notes; rewrote backends so that all normalising of data is now done within the contacts cache; made the vCard unescape routine slightly more acceptant of non-standard escaped characters
368
369
	static public String normaliseNote( String note )
370
	{
371
		if( note == null ) return null;
372
		note = note.trim();
373
		return note.length() > 0? note : null;
374
	}
39 by edam
- pulled contacts cache out in to seperate class
375
}