BluetoothPbapVcardManager.java revision 4446eaa935994bc91d6d308303e8d27526b4590d
1/*
2 * Copyright (c) 2008-2009, Motorola, Inc.
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * - Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * - Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * - Neither the name of the Motorola, Inc. nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33package com.android.bluetooth.pbap;
34
35import com.android.bluetooth.R;
36
37import android.content.ContentResolver;
38import android.content.Context;
39import android.database.Cursor;
40import android.net.Uri;
41import android.provider.Contacts;
42import android.provider.CallLog.Calls;
43import android.provider.Contacts.Organizations;
44import android.provider.CallLog;
45import android.provider.Contacts.People;
46import android.provider.Contacts.Phones;
47import android.provider.Contacts.People.ContactMethods;
48import android.syncml.pim.vcard.ContactStruct;
49import android.syncml.pim.vcard.VCardComposer;
50import android.syncml.pim.vcard.VCardException;
51import android.syncml.pim.vcard.VCardParser;
52import android.util.Log;
53
54import java.util.ArrayList;
55import java.util.Arrays;
56import java.util.HashMap;
57import java.util.List;
58
59public class BluetoothPbapVcardManager {
60    static private final String TAG = "BluetoothPbapVcardManager";
61
62    private static final boolean V = BluetoothPbapService.VERBOSE;
63
64    private ContentResolver mResolver;
65
66    private Context mContext;
67
68    private String mDefaultName = null;
69
70    private String mDefaultNumber = null;
71
72    /** The projection to use when querying the call log table */
73    public static final String[] CALL_LOG_PROJECTION = new String[] {
74            Calls._ID, Calls.NUMBER, Calls.DATE, Calls.DURATION, Calls.TYPE, Calls.CACHED_NAME,
75            Calls.CACHED_NUMBER_TYPE, Calls.CACHED_NUMBER_LABEL
76    };
77
78    public static final int ID_COLUMN_INDEX = 0;
79
80    public static final int NUMBER_COLUMN_INDEX = 1;
81
82    public static final int DATE_COLUMN_INDEX = 2;
83
84    public static final int DURATION_COLUMN_INDEX = 3;
85
86    public static final int CALL_TYPE_COLUMN_INDEX = 4;
87
88    public static final int CALLER_NAME_COLUMN_INDEX = 5;
89
90    public static final int CALLER_NUMBERTYPE_COLUMN_INDEX = 6;
91
92    public static final int CALLER_NUMBERLABEL_COLUMN_INDEX = 7;
93
94    /** The projection to use when querying the phone book table */
95    public static final String[] CONTACT_PROJECTION = new String[] {
96            People._ID, // 0
97            People.NAME, // 1
98            People.NOTES, // 2
99            People.PRIMARY_PHONE_ID, // 3
100            People.PRESENCE_STATUS, // 4
101            People.STARRED, // 5
102            People.CUSTOM_RINGTONE, // 6
103            People.SEND_TO_VOICEMAIL, // 7
104            People.PHONETIC_NAME, // 8
105            People.PRIMARY_EMAIL_ID, // 9
106            People.PRIMARY_ORGANIZATION_ID, // 10
107    };
108
109    public static final int CONTACT_ID_COLUMN = 0;
110
111    public static final int CONTACT_NAME_COLUMN = 1;
112
113    public static final int CONTACT_NOTES_COLUMN = 2;
114
115    public static final int CONTACT_PRIMARY_PHONE_ID_COLUMN = 3;
116
117    public static final int CONTACT_SERVER_STATUS_COLUMN = 4;
118
119    public static final int CONTACT_STARRED_COLUMN = 5;
120
121    public static final int CONTACT_CUSTOM_RINGTONE_COLUMN = 6;
122
123    public static final int CONTACT_SEND_TO_VOICEMAIL_COLUMN = 7;
124
125    public static final int CONTACT_PHONETIC_NAME_COLUMN = 8;
126
127    public static final int CONTACT_PRIMARY_EMAIL_ID_COLUMN = 9;
128
129    public static final int CONTACT_PRIMARY_ORGANIZATION_ID_COLUMN = 10;
130
131    public static final String[] PHONES_PROJECTION = new String[] {
132            Phones._ID, // 0
133            Phones.NUMBER, // 1
134            Phones.TYPE, // 2
135            Phones.LABEL, // 3
136            Phones.ISPRIMARY, // 4
137            Phones.PERSON_ID, // 5
138    };
139
140    public static final int PHONES_ID_COLUMN = 0;
141
142    public static final int PHONES_NUMBER_COLUMN = 1;
143
144    public static final int PHONES_TYPE_COLUMN = 2;
145
146    public static final int PHONES_LABEL_COLUMN = 3;
147
148    public static final int PHONES_ISPRIMARY_COLUMN = 4;
149
150    public static final int PHONES_PERSON_ID_COLUMN = 5;
151
152    public static final String[] METHODS_PROJECTION = new String[] {
153            ContactMethods._ID, // 0
154            ContactMethods.KIND, // 1
155            ContactMethods.DATA, // 2
156            ContactMethods.TYPE, // 3
157            ContactMethods.LABEL, // 4
158            ContactMethods.ISPRIMARY, // 5
159            ContactMethods.AUX_DATA, // 6
160            Phones.PERSON_ID, // 7
161    };
162
163    public static final int METHODS_ID_COLUMN = 0;
164
165    public static final int METHODS_KIND_COLUMN = 1;
166
167    public static final int METHODS_DATA_COLUMN = 2;
168
169    public static final int METHODS_TYPE_COLUMN = 3;
170
171    public static final int METHODS_LABEL_COLUMN = 4;
172
173    public static final int METHODS_ISPRIMARY_COLUMN = 5;
174
175    public static final int METHODS_AUX_DATA_COLUMN = 6;
176
177    public static final int METHODS_PERSON_COLUMN = 7;
178
179    public static final String[] ORGANIZATIONS_PROJECTION = new String[] {
180            Organizations._ID, // 0
181            Organizations.TYPE, // 1
182            Organizations.LABEL, // 2
183            Organizations.COMPANY, // 3
184            Organizations.TITLE, // 4
185            Organizations.ISPRIMARY, // 5
186            Organizations.PERSON_ID, // 6
187    };
188
189    public static final int ORGANIZATIONS_ID_COLUMN = 0;
190
191    public static final int ORGANIZATIONS_TYPE_COLUMN = 1;
192
193    public static final int ORGANIZATIONS_LABEL_COLUMN = 2;
194
195    public static final int ORGANIZATIONS_COMPANY_COLUMN = 3;
196
197    public static final int ORGANIZATIONS_TITLE_COLUMN = 4;
198
199    public static final int ORGANIZATIONS_ISPRIMARY_COLUMN = 5;
200
201    public static final int ORGANIZATIONS_PERSON_ID_COLUMN = 6;
202
203    public static final String SORT_ORDER = "person ASC";
204
205    private int QUERY_DB_ERROR = -1;
206
207    private int QUERY_DB_OK = 1;
208
209    private Cursor peopleCursor, phonesCursor, contactMethodsCursor, orgCursor, callLogCursor;
210
211    private HashMap<Integer, String> mPhones;
212
213    private HashMap<Integer, String> mContactMethods;
214
215    private HashMap<Integer, String> mOrganizations;
216
217    private HashMap<Integer, HashMap<Integer, String>> tableList;
218
219    public static final int TABLE_PHONE = 0;
220
221    public static final int TABLE_CONTACTMETHOD = 1;
222
223    public static final int TABLE_ORGANIZATION = 2;
224
225    public BluetoothPbapVcardManager(final Context context) {
226        mContext = context;
227        mResolver = mContext.getContentResolver();
228        mDefaultName = context.getString(android.R.string.unknownName);
229        mDefaultNumber = context.getString(R.string.defaultnumber);
230
231        mPhones = new HashMap<Integer, String>();
232        mContactMethods = new HashMap<Integer, String>();
233        mOrganizations = new HashMap<Integer, String>();
234
235        tableList = new HashMap<Integer, HashMap<Integer, String>>();
236        tableList.put(TABLE_PHONE, mPhones);
237        tableList.put(TABLE_CONTACTMETHOD, mContactMethods);
238        tableList.put(TABLE_ORGANIZATION, mOrganizations);
239    }
240
241    private String getThisPhoneName() {
242        String name;
243        name = BluetoothPbapService.getLocalPhoneName();
244        if (name == null || name.trim().length() == 0) {
245            name = mDefaultName;
246        }
247        return name;
248    }
249
250    private String getThisPhoneNumber() {
251        String number;
252        number = BluetoothPbapService.getLocalPhoneNum();
253        if (number == null || number.trim().length() == 0) {
254            number = mDefaultNumber;
255        }
256        return number;
257    }
258
259    public final int getPhonebookSize() {
260        Uri myUri = Contacts.People.CONTENT_URI;
261        Cursor contactC = mResolver.query(myUri, null, null, null, null);
262
263        int mPhonebookSize = 0;
264        if (contactC != null) {
265            mPhonebookSize = contactC.getCount() + 1; // always has the 0.vcf
266        }
267        return mPhonebookSize;
268    }
269
270    /**
271     * For each table - phones/contact_methods/organization:
272     * Save the cursor positions for one person to hashMap.
273     * @hide
274     */
275    private int fillDataToHashmap(int table) {
276        int person_column;
277        Cursor cursor;
278
279        if (table == TABLE_PHONE) {
280            cursor = phonesCursor;
281            person_column = PHONES_PERSON_ID_COLUMN;
282
283        } else if (table == TABLE_CONTACTMETHOD) {
284            cursor = contactMethodsCursor;
285            person_column = METHODS_PERSON_COLUMN;
286
287        } else if (table == TABLE_ORGANIZATION) {
288            cursor = orgCursor;
289            person_column = ORGANIZATIONS_PERSON_ID_COLUMN;
290
291        } else {
292            Log.w(TAG, "fillDataToHashmap(): - no such table-" + table);
293            return -1;
294        }
295
296        // Clear HashMap first
297        tableList.get(table).clear();
298
299        // Represent the cursor position for phone_id or contactMethod_id or
300        // orgnization_id
301        int x_cursor_pos = 0;
302        int person_id = 0;
303        int previous_person_id = 0;
304        StringBuilder result = new StringBuilder();
305        for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
306            person_id = cursor.getInt(person_column);
307
308            if (cursor.isFirst()) {
309                previous_person_id = person_id;
310            }
311
312            if (person_id != previous_person_id) {
313                tableList.get(table).put(previous_person_id, result.toString());
314                result = result.delete(0, result.length());
315            }
316            result.append(x_cursor_pos);
317            result.append(",");
318            previous_person_id = person_id;
319            x_cursor_pos++;
320
321            if (cursor.isLast()) {
322                tableList.get(table).put(previous_person_id, result.toString());
323            }
324        }
325        return 1;
326    }
327
328    public final void closeContactsCursor() {
329        peopleCursor.close();
330        contactMethodsCursor.close();
331        orgCursor.close();
332    }
333
334    public final void closeCallLogCursor() {
335        callLogCursor.close();
336    }
337
338    public final int queryDataFromCallLogDB(int type) {
339        Uri myUri = CallLog.Calls.CONTENT_URI;
340        String selection = null;
341        switch (type) {
342            case BluetoothPbapObexServer.ContentType.INCOMING_CALL_HISTORY:
343                selection = Calls.TYPE + "=" + CallLog.Calls.INCOMING_TYPE;
344                break;
345            case BluetoothPbapObexServer.ContentType.OUTGOING_CALL_HISTORY:
346                selection = Calls.TYPE + "=" + CallLog.Calls.OUTGOING_TYPE;
347                break;
348            case BluetoothPbapObexServer.ContentType.MISSED_CALL_HISTORY:
349                selection = Calls.TYPE + "=" + CallLog.Calls.MISSED_TYPE;
350                break;
351            default:
352                break;
353        }
354        callLogCursor = mResolver.query(myUri, CALL_LOG_PROJECTION, selection, null,
355                CallLog.Calls.DEFAULT_SORT_ORDER);
356
357        if (callLogCursor == null) {
358            Log.w(TAG, "Query table - call Log failed.");
359            return QUERY_DB_ERROR;
360        }
361        return QUERY_DB_OK;
362    }
363
364    public final int queryDataFromContactsDB() {
365        Uri peopleUri = Contacts.People.CONTENT_URI;
366        peopleCursor = mResolver.query(peopleUri, CONTACT_PROJECTION, null, null,
367                People._ID + " ASC");
368        if (peopleCursor == null) {
369            Log.w(TAG, "Query table - people failed.");
370            return QUERY_DB_ERROR;
371        }
372
373        Uri phonesUri = Contacts.Phones.CONTENT_URI;
374        phonesCursor = mResolver.query(phonesUri, PHONES_PROJECTION, null, null, SORT_ORDER);
375        if (phonesCursor == null) {
376            Log.w(TAG, "Query table - phone failed.");
377            return QUERY_DB_ERROR;
378        }
379        fillDataToHashmap(TABLE_PHONE);
380
381        Uri contactMethodsUri = Contacts.ContactMethods.CONTENT_URI;
382        contactMethodsCursor = mResolver.query(contactMethodsUri, METHODS_PROJECTION, null, null,
383                SORT_ORDER);
384        if (contactMethodsCursor == null) {
385            Log.w(TAG, "Query table - contact_method failed.");
386            return QUERY_DB_ERROR;
387        }
388        fillDataToHashmap(TABLE_CONTACTMETHOD);
389
390        Uri orgUri = Contacts.Organizations.CONTENT_URI;
391        orgCursor = mResolver.query(orgUri, ORGANIZATIONS_PROJECTION, null, null, SORT_ORDER);
392        if (orgCursor == null) {
393            Log.w(TAG, "Query table - organization failed.");
394            return QUERY_DB_ERROR;
395        }
396        fillDataToHashmap(TABLE_ORGANIZATION);
397
398        return QUERY_DB_OK;
399    }
400
401    private List<String> parseOutCursorPosForOnePerson(int table, int peopleId) {
402        if (V) Log.v(TAG, "parseOutCursorPos(): table=" + table + "; peopleId=" + peopleId);
403
404        String tmpStr = tableList.get(table).get(peopleId);
405        if (tmpStr == null || tmpStr.trim().length() == 0) {
406            Log.w(TAG, "Can not get curPosStr from HashMap.");
407            return null;
408        }
409
410        String[] splitStr = tmpStr.split(",");
411
412        List<String> list = Arrays.asList(splitStr);
413
414        if (V) Log.v(TAG, "parseOutIds(): list=" + list.toString());
415
416        return list;
417    }
418
419    public final String getPhonebook(final int pos, final boolean vcardType) {
420        // Build up the phone entries
421        ContactStruct contactStruct = new ContactStruct();
422
423        if (pos == 0) {
424            contactStruct.name = getThisPhoneName();
425            contactStruct.addPhone(Contacts.PhonesColumns.TYPE_MOBILE, getThisPhoneNumber(), "",
426                    true);
427        } else {
428            if (!peopleCursor.moveToPosition(pos - 1)) {
429                Log.w(TAG, "peopleCursor can not moveToPosition: " + (pos - 1));
430                return null;
431            }
432
433            int peopleId = peopleCursor.getInt(CONTACT_ID_COLUMN);
434            int primary_phone = peopleCursor.getInt(CONTACT_PRIMARY_PHONE_ID_COLUMN);
435            int primary_contactMethod = peopleCursor.getInt(CONTACT_PRIMARY_EMAIL_ID_COLUMN);
436            int primary_org = peopleCursor.getInt(CONTACT_PRIMARY_ORGANIZATION_ID_COLUMN);
437
438            String name = peopleCursor.getString(CONTACT_NAME_COLUMN);
439            if (V) Log.v(TAG, "query data from table-people: name=" + name + "; primary_phone="
440                        + primary_phone + "; primary_contactmehtod=" + primary_contactMethod
441                        + "; primary_org=" + primary_org);
442
443
444            // build basic info
445            if (name == null || name.trim().length() == 0) {
446                contactStruct.name = mDefaultName;
447            } else {
448                contactStruct.name = name;
449            }
450            contactStruct.notes.add(checkStrEnd(peopleCursor.getString(CONTACT_NOTES_COLUMN),
451                    vcardType));
452
453            String tmpStr = null;
454            List<String> posList = null;
455            String curPosStr = null;
456            int cursorPos = 0;
457            // build phone info
458            if (primary_phone != 0) {
459                posList = parseOutCursorPosForOnePerson(TABLE_PHONE, peopleId);
460                for (int i = 0; i < posList.size(); i++) {
461                    curPosStr = posList.get(i).toString();
462                    cursorPos = Integer.parseInt(curPosStr);
463                    if (cursorPos < 0 || cursorPos > phonesCursor.getCount()) {
464                        Log.w(TAG, "Get incorrect cursor position from HashMap.");
465                        return null;
466                    }
467                    if (phonesCursor.moveToPosition(cursorPos)) {
468                        int type = phonesCursor.getInt(PHONES_TYPE_COLUMN);
469                        String number = phonesCursor.getString(PHONES_NUMBER_COLUMN);
470                        String label = phonesCursor.getString(PHONES_LABEL_COLUMN);
471                        boolean isPrimary = phonesCursor.getInt(PHONES_ISPRIMARY_COLUMN)
472                                == 1 ? true : false;
473                        if (V) Log.v(TAG, "query data from table-phones: type=" + type +
474                                "; number=" + number + "; label=" + label + "; isPrimary="
475                                + isPrimary);
476
477                        contactStruct.addPhone(type, number, label, isPrimary);
478                    }
479                }
480            }
481
482            // build contact_methods info
483            if (primary_contactMethod != 0) {
484                posList = parseOutCursorPosForOnePerson(TABLE_CONTACTMETHOD, peopleId);
485                for (int i = 0; i < posList.size(); i++) {
486                    curPosStr = posList.get(i).toString();
487                    cursorPos = Integer.parseInt(curPosStr);
488                    if (cursorPos < 0 || cursorPos > contactMethodsCursor.getCount()) {
489                        Log.w(TAG, "Get incorrect method cursor position from HashMap.");
490                        return null;
491                    }
492                    if (contactMethodsCursor.moveToPosition(cursorPos)) {
493                        int kind = contactMethodsCursor.getInt(METHODS_KIND_COLUMN);
494                        String label = contactMethodsCursor.getString(METHODS_LABEL_COLUMN);
495                        String data = contactMethodsCursor.getString(METHODS_DATA_COLUMN);
496                        int type = contactMethodsCursor.getInt(METHODS_TYPE_COLUMN);
497                        boolean isPrimary = contactMethodsCursor.getInt(METHODS_ISPRIMARY_COLUMN)
498                                        == 1 ? true : false;
499                        /*
500                         * TODO Below code is totally depend on the
501                         * implementation of package android.syncml.pim.vcard
502                         * VcardComposer shall throw null pointer exception when
503                         * label is not set function appendContactMethodStr is
504                         * weak and shall be improved in the future
505                         */
506                        if (kind == Contacts.KIND_EMAIL) {
507                            if (type == Contacts.ContactMethodsColumns.TYPE_OTHER) {
508                                if (label == null || label.trim().length() == 0) {
509                                    label = Integer.toString(type);
510                                }
511                            }
512                        }
513                        if (kind == Contacts.KIND_POSTAL) {
514                            data = checkStrEnd(data, vcardType);
515                        }
516                        if (V) Log.v(TAG, "query data from table-contactMethods: kind=" + kind
517                                    + "; label=" + label + "; data=" + data);
518
519                        contactStruct.addContactmethod(kind, type, data, label, isPrimary);
520                    }
521                }
522            }
523
524            // build organization info
525            if (primary_org != 0) {
526                posList = parseOutCursorPosForOnePerson(TABLE_CONTACTMETHOD, peopleId);
527                for (int i = 0; i < posList.size(); i++) {
528                    curPosStr = posList.get(i).toString();
529                    cursorPos = Integer.parseInt(curPosStr);
530                    if (cursorPos < 0 || cursorPos > orgCursor.getCount()) {
531                        Log.w(TAG, "Get incorrect cursor position from HashMap.");
532                        return null;
533                    }
534                    if (orgCursor.moveToPosition(cursorPos)) {
535                        int type = orgCursor.getInt(ORGANIZATIONS_TYPE_COLUMN);
536                        String company = orgCursor.getString(ORGANIZATIONS_COMPANY_COLUMN);
537                        String title = checkStrEnd(orgCursor.getString(ORGANIZATIONS_TITLE_COLUMN),
538                                vcardType);
539                        boolean isPrimary;
540                        isPrimary = orgCursor.getInt(ORGANIZATIONS_ISPRIMARY_COLUMN) == 1 ? true
541                                : false;
542                        if (V) Log.v(TAG, "query data from table-organization: type=" + type
543                                    + "; company=" + company + "; title=" + title);
544
545                        contactStruct.addOrganization(type, company, title, isPrimary);
546                    }
547                }
548            }
549        }
550
551        String vcardStr;
552        // Generate vCard data.
553        try {
554            VCardComposer composer = new VCardComposer();
555            if (vcardType) {
556                vcardStr = composer.createVCard(contactStruct, VCardParser.VERSION_VCARD21_INT);
557            } else {
558                vcardStr = composer.createVCard(contactStruct, VCardParser.VERSION_VCARD30_INT);
559            }
560        } catch (VCardException e) {
561            Log.e(TAG, "catch exception in loadPhonebook" + e.toString());
562            return null;
563        }
564        return vcardStr;
565    }
566
567    public final int getCallHistorySize(final int type) {
568        Uri myUri = CallLog.Calls.CONTENT_URI;
569        String selection = null;
570        switch (type) {
571            case BluetoothPbapObexServer.ContentType.INCOMING_CALL_HISTORY:
572                selection = Calls.TYPE + "=" + CallLog.Calls.INCOMING_TYPE;
573                break;
574            case BluetoothPbapObexServer.ContentType.OUTGOING_CALL_HISTORY:
575                selection = Calls.TYPE + "=" + CallLog.Calls.OUTGOING_TYPE;
576                break;
577            case BluetoothPbapObexServer.ContentType.MISSED_CALL_HISTORY:
578                selection = Calls.TYPE + "=" + CallLog.Calls.MISSED_TYPE;
579                break;
580            default:
581                break;
582        }
583        Cursor callCursor = mResolver.query(myUri, null, selection, null,
584                CallLog.Calls.DEFAULT_SORT_ORDER);
585        int size = 0;
586        if (callCursor != null) {
587            size = callCursor.getCount();
588            callCursor.close();
589        }
590        return size;
591    }
592
593    public final String getCallHistory(final int pos, final boolean vCard21) {
594        try {
595            int size = callLogCursor.getCount();
596            if (pos >= 0 && pos < size) {
597                ContactStruct contactStruct = new ContactStruct();
598                if (callLogCursor.moveToPosition(pos)) {
599                    contactStruct.name = callLogCursor.getString(CALLER_NAME_COLUMN_INDEX);
600                    if (contactStruct.name == null || contactStruct.name.trim().length() == 0) {
601                        contactStruct.name = callLogCursor.getString(NUMBER_COLUMN_INDEX);
602                    }
603                    String number = callLogCursor.getString(NUMBER_COLUMN_INDEX);
604                    int type = callLogCursor.getInt(CALLER_NUMBERTYPE_COLUMN_INDEX);
605                    String label = callLogCursor.getString(CALLER_NUMBERLABEL_COLUMN_INDEX);
606                    if (label == null || label.trim().length() == 0) {
607                        label = Integer.toString(type);
608                    }
609                    contactStruct.addPhone(type, number, label, true);
610                }
611
612                try {
613                    VCardComposer composer = new VCardComposer();
614                    if (vCard21) {
615                        return composer.createVCard(contactStruct,
616                                VCardParser.VERSION_VCARD21_INT);
617                    } else {
618                        return composer.createVCard(contactStruct,
619                                VCardParser.VERSION_VCARD30_INT);
620                    }
621                } catch (VCardException e) {
622                    Log.e(TAG, "catch exception" + e.toString());
623                }
624            } else {
625                Log.w(TAG, "pos invalid");
626            }
627        } catch (Exception e) {
628            Log.e(TAG, "catch exception e" + e.toString());
629        }
630        return null;
631    }
632
633    public final ArrayList<String> loadNameList() {
634        ArrayList<String> nameList = new ArrayList<String>();
635        int size = getPhonebookSize();
636        Uri myUri = Contacts.People.CONTENT_URI;
637        Cursor contactC = mResolver.query(myUri, null, null, null, null);
638        for (int pos = 0; pos < size; pos++) {
639            if (pos == 0) {
640                nameList.add(getThisPhoneName());
641            } else {
642                if (contactC != null) {
643                    contactC.moveToPosition(pos - 1);
644                    String name = contactC.getString(contactC
645                            .getColumnIndexOrThrow(Contacts.People.NAME));
646                    if (name == null || name.trim().length() == 0) {
647                        name = mDefaultName;
648                    }
649                    nameList.add(name);
650                }
651            }
652        }
653        if (contactC != null) {
654            contactC.close();
655        }
656        return nameList;
657    }
658
659    public final ArrayList<String> loadNumberList() {
660        ArrayList<String> numberList = new ArrayList<String>();
661        if (numberList.size() > 0) {
662            numberList.clear();
663        }
664        int size = getPhonebookSize();
665        Uri myUri = Contacts.People.CONTENT_URI;
666        Cursor contactC = mResolver.query(myUri, null, null, null, null);
667        for (int pos = 0; pos < size; pos++) {
668            if (pos == 0) {
669                numberList.add(getThisPhoneNumber());
670            } else {
671                contactC.moveToPosition(pos - 1);
672                String number = contactC.getString(contactC
673                        .getColumnIndexOrThrow(Contacts.People.PRIMARY_PHONE_ID));
674                if (number == null || number.trim().length() == 0) {
675                    number = mDefaultNumber;
676                }
677                numberList.add(number);
678            }
679        }
680        return numberList;
681    }
682
683    public final ArrayList<String> loadCallHistoryList(final int type) {
684        int size = 0;
685        String selection = null;
686        Uri myUri = CallLog.Calls.CONTENT_URI;
687        ArrayList<String> list = new ArrayList<String>();
688        switch (type) {
689            case BluetoothPbapObexServer.ContentType.INCOMING_CALL_HISTORY:
690                selection = Calls.TYPE + "=" + CallLog.Calls.INCOMING_TYPE;
691                break;
692            case BluetoothPbapObexServer.ContentType.OUTGOING_CALL_HISTORY:
693                selection = Calls.TYPE + "=" + CallLog.Calls.OUTGOING_TYPE;
694                break;
695            case BluetoothPbapObexServer.ContentType.MISSED_CALL_HISTORY:
696                selection = Calls.TYPE + "=" + CallLog.Calls.MISSED_TYPE;
697                break;
698            default:
699                break;
700        }
701        size = getCallHistorySize(type);
702        Cursor callCursor = mResolver.query(myUri, CALL_LOG_PROJECTION, selection, null,
703                CallLog.Calls.DEFAULT_SORT_ORDER);
704        if (callCursor != null) {
705            for (int pos = 0; pos < size; pos++) {
706                callCursor.moveToPosition(pos);
707                String name = callCursor.getString(CALLER_NAME_COLUMN_INDEX);
708                if (name == null || name.trim().length() == 0) {
709                    // name not found,use number instead
710                    name = callCursor.getString(NUMBER_COLUMN_INDEX);
711                }
712                list.add(name);
713            }
714            callCursor.close();
715        }
716        return list;
717    }
718
719    /**
720     * This function is to check the string end to avoid function
721     * foldingString's returning null in package android.syncml.pim.vcard
722     */
723    private final String checkStrEnd(String str, final boolean vCard21) {
724        if (str == null || str.trim().length() == 0) {
725            return str;
726        }
727        if (str.charAt(str.length() - 1) != '\n') {
728            if (vCard21) {
729                str += "\r\n";
730            } else {
731                str += "\n";
732            }
733        }
734        return str;
735    }
736}
737