15aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank/*
25aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank * Copyright (C) 2010 The Android Open Source Project
35aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank *
45aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank * Licensed under the Apache License, Version 2.0 (the "License");
55aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank * you may not use this file except in compliance with the License.
65aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank * You may obtain a copy of the License at
75aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank *
85aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank *      http://www.apache.org/licenses/LICENSE-2.0
95aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank *
105aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank * Unless required by applicable law or agreed to in writing, software
115aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank * distributed under the License is distributed on an "AS IS" BASIS,
125aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank * See the License for the specific language governing permissions and
145aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank * limitations under the License.
155aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank */
165aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank
175aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blankpackage com.android.exchange.provider;
185aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank
191f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikovimport android.accounts.AccountManager;
205aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blankimport android.content.ContentProvider;
215aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blankimport android.content.ContentValues;
227307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blankimport android.content.Context;
235aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blankimport android.content.UriMatcher;
245aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blankimport android.database.Cursor;
255aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blankimport android.database.MatrixCursor;
265aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blankimport android.net.Uri;
275aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blankimport android.os.Binder;
28c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport android.os.Bundle;
29469544cd1b9652c446c96b97f4abbdb65d7e06aaMarc Blankimport android.provider.ContactsContract;
30d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Email;
31d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone;
32d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
33c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport android.provider.ContactsContract.Contacts;
34d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikovimport android.provider.ContactsContract.Contacts.Data;
35c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport android.provider.ContactsContract.Directory;
36222dec5216626980f1450d8917e9b73e2d4c9040Alon Albertimport android.provider.ContactsContract.DisplayNameSources;
37c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport android.provider.ContactsContract.RawContacts;
38d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikovimport android.text.TextUtils;
39e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shraunerimport android.util.Log;
40e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shraunerimport android.util.Pair;
41d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov
426ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albertimport com.android.emailcommon.Configuration;
436ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albertimport com.android.emailcommon.mail.PackedString;
446ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albertimport com.android.emailcommon.provider.Account;
456ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albertimport com.android.emailcommon.provider.EmailContent;
466ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albertimport com.android.emailcommon.provider.EmailContent.AccountColumns;
476ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albertimport com.android.emailcommon.service.AccountServiceProxy;
486ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albertimport com.android.emailcommon.utility.Utility;
496ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albertimport com.android.exchange.Eas;
506ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albertimport com.android.exchange.R;
516ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albertimport com.android.exchange.provider.GalResult.GalData;
52bb0141b49e7eff978fa445249dc888461ea581e3Martin Hibdonimport com.android.exchange.service.EasService;
536ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albertimport com.android.mail.utils.LogUtils;
546ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albert
55222dec5216626980f1450d8917e9b73e2d4c9040Alon Albertimport java.text.Collator;
56222dec5216626980f1450d8917e9b73e2d4c9040Alon Albertimport java.util.ArrayList;
57222dec5216626980f1450d8917e9b73e2d4c9040Alon Albertimport java.util.Comparator;
58d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikovimport java.util.HashMap;
59222dec5216626980f1450d8917e9b73e2d4c9040Alon Albertimport java.util.HashSet;
60d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikovimport java.util.List;
61222dec5216626980f1450d8917e9b73e2d4c9040Alon Albertimport java.util.Set;
62222dec5216626980f1450d8917e9b73e2d4c9040Alon Albertimport java.util.TreeMap;
635aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank
645aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank/**
655aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank * ExchangeDirectoryProvider provides real-time data from the Exchange server; at the moment, it is
665aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank * used solely to provide GAL (Global Address Lookup) service to email address adapters
675aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank */
685aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blankpublic class ExchangeDirectoryProvider extends ContentProvider {
696ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albert    private static final String TAG = Eas.LOG_TAG;
706ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albert
712bf91acb8174aab82582ce975a4b60702c810a9cMarc Blank    public static final String EXCHANGE_GAL_AUTHORITY =
722bf91acb8174aab82582ce975a4b60702c810a9cMarc Blank            com.android.exchange.Configuration.EXCHANGE_GAL_AUTHORITY;
735aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank
74d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov    private static final int DEFAULT_CONTACT_ID = 1;
75e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner
76469544cd1b9652c446c96b97f4abbdb65d7e06aaMarc Blank    private static final int DEFAULT_LOOKUP_LIMIT = 20;
77e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner    private static final int MAX_LOOKUP_LIMIT = 100;
78d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov
795aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank    private static final int GAL_BASE = 0;
801f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov    private static final int GAL_DIRECTORIES = GAL_BASE;
811f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov    private static final int GAL_FILTER = GAL_BASE + 1;
821f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov    private static final int GAL_CONTACT = GAL_BASE + 2;
831f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov    private static final int GAL_CONTACT_WITH_ID = GAL_BASE + 3;
84f6e831704c7df2b841760931469b1e995b957cd9Dmitri Plotnikov    private static final int GAL_EMAIL_FILTER = GAL_BASE + 4;
85222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert    private static final int GAL_PHONE_FILTER = GAL_BASE + 5;
865aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank
877f29df6b105b1b7fe83713245a680d151fd32887Marc Blank    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
887307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank    /*package*/ final HashMap<String, Long> mAccountIdMap = new HashMap<String, Long>();
895aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank
907f29df6b105b1b7fe83713245a680d151fd32887Marc Blank    static {
917f29df6b105b1b7fe83713245a680d151fd32887Marc Blank        sURIMatcher.addURI(EXCHANGE_GAL_AUTHORITY, "directories", GAL_DIRECTORIES);
927f29df6b105b1b7fe83713245a680d151fd32887Marc Blank        sURIMatcher.addURI(EXCHANGE_GAL_AUTHORITY, "contacts/filter/*", GAL_FILTER);
937f29df6b105b1b7fe83713245a680d151fd32887Marc Blank        sURIMatcher.addURI(EXCHANGE_GAL_AUTHORITY, "contacts/lookup/*/entities", GAL_CONTACT);
947f29df6b105b1b7fe83713245a680d151fd32887Marc Blank        sURIMatcher.addURI(EXCHANGE_GAL_AUTHORITY, "contacts/lookup/*/#/entities",
957f29df6b105b1b7fe83713245a680d151fd32887Marc Blank                GAL_CONTACT_WITH_ID);
967f29df6b105b1b7fe83713245a680d151fd32887Marc Blank        sURIMatcher.addURI(EXCHANGE_GAL_AUTHORITY, "data/emails/filter/*", GAL_EMAIL_FILTER);
97222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        sURIMatcher.addURI(EXCHANGE_GAL_AUTHORITY, "data/phones/filter/*", GAL_PHONE_FILTER);
98222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
997f29df6b105b1b7fe83713245a680d151fd32887Marc Blank    }
1007f29df6b105b1b7fe83713245a680d151fd32887Marc Blank
1015aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank    @Override
1025aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank    public boolean onCreate() {
10388e51a3c990658e8b104881cb9d6c6a8d488a8a6Tony Mantler        EmailContent.init(getContext());
1045aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank        return true;
1055aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank    }
1065aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank
107d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov    static class GalProjection {
108d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        final int size;
109d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        final HashMap<String, Integer> columnMap = new HashMap<String, Integer>();
110d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov
111d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        GalProjection(String[] projection) {
112d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            size = projection.length;
113d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            for (int i = 0; i < projection.length; i++) {
114d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                columnMap.put(projection[i], i);
115d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            }
116d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        }
117d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov    }
118d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov
119d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov    static class GalContactRow {
120d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        private final GalProjection mProjection;
121d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        private Object[] row;
122d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        static long dataId = 1;
123d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov
12456d35257ef1493e849522c3c2828d904e4c5531fScott Kennedy        GalContactRow(GalProjection projection, long contactId, String accountName,
12556d35257ef1493e849522c3c2828d904e4c5531fScott Kennedy                String displayName) {
126d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            this.mProjection = projection;
127d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            row = new Object[projection.size];
128d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov
129d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            put(Contacts.Entity.CONTACT_ID, contactId);
130d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov
131d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            // We only have one raw contact per aggregate, so they can have the same ID
132d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            put(Contacts.Entity.RAW_CONTACT_ID, contactId);
133d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            put(Contacts.Entity.DATA_ID, dataId++);
134d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov
135d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            put(Contacts.DISPLAY_NAME, displayName);
136d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov
137d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            // TODO alternative display name
138d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            put(Contacts.DISPLAY_NAME_ALTERNATIVE, displayName);
139d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov
140c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blank            put(RawContacts.ACCOUNT_TYPE, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE);
141d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            put(RawContacts.ACCOUNT_NAME, accountName);
142d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            put(RawContacts.RAW_CONTACT_IS_READ_ONLY, 1);
143d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            put(Data.IS_READ_ONLY, 1);
144d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        }
145d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov
146d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        Object[] getRow () {
147d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            return row;
148d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        }
149d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov
150d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        void put(String columnName, Object value) {
151222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            final Integer integer = mProjection.columnMap.get(columnName);
152d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            if (integer != null) {
153d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                row[integer] = value;
154d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            } else {
1556ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albert                LogUtils.e(TAG, "Unsupported column: " + columnName);
156d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            }
157d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        }
158d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov
159d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        static void addEmailAddress(MatrixCursor cursor, GalProjection galProjection,
16056d35257ef1493e849522c3c2828d904e4c5531fScott Kennedy                long contactId, String accountName, String displayName, String address) {
161d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            if (!TextUtils.isEmpty(address)) {
162222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                final GalContactRow r = new GalContactRow(
16356d35257ef1493e849522c3c2828d904e4c5531fScott Kennedy                        galProjection, contactId, accountName, displayName);
164d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                r.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
165d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                r.put(Email.TYPE, Email.TYPE_WORK);
166d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                r.put(Email.ADDRESS, address);
167d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                cursor.addRow(r.getRow());
168d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            }
169d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        }
170d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov
171d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        static void addPhoneRow(MatrixCursor cursor, GalProjection projection, long contactId,
17256d35257ef1493e849522c3c2828d904e4c5531fScott Kennedy                String accountName, String displayName, int type, String number) {
173d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            if (!TextUtils.isEmpty(number)) {
174222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                final GalContactRow r = new GalContactRow(
17556d35257ef1493e849522c3c2828d904e4c5531fScott Kennedy                        projection, contactId, accountName, displayName);
176d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                r.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
177d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                r.put(Phone.TYPE, type);
178d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                r.put(Phone.NUMBER, number);
179d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                cursor.addRow(r.getRow());
180d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            }
181d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        }
182d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov
183d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        public static void addNameRow(MatrixCursor cursor, GalProjection galProjection,
18456d35257ef1493e849522c3c2828d904e4c5531fScott Kennedy                long contactId, String accountName, String displayName,
185d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                String firstName, String lastName) {
186222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            final GalContactRow r = new GalContactRow(
18756d35257ef1493e849522c3c2828d904e4c5531fScott Kennedy                    galProjection, contactId, accountName, displayName);
188d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            r.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
189d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            r.put(StructuredName.GIVEN_NAME, firstName);
190d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            r.put(StructuredName.FAMILY_NAME, lastName);
191d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            r.put(StructuredName.DISPLAY_NAME, displayName);
192d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            cursor.addRow(r.getRow());
193d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        }
194d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov    }
195d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov
1967307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank    /**
1977307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank     * Find the record id of an Account, given its name (email address)
1987307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank     * @param accountName the name of the account
1997307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank     * @return the record id of the Account, or -1 if not found
2007307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank     */
2017307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank    /*package*/ long getAccountIdByName(Context context, String accountName) {
2027307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank        Long accountId = mAccountIdMap.get(accountName);
2037307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank        if (accountId == null) {
2047307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank            accountId = Utility.getFirstRowLong(context, Account.CONTENT_URI,
2057307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank                    EmailContent.ID_PROJECTION, AccountColumns.EMAIL_ADDRESS + "=?",
2067307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank                    new String[] {accountName}, null, EmailContent.ID_PROJECTION_COLUMN , -1L);
2077307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank            if (accountId != -1) {
2087307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank                mAccountIdMap.put(accountName, accountId);
2097307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank            }
2107307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank        }
2117307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank        return accountId;
2127307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank    }
2137307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank
2145aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank    @Override
2155aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
2165aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank            String sortOrder) {
2176ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albert        LogUtils.d(TAG, "ExchangeDirectoryProvider: query: %s", uri.toString());
218222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        final int match = sURIMatcher.match(uri);
219222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        final MatrixCursor cursor;
220d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        Object[] row;
221222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        final PackedString ps;
222222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        final String lookupKey;
223d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov
2245aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank        switch (match) {
2251f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov            case GAL_DIRECTORIES: {
2261f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                // Assuming that GAL can be used with all exchange accounts
227222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                final android.accounts.Account[] accounts = AccountManager.get(getContext())
228c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blank                        .getAccountsByType(Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE);
2291f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                cursor = new MatrixCursor(projection);
2301f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                if (accounts != null) {
2311f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                    for (android.accounts.Account account : accounts) {
2321f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                        row = new Object[projection.length];
2331f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov
2341f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                        for (int i = 0; i < projection.length; i++) {
235222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                            final String column = projection[i];
2361f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                            if (column.equals(Directory.ACCOUNT_NAME)) {
2371f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                                row[i] = account.name;
2381f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                            } else if (column.equals(Directory.ACCOUNT_TYPE)) {
2391f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                                row[i] = account.type;
2401f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                            } else if (column.equals(Directory.TYPE_RESOURCE_ID)) {
241222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                                final String accountType = Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE;
242222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                                final Bundle bundle = new AccountServiceProxy(getContext())
243d1771c4473a98c032c95ff66fa816043e08976f1Todd Kennedy                                    .getConfigurationData(accountType);
244c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blank                                // Default to the alternative name, erring on the conservative side
245c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blank                                int exchangeName = R.string.exchange_name_alternate;
246c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blank                                if (bundle != null && !bundle.getBoolean(
247c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blank                                        Configuration.EXCHANGE_CONFIGURATION_USE_ALTERNATE_STRINGS,
248c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blank                                        true)) {
249c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blank                                    exchangeName = R.string.exchange_name;
2501f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                                }
251c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blank                                row[i] = exchangeName;
2521f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                            } else if (column.equals(Directory.DISPLAY_NAME)) {
253aa408e086ac013cd822c4f6b5f4ad91ae442d02fDmitri Plotnikov                                // If the account name is an email address, extract
254aa408e086ac013cd822c4f6b5f4ad91ae442d02fDmitri Plotnikov                                // the domain name and use it as the directory display name
255aa408e086ac013cd822c4f6b5f4ad91ae442d02fDmitri Plotnikov                                final String accountName = account.name;
256222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                                final int atIndex = accountName.indexOf('@');
257aa408e086ac013cd822c4f6b5f4ad91ae442d02fDmitri Plotnikov                                if (atIndex != -1 && atIndex < accountName.length() - 2) {
258aa408e086ac013cd822c4f6b5f4ad91ae442d02fDmitri Plotnikov                                    final char firstLetter = Character.toUpperCase(
259aa408e086ac013cd822c4f6b5f4ad91ae442d02fDmitri Plotnikov                                            accountName.charAt(atIndex + 1));
260aa408e086ac013cd822c4f6b5f4ad91ae442d02fDmitri Plotnikov                                    row[i] = firstLetter + accountName.substring(atIndex + 2);
261aa408e086ac013cd822c4f6b5f4ad91ae442d02fDmitri Plotnikov                                } else {
262aa408e086ac013cd822c4f6b5f4ad91ae442d02fDmitri Plotnikov                                    row[i] = account.name;
263aa408e086ac013cd822c4f6b5f4ad91ae442d02fDmitri Plotnikov                                }
2641f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                            } else if (column.equals(Directory.EXPORT_SUPPORT)) {
2651f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                                row[i] = Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY;
2661f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                            } else if (column.equals(Directory.SHORTCUT_SUPPORT)) {
267831936a7ee4cbf93c847a66d7cd3062680676f22Dmitri Plotnikov                                row[i] = Directory.SHORTCUT_SUPPORT_NONE;
2681f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                            }
2691f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                        }
2701f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                        cursor.addRow(row);
2711f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                    }
2721f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                }
2731f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                return cursor;
2741f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov            }
2751f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov
276f6e831704c7df2b841760931469b1e995b957cd9Dmitri Plotnikov            case GAL_FILTER:
277222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            case GAL_PHONE_FILTER:
278f6e831704c7df2b841760931469b1e995b957cd9Dmitri Plotnikov            case GAL_EMAIL_FILTER: {
279222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                final String filter = uri.getLastPathSegment();
2805aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank                // We should have at least two characters before doing a GAL search
2815aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank                if (filter == null || filter.length() < 2) {
2825aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank                    return null;
2835aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank                }
2841f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov
285222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                final String accountName = uri.getQueryParameter(RawContacts.ACCOUNT_NAME);
2861f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                if (accountName == null) {
2871f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                    return null;
2881f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                }
2891f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov
290469544cd1b9652c446c96b97f4abbdb65d7e06aaMarc Blank                // Enforce a limit on the number of lookup responses
291222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                final String limitString = uri.getQueryParameter(ContactsContract.LIMIT_PARAM_KEY);
292469544cd1b9652c446c96b97f4abbdb65d7e06aaMarc Blank                int limit = DEFAULT_LOOKUP_LIMIT;
293469544cd1b9652c446c96b97f4abbdb65d7e06aaMarc Blank                if (limitString != null) {
294469544cd1b9652c446c96b97f4abbdb65d7e06aaMarc Blank                    try {
295469544cd1b9652c446c96b97f4abbdb65d7e06aaMarc Blank                        limit = Integer.parseInt(limitString);
296469544cd1b9652c446c96b97f4abbdb65d7e06aaMarc Blank                    } catch (NumberFormatException e) {
297469544cd1b9652c446c96b97f4abbdb65d7e06aaMarc Blank                        limit = 0;
298469544cd1b9652c446c96b97f4abbdb65d7e06aaMarc Blank                    }
299469544cd1b9652c446c96b97f4abbdb65d7e06aaMarc Blank                    if (limit <= 0) {
300469544cd1b9652c446c96b97f4abbdb65d7e06aaMarc Blank                        throw new IllegalArgumentException("Limit not valid: " + limitString);
301469544cd1b9652c446c96b97f4abbdb65d7e06aaMarc Blank                    }
302469544cd1b9652c446c96b97f4abbdb65d7e06aaMarc Blank                }
303469544cd1b9652c446c96b97f4abbdb65d7e06aaMarc Blank
304222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                final long callingId = Binder.clearCallingIdentity();
3055aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank                try {
3067307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank                    // Find the account id to pass along to EasSyncService
307222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                    final long accountId = getAccountIdByName(getContext(), accountName);
3087307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank                    if (accountId == -1) {
3097307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank                        // The account was deleted?
3107307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank                        return null;
3117307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank                    }
3127307f7e5c2c4f85f6f3e0ebaf5775f65cba4b20cMarc Blank
313e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    final boolean isEmail = match == GAL_EMAIL_FILTER;
314e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    final boolean isPhone = match == GAL_PHONE_FILTER;
315e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    // For phone filter queries we request more results from the server
316e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    // than requested by the caller because we omit contacts without
317e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    // phone numbers, and the server lacks the ability to do this filtering
318e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    // for us. We then enforce the limit when constructing the cursor
319e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    // containing the results.
320e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    int queryLimit = limit;
321e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    if (isPhone) {
322e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                        queryLimit = 3 * queryLimit;
323e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    }
324e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    if (queryLimit > MAX_LOOKUP_LIMIT) {
325e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                        queryLimit = MAX_LOOKUP_LIMIT;
326e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    }
327e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner
3285aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank                    // Get results from the Exchange account
329bb0141b49e7eff978fa445249dc888461ea581e3Martin Hibdon                    final GalResult galResult = EasService.searchGal(getContext(), accountId,
330e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                            filter, queryLimit);
3312db0b68170b046b41c0319768d7018d093e536a4Anthony Lee                    if (galResult != null && (galResult.getNumEntries() > 0)) {
332e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                         return buildGalResultCursor(
333e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                                 projection, galResult, sortOrder, limit, isEmail, isPhone);
3345aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank                    }
3355aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank                } finally {
3365aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank                    Binder.restoreCallingIdentity(callingId);
3375aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank                }
3385aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank                break;
339d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            }
340d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov
341d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            case GAL_CONTACT:
342d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            case GAL_CONTACT_WITH_ID: {
343222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                final String accountName = uri.getQueryParameter(RawContacts.ACCOUNT_NAME);
3441f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                if (accountName == null) {
3451f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                    return null;
3461f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov                }
3471f4b000ebe72fe9e71082031f48d0133914bda2dDmitri Plotnikov
348222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                final GalProjection galProjection = new GalProjection(projection);
349d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                cursor = new MatrixCursor(projection);
350d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                // Handle the decomposition of the key into rows suitable for CP2
351222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                final List<String> pathSegments = uri.getPathSegments();
352d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                lookupKey = pathSegments.get(2);
353222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                final long contactId = (match == GAL_CONTACT_WITH_ID)
354d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                        ? Long.parseLong(pathSegments.get(3))
355d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                        : DEFAULT_CONTACT_ID;
356d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                ps = new PackedString(lookupKey);
357222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                final String displayName = ps.get(GalData.DISPLAY_NAME);
35856d35257ef1493e849522c3c2828d904e4c5531fScott Kennedy                GalContactRow.addEmailAddress(cursor, galProjection, contactId,
359d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                        accountName, displayName, ps.get(GalData.EMAIL_ADDRESS));
36056d35257ef1493e849522c3c2828d904e4c5531fScott Kennedy                GalContactRow.addPhoneRow(cursor, galProjection, contactId,
361d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                        displayName, displayName, Phone.TYPE_HOME, ps.get(GalData.HOME_PHONE));
36256d35257ef1493e849522c3c2828d904e4c5531fScott Kennedy                GalContactRow.addPhoneRow(cursor, galProjection, contactId,
363d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                        displayName, displayName, Phone.TYPE_WORK, ps.get(GalData.WORK_PHONE));
36456d35257ef1493e849522c3c2828d904e4c5531fScott Kennedy                GalContactRow.addPhoneRow(cursor, galProjection, contactId,
365d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                        displayName, displayName, Phone.TYPE_MOBILE, ps.get(GalData.MOBILE_PHONE));
36656d35257ef1493e849522c3c2828d904e4c5531fScott Kennedy                GalContactRow.addNameRow(cursor, galProjection, contactId, displayName,
367d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                        ps.get(GalData.FIRST_NAME), ps.get(GalData.LAST_NAME), displayName);
368d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                return cursor;
369d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            }
3705aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank        }
3715aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank
3725aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank        return null;
3735aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank    }
3745aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank
375222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert    /*package*/ Cursor buildGalResultCursor(String[] projection, GalResult galResult,
376e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            String sortOrder, int limit, boolean isEmailFilter, boolean isPhoneFilter) {
3775aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank        int displayNameIndex = -1;
378222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        int displayNameSourceIndex = -1;
379222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        int alternateDisplayNameIndex = -1;
3805aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank        int emailIndex = -1;
381222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        int emailTypeIndex = -1;
382222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        int phoneNumberIndex = -1;
383222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        int phoneTypeIndex = -1;
384222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        int hasPhoneNumberIndex = -1;
385d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        int idIndex = -1;
386222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        int contactIdIndex = -1;
387d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov        int lookupIndex = -1;
3885aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank
3895aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank        for (int i = 0; i < projection.length; i++) {
390222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            final String column = projection[i];
3915aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank            if (Contacts.DISPLAY_NAME.equals(column) ||
3925aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank                    Contacts.DISPLAY_NAME_PRIMARY.equals(column)) {
3935aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank                displayNameIndex = i;
3945aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank            } else if (Contacts.DISPLAY_NAME_ALTERNATIVE.equals(column)) {
395d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                alternateDisplayNameIndex = i;
396222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            } else if (Contacts.DISPLAY_NAME_SOURCE.equals(column)) {
397222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                displayNameSourceIndex = i;
398222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            } else if (Contacts.HAS_PHONE_NUMBER.equals(column)) {
399222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                hasPhoneNumberIndex = i;
400d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            } else if (Contacts._ID.equals(column)) {
401d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                idIndex = i;
402222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            } else if (Phone.CONTACT_ID.equals(column)) {
403222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                contactIdIndex = i;
404d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            } else if (Contacts.LOOKUP_KEY.equals(column)) {
405d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov                lookupIndex = i;
406222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            } else if (isPhoneFilter) {
407222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                if (Phone.NUMBER.equals(column)) {
408222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                    phoneNumberIndex = i;
409222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                } else if (Phone.TYPE.equals(column)) {
410222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                    phoneTypeIndex = i;
411222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                }
412222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            } else {
413222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                // Cannot support for Email and Phone in same query, so default
414222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                // is to return email addresses.
415222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                if (Email.ADDRESS.equals(column)) {
416222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                    emailIndex = i;
417222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                } else if (Email.TYPE.equals(column)) {
418222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                    emailTypeIndex = i;
419222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                }
4205aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank            }
4215aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank        }
4225aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank
423e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        boolean usePrimarySortKey = false;
424e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        boolean useAlternateSortKey = false;
425e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        if (Contacts.SORT_KEY_PRIMARY.equals(sortOrder)) {
426e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            usePrimarySortKey = true;
427e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        } else if (Contacts.SORT_KEY_ALTERNATIVE.equals(sortOrder)) {
428e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            useAlternateSortKey = true;
429e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        } else if (sortOrder != null && sortOrder.length() > 0) {
430e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            Log.w(TAG, "Ignoring unsupported sort order: " + sortOrder);
431e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        }
4325aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank
433222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        final TreeMap<GalSortKey, Object[]> sortedResultsMap =
434222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                new TreeMap<GalSortKey, Object[]>(new NameComparator());
435222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
436222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        // id populates the _ID column and is incremented for each row in the
437222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        // result set, so each row has a unique id.
438222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        int id = 1;
439222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        // contactId populates the CONTACT_ID column and is incremented for
440222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        // each contact. For the email and phone filters, there may be more
441222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        // than one row with the same contactId if a given contact has multiple
442222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        // email addresses or multiple phone numbers.
443222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        int contactId = 1;
444222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
445222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        final int count = galResult.galData.size();
4465aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank        for (int i = 0; i < count; i++) {
447222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            final GalData galDataRow = galResult.galData.get(i);
448222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
449e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            final List<PhoneInfo> phones = new ArrayList<PhoneInfo>();
450222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            addPhoneInfo(phones, galDataRow.get(GalData.WORK_PHONE), Phone.TYPE_WORK);
451222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            addPhoneInfo(phones, galDataRow.get(GalData.OFFICE), Phone.TYPE_COMPANY_MAIN);
452222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            addPhoneInfo(phones, galDataRow.get(GalData.HOME_PHONE), Phone.TYPE_HOME);
453222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            addPhoneInfo(phones, galDataRow.get(GalData.MOBILE_PHONE), Phone.TYPE_MOBILE);
454222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
455e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            // Track whether we added a result for this contact or not, in
456e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            // order to stop once we have maxResult contacts.
457e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            boolean addedContact = false;
458e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner
459e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            Pair<String, Integer> displayName = getDisplayName(galDataRow, phones);
460e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            if (TextUtils.isEmpty(displayName.first)) {
461e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                // can't use a contact if we can't find a decent name for it.
462e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                continue;
463d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            }
464e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            galDataRow.put(GalData.DISPLAY_NAME, displayName.first);
465d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov
466e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            final String alternateDisplayName = getAlternateDisplayName(
467e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    galDataRow, displayName.first);
468e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            final String sortName = usePrimarySortKey ? displayName.first
469e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                : (useAlternateSortKey ? alternateDisplayName : "");
470e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            final Object[] row = new Object[projection.length];
4715aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank            if (displayNameIndex != -1) {
472e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                row[displayNameIndex] = displayName.first;
473d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            }
474e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            if (displayNameSourceIndex != -1) {
475e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                row[displayNameSourceIndex] = displayName.second;
476222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            }
477222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
478d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            if (alternateDisplayNameIndex != -1) {
479222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                row[alternateDisplayNameIndex] = alternateDisplayName;
4805aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank            }
481222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
482e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            if (hasPhoneNumberIndex != -1) {
483222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                if (phones.size() > 0) {
484222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                    row[hasPhoneNumberIndex] = true;
485222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                }
486d41eaba645dcb4573c5dc33077fc87799ef06195Dmitri Plotnikov            }
487222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
488222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            if (contactIdIndex != -1) {
489222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                row[contactIdIndex] = contactId;
4905aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank            }
491222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
4928999766c3798c606840477abd203fc02fb55aa13Yu Ping Hu            if (lookupIndex != -1) {
4938999766c3798c606840477abd203fc02fb55aa13Yu Ping Hu                // We use the packed string as our lookup key; it contains ALL of the gal data
4948999766c3798c606840477abd203fc02fb55aa13Yu Ping Hu                // We do this because we are not able to provide a stable id to ContactsProvider
4958999766c3798c606840477abd203fc02fb55aa13Yu Ping Hu                row[lookupIndex] = Uri.encode(galDataRow.toPackedString());
4968999766c3798c606840477abd203fc02fb55aa13Yu Ping Hu            }
4978999766c3798c606840477abd203fc02fb55aa13Yu Ping Hu
498222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            if (isPhoneFilter) {
499222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                final Set<String> uniqueNumbers = new HashSet<String>();
500222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
501222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                for (PhoneInfo phone : phones) {
502222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                    if (!uniqueNumbers.add(phone.mNumber)) {
503222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                        continue;
504222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                    }
505e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    if (phoneNumberIndex != -1) {
506222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                        row[phoneNumberIndex] = phone.mNumber;
507222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                    }
508e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    if (phoneTypeIndex != -1) {
509222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                        row[phoneTypeIndex] = phone.mType;
510222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                    }
511222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                    if (idIndex != -1) {
512222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                        row[idIndex] = id;
513222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                    }
514222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                    sortedResultsMap.put(new GalSortKey(sortName, id), row.clone());
515e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    addedContact = true;
516222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                    id++;
517222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                }
518222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
519222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            } else {
520e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                boolean haveEmail = false;
521e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                Object address = galDataRow.get(GalData.EMAIL_ADDRESS);
522e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                if (address != null && !TextUtils.isEmpty(address.toString())) {
523e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    if (emailIndex != -1) {
524e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                        row[emailIndex] = address;
525e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    }
526e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    if (emailTypeIndex != -1) {
527e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                        row[emailTypeIndex] = Email.TYPE_WORK;
528e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    }
529e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    haveEmail = true;
530222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                }
531222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
532e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                if (!isEmailFilter || haveEmail) {
533e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    if (idIndex != -1) {
534e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                        row[idIndex] = id;
535e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    }
536e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    sortedResultsMap.put(new GalSortKey(sortName, id), row.clone());
537e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    addedContact = true;
538e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    id++;
539e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                }
540e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            }
541e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            if (addedContact) {
542e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                contactId++;
543e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                if (contactId > limit) {
544e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                    break;
545222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert                }
546222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            }
547222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        }
548222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        final MatrixCursor cursor = new MatrixCursor(projection, sortedResultsMap.size());
549222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        for(Object[] result : sortedResultsMap.values()) {
550222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            cursor.addRow(result);
5515aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank        }
552222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
5535aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank        return cursor;
5545aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank    }
5555aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank
556e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner    /**
557e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner     * Try to create a display name from various fields.
558e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner     *
559e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner     * @return a display name for contact and its source
560e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner     */
561e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner    private static Pair<String, Integer> getDisplayName(GalData galDataRow, List<PhoneInfo> phones) {
562e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        String displayName = galDataRow.get(GalData.DISPLAY_NAME);
563e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        if (!TextUtils.isEmpty(displayName)) {
564e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            return Pair.create(displayName, DisplayNameSources.STRUCTURED_NAME);
565e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        }
566e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner
567e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        // try to get displayName from name fields
568e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        final String firstName = galDataRow.get(GalData.FIRST_NAME);
569e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        final String lastName = galDataRow.get(GalData.LAST_NAME);
570e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        if (!TextUtils.isEmpty(firstName) || !TextUtils.isEmpty(lastName)) {
571e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            if (!TextUtils.isEmpty(firstName) && !TextUtils.isEmpty(lastName)) {
572e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                displayName = firstName + " " + lastName;
573e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            } else if (!TextUtils.isEmpty(firstName)) {
574e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                displayName = firstName;
575e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            } else {
576e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                displayName = lastName;
577e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            }
578e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            return Pair.create(displayName, DisplayNameSources.STRUCTURED_NAME);
579e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        }
580e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner
581e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        // try to get displayName from email
582e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        final String emailAddress = galDataRow.get(GalData.EMAIL_ADDRESS);
583e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        if (!TextUtils.isEmpty(emailAddress)) {
584e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            return Pair.create(emailAddress, DisplayNameSources.EMAIL);
585e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        }
586e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner
587e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        // try to get displayName from phone numbers
588e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        if (phones != null && phones.size() > 0) {
589e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            final PhoneInfo phone = (PhoneInfo) phones.get(0);
590e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            if (phone != null && !TextUtils.isEmpty(phone.mNumber)) {
591e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                return Pair.create(phone.mNumber, DisplayNameSources.PHONE);
592e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            }
593e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        }
594e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        return Pair.create(null, null);
595e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner    }
596e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner
597e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner    /**
598e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner     * Try to create the alternate display name from various fields. The CP2
599e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner     * Alternate Display Name field is LastName FirstName to support user
600e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner     * choice of how to order names for display.
601e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner     *
602e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner     * @return alternate display name for contact and its source
603e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner     */
604e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner    private static String getAlternateDisplayName(GalData galDataRow, String displayName) {
605e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        // try to get displayName from name fields
606e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        final String firstName = galDataRow.get(GalData.FIRST_NAME);
607e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        final String lastName = galDataRow.get(GalData.LAST_NAME);
608e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        if (!TextUtils.isEmpty(firstName) && !TextUtils.isEmpty(lastName)) {
609e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            return lastName + " " + firstName;
610e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        } else if (!TextUtils.isEmpty(lastName)) {
611e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            return lastName;
612e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        }
613e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner        return displayName;
614e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner    }
615e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner
616222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert    private void addPhoneInfo(List<PhoneInfo> phones, String number, int type) {
6178999766c3798c606840477abd203fc02fb55aa13Yu Ping Hu        if (!TextUtils.isEmpty(number)) {
618222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            phones.add(new PhoneInfo(number, type));
619222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        }
620222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert    }
621222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
6225aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank    @Override
6235aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank    public String getType(Uri uri) {
624222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        final int match = sURIMatcher.match(uri);
6255aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank        switch (match) {
6265aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank            case GAL_FILTER:
6275aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank                return Contacts.CONTENT_ITEM_TYPE;
6285aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank        }
6295aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank        return null;
6305aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank    }
6315aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank
6325aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank    @Override
6335aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank    public int delete(Uri uri, String selection, String[] selectionArgs) {
6345aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank        throw new UnsupportedOperationException();
6355aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank    }
6365aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank
6375aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank    @Override
6385aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank    public Uri insert(Uri uri, ContentValues values) {
6395aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank        throw new UnsupportedOperationException();
6405aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank    }
6415aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank
6425aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank    @Override
6435aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
6445aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank        throw new UnsupportedOperationException();
6455aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank    }
646222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
647222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert    /**
648222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert     * Sort key for Gal filter results.
649222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert     *  - primary key is name
650222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert     *      for SORT_KEY_PRIMARY, this is displayName
651222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert     *      for SORT_KEY_ALTERNATIVE, this is alternativeDisplayName
652222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert     *      if no sort order is specified, this key is empty
653222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert     *  - secondary key is id, so ordering of the original results are
654222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert     *      preserved both between contacts with the same name and for
655222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert     *      multiple results within a given contact
656222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert     */
657674df0303129dcfa54a2cee2bf7e11195986220bAnthony Lee    protected static class GalSortKey {
658222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        final String sortName;
659674df0303129dcfa54a2cee2bf7e11195986220bAnthony Lee        final int id;
660222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
661674df0303129dcfa54a2cee2bf7e11195986220bAnthony Lee        public GalSortKey(final String sortName, final int id) {
662222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            this.sortName = sortName;
663222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            this.id = id;
664222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        }
665222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert    }
666222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
667674df0303129dcfa54a2cee2bf7e11195986220bAnthony Lee    /**
668674df0303129dcfa54a2cee2bf7e11195986220bAnthony Lee     * The Comparator that is used by ExchangeDirectoryProvider
669674df0303129dcfa54a2cee2bf7e11195986220bAnthony Lee     */
670674df0303129dcfa54a2cee2bf7e11195986220bAnthony Lee    protected static class NameComparator implements Comparator<GalSortKey> {
671222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        private final Collator collator;
672222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
673222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        public NameComparator() {
674222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            collator = Collator.getInstance();
675222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            // Case insensitive sorting
676222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            collator.setStrength(Collator.SECONDARY);
677222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        }
678222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
679222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        @Override
680674df0303129dcfa54a2cee2bf7e11195986220bAnthony Lee        public int compare(final GalSortKey lhs, final GalSortKey rhs) {
681674df0303129dcfa54a2cee2bf7e11195986220bAnthony Lee            if (lhs.sortName != null && rhs.sortName != null) {
682e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                final int res = collator.compare(lhs.sortName, rhs.sortName);
683674df0303129dcfa54a2cee2bf7e11195986220bAnthony Lee                if (res != 0) {
684674df0303129dcfa54a2cee2bf7e11195986220bAnthony Lee                    return res;
685674df0303129dcfa54a2cee2bf7e11195986220bAnthony Lee                }
686e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            } else if (lhs.sortName != null) {
687e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                return 1;
688e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            } else if (rhs.sortName != null) {
689e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                return -1;
690222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            }
691e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner
692e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            // Either the names compared equally or both were null, use the id to compare.
693e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            if (lhs.id != rhs.id) {
694e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner                return lhs.id > rhs.id ? 1 : -1;
695222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            }
696e7e74f5d67647510a0394dcc8346a9cd7781d970Jay Shrauner            return 0;
697222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        }
698222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert    }
699222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
700222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert    private static class PhoneInfo {
701222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        private String mNumber;
702222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        private int mType;
703222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert
704222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        private PhoneInfo(String number, int type) {
705222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            mNumber = number;
706222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert            mType = type;
707222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert        }
708222dec5216626980f1450d8917e9b73e2d4c9040Alon Albert    }
7095aec61dc15c62cff0cf55a3d6cb483f9e338230aMarc Blank}
710