private Cursor doQuery(CharSequence constraint, int limit, Long directoryId) {
   final Uri.Builder builder =
       mQuery
           .getContentFilterUri()
           .buildUpon()
           .appendPath(constraint.toString())
           .appendQueryParameter(
               ContactsContract.LIMIT_PARAM_KEY, String.valueOf(limit + ALLOWANCE_FOR_DUPLICATES));
   if (directoryId != null) {
     builder.appendQueryParameter(
         ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId));
   }
   if (mAccount != null) {
     builder.appendQueryParameter(PRIMARY_ACCOUNT_NAME, mAccount.name);
     builder.appendQueryParameter(PRIMARY_ACCOUNT_TYPE, mAccount.type);
   }
   final long start = System.currentTimeMillis();
   final Cursor cursor =
       mContentResolver.query(builder.build(), mQuery.getProjection(), null, null, null);
   final long end = System.currentTimeMillis();
   if (DEBUG) {
     Log.d(
         TAG,
         "Time for autocomplete (query: "
             + constraint
             + ", directoryId: "
             + directoryId
             + ", num_of_results: "
             + (cursor != null ? cursor.getCount() : "null")
             + "): "
             + (end - start)
             + " ms");
   }
   return cursor;
 }
  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
    final RecipientEntry entry = getEntries().get(position);
    String displayName = entry.getDisplayName();
    String destination = entry.getDestination();
    if (TextUtils.isEmpty(displayName) || TextUtils.equals(displayName, destination)) {
      displayName = destination;

      // We only show the destination for secondary entries, so clear it
      // only for the first level.
      if (entry.isFirstLevel()) {
        destination = null;
      }
    }

    final View itemView =
        convertView != null ? convertView : mInflater.inflate(getItemLayout(), parent, false);
    final TextView displayNameView = (TextView) itemView.findViewById(getDisplayNameId());
    final TextView destinationView = (TextView) itemView.findViewById(getDestinationId());
    final TextView destinationTypeView = (TextView) itemView.findViewById(getDestinationTypeId());
    final ImageView imageView = (ImageView) itemView.findViewById(getPhotoId());
    displayNameView.setText(displayName);
    if (!TextUtils.isEmpty(destination)) {
      destinationView.setText(destination);
    } else {
      destinationView.setText(null);
    }
    if (destinationTypeView != null) {
      final CharSequence destinationType =
          mQuery
              .getTypeLabel(
                  mContext.getResources(), entry.getDestinationType(), entry.getDestinationLabel())
              .toString()
              .toUpperCase();

      destinationTypeView.setText(destinationType);
    }

    if (entry.isFirstLevel()) {
      displayNameView.setVisibility(View.VISIBLE);
      if (imageView != null) {
        imageView.setVisibility(View.VISIBLE);
        final byte[] photoBytes = entry.getPhotoBytes();
        if (photoBytes != null) {
          final Bitmap photo = BitmapFactory.decodeByteArray(photoBytes, 0, photoBytes.length);
          imageView.setImageBitmap(photo);
        } else {
          imageView.setImageResource(getDefaultPhotoResource());
        }
      }
    } else {
      displayNameView.setVisibility(View.GONE);
      if (imageView != null) {
        imageView.setVisibility(View.INVISIBLE);
      }
    }
    return itemView;
  }
  /**
   * Get a HashMap of address to RecipientEntry that contains all contact information for a contact
   * with the provided address, if one exists. This may block the UI, so run it in an async task.
   *
   * @param context Context.
   * @param inAddresses Array of addresses on which to perform the lookup.
   * @param callback RecipientMatchCallback called when a match or matches are found.
   */
  public static void getMatchingRecipients(
      Context context,
      BaseRecipientAdapter adapter,
      ArrayList<String> inAddresses,
      int addressType,
      Account account,
      RecipientMatchCallback callback) {
    Queries.Query query;
    if (addressType == QUERY_TYPE_EMAIL) {
      query = Queries.EMAIL;
    } else {
      query = Queries.PHONE;
    }
    int addressesSize = Math.min(MAX_LOOKUPS, inAddresses.size());
    HashSet<String> addresses = new HashSet<String>();
    StringBuilder bindString = new StringBuilder();
    // Create the "?" string and set up arguments.
    for (int i = 0; i < addressesSize; i++) {
      Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(inAddresses.get(i).toLowerCase());
      addresses.add(tokens.length > 0 ? tokens[0].getAddress() : inAddresses.get(i));
      bindString.append("?");
      if (i < addressesSize - 1) {
        bindString.append(",");
      }
    }

    if (Log.isLoggable(TAG, Log.DEBUG)) {
      Log.d(TAG, "Doing reverse lookup for " + addresses.toString());
    }

    String[] addressArray = new String[addresses.size()];
    addresses.toArray(addressArray);
    HashMap<String, RecipientEntry> recipientEntries = null;
    Cursor c = null;

    try {
      c =
          context
              .getContentResolver()
              .query(
                  query.getContentUri(),
                  query.getProjection(),
                  query.getProjection()[Queries.Query.DESTINATION]
                      + " IN ("
                      + bindString.toString()
                      + ")",
                  addressArray,
                  null);
      recipientEntries = processContactEntries(c, null /* directoryId */);
      callback.matchesFound(recipientEntries);
    } finally {
      if (c != null) {
        c.close();
      }
    }
    //
    final Set<String> matchesNotFound = new HashSet<String>();

    getMatchingRecipientsFromDirectoryQueries(
        context, recipientEntries, addresses, account, matchesNotFound, query, callback);

    getMatchingRecipientsFromExtensionMatcher(adapter, matchesNotFound, callback);
  }
  /**
   * Get a HashMap of address to RecipientEntry that contains all contact information for a contact
   * with the provided address, if one exists. This may block the UI, so run it in an async task.
   *
   * @param context Context.
   * @param inAddresses Array of addresses on which to perform the lookup.
   * @return HashMap<String,RecipientEntry>
   */
  public static HashMap<String, RecipientEntry> getMatchingRecipients(
      Context context, ArrayList<String> inAddresses, int addressType) {
    Queries.Query query;
    if (addressType == QUERY_TYPE_EMAIL) {
      query = Queries.EMAIL;
    } else {
      query = Queries.PHONE;
    }
    int addressesSize = Math.min(MAX_LOOKUPS, inAddresses.size());
    String[] addresses = new String[addressesSize];
    StringBuilder bindString = new StringBuilder();
    // Create the "?" string and set up arguments.
    for (int i = 0; i < addressesSize; i++) {
      Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(inAddresses.get(i).toLowerCase());
      addresses[i] = (tokens.length > 0 ? tokens[0].getAddress() : inAddresses.get(i));
      bindString.append("?");
      if (i < addressesSize - 1) {
        bindString.append(",");
      }
    }

    if (Log.isLoggable(TAG, Log.DEBUG)) {
      Log.d(TAG, "Doing reverse lookup for " + addresses.toString());
    }

    HashMap<String, RecipientEntry> recipientEntries = new HashMap<String, RecipientEntry>();
    Cursor c =
        context
            .getContentResolver()
            .query(
                query.getContentUri(),
                query.getProjection(),
                query.getProjection()[Queries.Query.DESTINATION]
                    + " IN ("
                    + bindString.toString()
                    + ")",
                addresses,
                null);

    if (c != null) {
      try {
        if (c.moveToFirst()) {
          do {
            String address = c.getString(Queries.Query.DESTINATION);
            recipientEntries.put(
                address,
                RecipientEntry.constructTopLevelEntry(
                    c.getString(Queries.Query.NAME),
                    c.getInt(Queries.Query.DISPLAY_NAME_SOURCE),
                    c.getString(Queries.Query.DESTINATION),
                    c.getInt(Queries.Query.DESTINATION_TYPE),
                    c.getString(Queries.Query.DESTINATION_LABEL),
                    c.getLong(Queries.Query.CONTACT_ID),
                    c.getLong(Queries.Query.DATA_ID),
                    c.getString(Queries.Query.PHOTO_THUMBNAIL_URI)));
            if (Log.isLoggable(TAG, Log.DEBUG)) {
              Log.d(
                  TAG,
                  "Received reverse look up information for "
                      + address
                      + " RESULTS: "
                      + " NAME : "
                      + c.getString(Queries.Query.NAME)
                      + " CONTACT ID : "
                      + c.getLong(Queries.Query.CONTACT_ID)
                      + " ADDRESS :"
                      + c.getString(Queries.Query.DESTINATION));
            }
          } while (c.moveToNext());
        }
      } finally {
        c.close();
      }
    }
    return recipientEntries;
  }
  /**
   * Get a HashMap of address to RecipientEntry that contains all contact information for a contact
   * with the provided address, if one exists. This may block the UI, so run it in an async task.
   *
   * @param context Context.
   * @param inAddresses Array of addresses on which to perform the lookup.
   * @param callback RecipientMatchCallback called when a match or matches are found.
   * @return HashMap<String,RecipientEntry>
   */
  public static void getMatchingRecipients(
      Context context,
      ArrayList<String> inAddresses,
      int addressType,
      Account account,
      RecipientMatchCallback callback) {
    Queries.Query query;
    if (addressType == QUERY_TYPE_EMAIL) {
      query = Queries.EMAIL;
    } else {
      query = Queries.PHONE;
    }
    int addressesSize = Math.min(MAX_LOOKUPS, inAddresses.size());
    HashSet<String> addresses = new HashSet<String>();
    StringBuilder bindString = new StringBuilder();
    // Create the "?" string and set up arguments.
    for (int i = 0; i < addressesSize; i++) {
      Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(inAddresses.get(i).toLowerCase());
      addresses.add(tokens.length > 0 ? tokens[0].getAddress() : inAddresses.get(i));
      bindString.append("?");
      if (i < addressesSize - 1) {
        bindString.append(",");
      }
    }

    if (Log.isLoggable(TAG, Log.DEBUG)) {
      Log.d(TAG, "Doing reverse lookup for " + addresses.toString());
    }

    String[] addressArray = new String[addresses.size()];
    addresses.toArray(addressArray);
    HashMap<String, RecipientEntry> recipientEntries = null;
    Cursor c = null;

    try {
      c =
          context
              .getContentResolver()
              .query(
                  query.getContentUri(),
                  query.getProjection(),
                  query.getProjection()[Queries.Query.DESTINATION]
                      + " IN ("
                      + bindString.toString()
                      + ")",
                  addressArray,
                  null);
      recipientEntries = processContactEntries(c);
      callback.matchesFound(recipientEntries);
    } finally {
      if (c != null) {
        c.close();
      }
    }
    // See if any entries did not resolve; if so, we need to check other
    // directories
    final Set<String> matchesNotFound = new HashSet<String>();
    if (recipientEntries.size() < addresses.size()) {
      final List<DirectorySearchParams> paramsList;
      Cursor directoryCursor = null;
      try {
        directoryCursor =
            context
                .getContentResolver()
                .query(DirectoryListQuery.URI, DirectoryListQuery.PROJECTION, null, null, null);
        paramsList = BaseRecipientAdapter.setupOtherDirectories(context, directoryCursor, account);
      } finally {
        if (directoryCursor != null) {
          directoryCursor.close();
        }
      }
      // Run a directory query for each unmatched recipient.
      HashSet<String> unresolvedAddresses = new HashSet<String>();
      for (String address : addresses) {
        if (!recipientEntries.containsKey(address)) {
          unresolvedAddresses.add(address);
        }
      }

      matchesNotFound.addAll(unresolvedAddresses);

      Cursor directoryContactsCursor = null;
      for (String unresolvedAddress : unresolvedAddresses) {
        for (int i = 0; i < paramsList.size(); i++) {
          try {
            directoryContactsCursor =
                doQuery(
                    unresolvedAddress,
                    1,
                    paramsList.get(i).directoryId,
                    account,
                    context.getContentResolver(),
                    query);
          } finally {
            if (directoryContactsCursor != null && directoryContactsCursor.getCount() == 0) {
              directoryContactsCursor.close();
              directoryContactsCursor = null;
            } else {
              break;
            }
          }
        }
        if (directoryContactsCursor != null) {
          try {
            final Map<String, RecipientEntry> entries =
                processContactEntries(directoryContactsCursor);

            for (final String address : entries.keySet()) {
              matchesNotFound.remove(address);
            }

            callback.matchesFound(entries);
          } finally {
            directoryContactsCursor.close();
          }
        }
      }
    }

    callback.matchesNotFound(matchesNotFound);
  }