BaseEmailAddressAdapter.java revision 5cc774535d73c09b6788b63ecc728e60da09cfa9
15cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov/*
25cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov * Copyright (C) 2010 The Android Open Source Project
35cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov *
45cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov * Licensed under the Apache License, Version 2.0 (the "License");
55cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov * you may not use this file except in compliance with the License.
65cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov * You may obtain a copy of the License at
75cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov *
85cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov *      http://www.apache.org/licenses/LICENSE-2.0
95cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov *
105cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov * Unless required by applicable law or agreed to in writing, software
115cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov * distributed under the License is distributed on an "AS IS" BASIS,
125cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov * See the License for the specific language governing permissions and
145cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov * limitations under the License.
155cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov */
165cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
175cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovpackage com.android.common.contacts;
185cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
195cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport com.android.common.widget.CompositeCursorAdapter;
205cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
215cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.accounts.Account;
225cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.app.Activity;
235cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.content.ContentResolver;
245cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.content.Context;
255cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.content.pm.PackageManager;
265cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.content.pm.PackageManager.NameNotFoundException;
275cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.content.res.Resources;
285cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.database.Cursor;
295cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.database.MatrixCursor;
305cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.net.Uri;
315cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.provider.ContactsContract;
325cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Email;
335cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.provider.ContactsContract.Contacts;
345cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.provider.ContactsContract.Directory;
355cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.text.TextUtils;
365cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.text.util.Rfc822Token;
375cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.util.Log;
385cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.view.View;
395cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.view.ViewGroup;
405cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.widget.Filter;
415cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport android.widget.Filterable;
425cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
435cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport java.util.ArrayList;
445cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovimport java.util.List;
455cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
465cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov/**
475cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov * A base class for email address autocomplete adapters. It uses
485cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov * {@link Email#CONTENT_FILTER_URI} to search for data rows by email address
495cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov * and/or contact name. It also searches registered {@link Directory}'s.
505cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov */
515cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikovpublic abstract class BaseEmailAddressAdapter extends CompositeCursorAdapter implements Filterable {
525cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
535cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    private static final String TAG = "BaseEmailAddressAdapter";
545cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
555cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    /**
565cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     * Model object for a {@link Directory} row. There is a partition in the
575cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     * {@link CompositeCursorAdapter} for every directory (except
585cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     * {@link Directory#LOCAL_INVISIBLE}.
595cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     */
605cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    public final static class DirectoryPartition extends CompositeCursorAdapter.Partition {
615cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public long directoryId;
625cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public String directoryType;
635cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public String displayName;
645cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public String accountName;
655cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public String accountType;
665cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public boolean loading;
675cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public CharSequence constraint;
685cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public DirectoryPartitionFilter filter;
695cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
705cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public DirectoryPartition() {
715cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            super(false, false);
725cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        }
735cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    }
745cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
755cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    private static class EmailQuery {
765cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public static final String[] PROJECTION = {
775cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            Contacts.DISPLAY_NAME,  // 0
785cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            Email.ADDRESS           // 1
795cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        };
805cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
815cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public static final int NAME = 0;
825cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public static final int ADDRESS = 1;
835cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    }
845cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
855cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    private static class DirectoryListQuery {
865cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public static final String[] PROJECTION = {
875cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            Directory._ID,              // 0
885cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            Directory.ACCOUNT_NAME,     // 1
895cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            Directory.ACCOUNT_TYPE,     // 2
905cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            Directory.DISPLAY_NAME,     // 3
915cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            Directory.PACKAGE_NAME,     // 4
925cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            Directory.TYPE_RESOURCE_ID, // 5
935cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        };
945cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
955cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public static final int ID = 0;
965cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public static final int ACCOUNT_NAME = 1;
975cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public static final int ACCOUNT_TYPE = 2;
985cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public static final int DISPLAY_NAME = 3;
995cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public static final int PACKAGE_NAME = 4;
1005cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public static final int TYPE_RESOURCE_ID = 5;
1015cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    }
1025cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
1035cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    /**
1045cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     * A fake column name that indicates a "Searching..." item in the list.
1055cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     */
1065cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    private static final String SEARCHING_CURSOR_MARKER = "searching";
1075cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
1085cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    /**
1095cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     * An asynchronous filter used for loading two data sets: email rows from the local
1105cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     * contact provider and the list of {@link Directory}'s.
1115cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     */
1125cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    private final class DefaultPartitionFilter extends Filter {
1135cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        @Override
1145cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        protected FilterResults performFiltering(CharSequence constraint) {
1155cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            Cursor directoryCursor = null;
1165cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            if (!mDirectoriesLoaded) {
1175cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                directoryCursor = mContentResolver.query(
1185cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                        Directory.CONTENT_URI, DirectoryListQuery.PROJECTION, null, null, null);
1195cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                mDirectoriesLoaded = true;
1205cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            }
1215cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
1225cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            FilterResults results = new FilterResults();
1235cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            Cursor cursor = null;
1245cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            if (!TextUtils.isEmpty(constraint)) {
1255cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                Uri uri = Uri.withAppendedPath(
1265cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                        Email.CONTENT_FILTER_URI, Uri.encode(constraint.toString()));
1275cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                cursor = mContentResolver.query(uri, EmailQuery.PROJECTION, null, null, null);
1285cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                results.count = cursor.getCount();
1295cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            }
1305cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            results.values = new Cursor[] { directoryCursor, cursor };
1315cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            return results;
1325cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        }
1335cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
1345cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        @Override
1355cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        protected void publishResults(CharSequence constraint, FilterResults results) {
1365cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            if (results.values != null) {
1375cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                Cursor[] cursors = (Cursor[]) results.values;
1385cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                onDirectoryLoadFinished(constraint, cursors[0], cursors[1]);
1395cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            }
1405cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            results.count = getCount();
1415cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        }
1425cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
1435cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        @Override
1445cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public CharSequence convertResultToString(Object resultValue) {
1455cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            return makeDisplayString((Cursor) resultValue);
1465cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        }
1475cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    }
1485cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
1495cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    /**
1505cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     * An asynchronous filter that performs search in a particular directory.
1515cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     */
1525cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    private final class DirectoryPartitionFilter extends Filter {
1535cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
1545cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        private final int mPartitionIndex;
1555cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        private final long mDirectoryId;
1565cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
1575cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        public DirectoryPartitionFilter(int partitionIndex, long directoryId) {
1585cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            this.mPartitionIndex = partitionIndex;
1595cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            this.mDirectoryId = directoryId;
1605cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        }
1615cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
1625cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        @Override
1635cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        protected FilterResults performFiltering(CharSequence constraint) {
1645cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            FilterResults results = new FilterResults();
1655cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            if (!TextUtils.isEmpty(constraint)) {
1665cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                Uri uri = Email.CONTENT_FILTER_URI.buildUpon()
1675cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                        .appendPath(constraint.toString())
1685cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                        .appendQueryParameter(
1695cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                                ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(mDirectoryId))
1705cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                        .build();
1715cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                Cursor cursor = mContentResolver.query(
1725cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                        uri, EmailQuery.PROJECTION, null, null, null);
1735cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                results.values = cursor;
1745cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            }
1755cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            return results;
1765cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        }
1775cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
1785cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        @Override
1795cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        protected void publishResults(CharSequence constraint, FilterResults results) {
1805cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            Cursor cursor = (Cursor) results.values;
1815cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            onPartitionLoadFinished(constraint, mPartitionIndex, cursor);
1825cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            results.count = getCount();
1835cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        }
1845cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    }
1855cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
1865cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    protected final ContentResolver mContentResolver;
1875cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    private boolean mDirectoriesLoaded;
1885cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    private Account mAccount;
1895cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
1905cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    public BaseEmailAddressAdapter(Context context) {
1915cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        super(context);
1925cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        mContentResolver = context.getContentResolver();
1935cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    }
1945cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
1955cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    /**
1965cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     * Set the account when known. Causes the search to prioritize contacts from
1975cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     * that account.
1985cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     */
1995cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    public void setAccount(Account account) {
2005cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        mAccount = account;
2015cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    }
2025cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
2035cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    /**
2045cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     * Override to create a view for line item in the autocomplete suggestion list UI.
2055cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     */
2065cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    protected abstract View inflateItemView(ViewGroup parent);
2075cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
2085cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    /**
2095cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     * Override to populate the autocomplete suggestion line item UI with data.
2105cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     */
2115cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    protected abstract void bindView(View view, String directoryType, String directoryName,
2125cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            String displayName, String emailAddress);
2135cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
2145cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    /**
2155cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     * Override to create a view for a "Searching directory" line item, which is
2165cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     * displayed temporarily while the corresponding filter is running.
2175cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     */
2185cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    protected abstract View inflateItemViewLoading(ViewGroup parent);
2195cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
2205cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    /**
2215cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     * Override to populate the "Searching directory" line item UI with data.
2225cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     */
2235cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    protected abstract void bindViewLoading(View view, String directoryType, String directoryName);
2245cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
2255cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    @Override
2265cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    protected int getItemViewType(int partitionIndex, int position) {
2275cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        DirectoryPartition partition = (DirectoryPartition)getPartition(partitionIndex);
2285cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        return partition.loading ? 1 : 0;
2295cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    }
2305cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
2315cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    @Override
2325cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    protected View newView(Context context, int partitionIndex, Cursor cursor,
2335cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            int position, ViewGroup parent) {
2345cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        DirectoryPartition partition = (DirectoryPartition)getPartition(partitionIndex);
2355cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        if (partition.loading) {
2365cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            return inflateItemViewLoading(parent);
2375cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        } else {
2385cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            return inflateItemView(parent);
2395cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        }
2405cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    }
2415cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
2425cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    @Override
2435cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    protected void bindView(View v, int partition, Cursor cursor, int position) {
2445cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        DirectoryPartition directoryPartition = (DirectoryPartition)getPartition(partition);
2455cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        String directoryType = directoryPartition.directoryType;
2465cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        String directoryName = directoryPartition.displayName;
2475cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        if (directoryPartition.loading) {
2485cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            bindViewLoading(v, directoryType, directoryName);
2495cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        } else {
2505cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            String displayName = cursor.getString(EmailQuery.NAME);
2515cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            String emailAddress = cursor.getString(EmailQuery.ADDRESS);
2525cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            bindView(v, directoryType, directoryName, displayName, emailAddress);
2535cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        }
2545cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    }
2555cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
2565cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    @Override
2575cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    public boolean areAllItemsEnabled() {
2585cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        return false;
2595cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    }
2605cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
2615cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    @Override
2625cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    protected boolean isEnabled(int partitionIndex, int position) {
2635cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        // The "Searching..." item should not be selectable
2645cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        return !((DirectoryPartition)getPartition(partitionIndex)).loading;
2655cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    }
2665cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
2675cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    @Override
2685cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    public Filter getFilter() {
2695cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        return new DefaultPartitionFilter();
2705cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    }
2715cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
2725cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    /**
2735cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     * Handles the result of the initial call, which brings back the list of
2745cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     * directories as well as the search results for the local directories.
2755cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     */
2765cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    protected void onDirectoryLoadFinished(
2775cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            CharSequence constraint, Cursor directoryCursor, Cursor defaultPartitionCursor) {
2785cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        if (directoryCursor != null) {
2795cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            PackageManager packageManager = getContext().getPackageManager();
2805cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            DirectoryPartition preferredDirectory = null;
2815cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            List<DirectoryPartition> directories = new ArrayList<DirectoryPartition>();
2825cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            while (directoryCursor.moveToNext()) {
2835cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                long id = directoryCursor.getLong(DirectoryListQuery.ID);
2845cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
2855cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                // Skip the local invisible directory, because the default directory
2865cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                // already includes all local results.
2875cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                if (id == Directory.LOCAL_INVISIBLE) {
2885cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                    continue;
2895cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                }
2905cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
2915cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                DirectoryPartition partition = new DirectoryPartition();
2925cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                partition.directoryId = id;
2935cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                partition.displayName = directoryCursor.getString(DirectoryListQuery.DISPLAY_NAME);
2945cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                partition.accountName = directoryCursor.getString(DirectoryListQuery.ACCOUNT_NAME);
2955cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                partition.accountType = directoryCursor.getString(DirectoryListQuery.ACCOUNT_TYPE);
2965cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                String packageName = directoryCursor.getString(DirectoryListQuery.PACKAGE_NAME);
2975cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                int resourceId = directoryCursor.getInt(DirectoryListQuery.TYPE_RESOURCE_ID);
2985cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                if (packageName != null && resourceId != 0) {
2995cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                    try {
3005cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                        Resources resources =
3015cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                                packageManager.getResourcesForApplication(packageName);
3025cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                        partition.directoryType = resources.getString(resourceId);
3035cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                        if (partition.directoryType == null) {
3045cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                            Log.e(TAG, "Cannot resolve directory name: "
3055cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                                    + resourceId + "@" + packageName);
3065cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                        }
3075cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                    } catch (NameNotFoundException e) {
3085cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                        Log.e(TAG, "Cannot resolve directory name: "
3095cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                                + resourceId + "@" + packageName, e);
3105cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                    }
3115cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                }
3125cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
3135cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                // If an account has been provided and we found a directory that
3145cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                // corresponds to that account, place that directory second, directly
3155cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                // underneath the local contacts.
3165cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                if (mAccount != null && mAccount.name.equals(partition.accountName) &&
3175cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                        mAccount.type.equals(partition.accountType)) {
3185cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                    preferredDirectory = partition;
3195cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                } else {
3205cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                    directories.add(partition);
3215cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                }
3225cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            }
3235cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
3245cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            if (preferredDirectory != null) {
3255cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                directories.add(1, preferredDirectory);
3265cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            }
3275cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
3285cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            for (DirectoryPartition partition : directories) {
3295cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                addPartition(partition);
3305cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            }
3315cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        }
3325cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
3335cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        // The filter has loaded results for the default partition too.
3345cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        if (defaultPartitionCursor != null && getPartitionCount() > 0) {
3355cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            changeCursor(0, defaultPartitionCursor);
3365cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        }
3375cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
3385cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        // Start search in other directories
3395cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        int count = getPartitionCount();
3405cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        // Note: skipping the default partition (index 0), which has already been loaded
3415cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        for (int i = 1; i < count; i++) {
3425cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            DirectoryPartition partition = (DirectoryPartition) getPartition(i);
3435cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            partition.constraint = constraint;
3445cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
3455cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            if (!partition.loading) {
3465cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                partition.loading = true;
3475cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                changeCursor(i, createLoadingCursor());
3485cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            }
3495cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
3505cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            if (partition.filter == null) {
3515cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                partition.filter = new DirectoryPartitionFilter(i, partition.directoryId);
3525cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            }
3535cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            partition.filter.filter(constraint);
3545cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        }
3555cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    }
3565cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
3575cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    /**
3585cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     * Creates a dummy cursor to represent the "Searching directory..." item.
3595cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov     */
3605cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    private Cursor createLoadingCursor() {
3615cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(new String[]{SEARCHING_CURSOR_MARKER});
3625cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        cursor.addRow(new Object[]{""});
3635cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        return cursor;
3645cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    }
3655cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
3665cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    public void onPartitionLoadFinished(
3675cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            CharSequence constraint, int partitionIndex, Cursor cursor) {
3685cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        if (partitionIndex < getPartitionCount()) {
3695cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            DirectoryPartition partition = (DirectoryPartition) getPartition(partitionIndex);
3705cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            // Check if the received result matches the current constraint
3715cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            // If not - the user must have continued typing after the request
3725cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            // was issued
3735cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            if (TextUtils.equals(constraint, partition.constraint)) {
3745cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                partition.loading = false;
3755cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                changeCursor(partitionIndex, cursor);
3765cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            } else {
3775cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                // We got the result for an unexpected query (the user is still typing)
3785cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                // Just ignore this result
3795cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                if (cursor != null) {
3805cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                    cursor.close();
3815cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov                }
3825cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            }
3835cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        } else if (cursor != null) {
3845cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            cursor.close();
3855cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        }
3865cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    }
3875cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
3885cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    private final String makeDisplayString(Cursor cursor) {
3895cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        if (cursor.getColumnName(0).equals(SEARCHING_CURSOR_MARKER)) {
3905cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov            return "";
3915cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        }
3925cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov
3935cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        String name = cursor.getString(EmailQuery.NAME);
3945cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        String address = cursor.getString(EmailQuery.ADDRESS);
3955cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov        return new Rfc822Token(name, address, null).toString();
3965cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov    }
3975cc774535d73c09b6788b63ecc728e60da09cfa9Dmitri Plotnikov}
398