public static void getMatchingRecipientsFromExtensionMatcher(
     BaseRecipientAdapter adapter, Set<String> matchesNotFound, RecipientMatchCallback callback) {
   // If no matches found in contact provider or the directories, try the extension
   // matcher.
   // todo (aalbert): This whole method needs to be in the adapter?
   if (adapter != null) {
     final Map<String, RecipientEntry> entries = adapter.getMatchingRecipients(matchesNotFound);
     if (entries != null && entries.size() > 0) {
       callback.matchesFound(entries);
       for (final String address : entries.keySet()) {
         matchesNotFound.remove(address);
       }
     }
   }
   callback.matchesNotFound(matchesNotFound);
 }
  /**
   * 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);
  }
  private static void getMatchingRecipientsFromDirectoryQueries(
      Context context,
      Map<String, RecipientEntry> recipientEntries,
      Set<String> addresses,
      Account account,
      Set<String> matchesNotFound,
      Queries.Query query,
      RecipientMatchCallback callback) {
    // See if any entries did not resolve; if so, we need to check other
    // directories

    if (recipientEntries.size() < addresses.size()) {
      final List<DirectorySearchParams> paramsList;
      Cursor directoryCursor = null;
      try {
        directoryCursor =
            context
                .getContentResolver()
                .query(DirectoryListQuery.URI, DirectoryListQuery.PROJECTION, null, null, null);
        if (directoryCursor == null) {
          paramsList = null;
        } else {
          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);

      if (paramsList != null) {
        Cursor directoryContactsCursor = null;
        for (String unresolvedAddress : unresolvedAddresses) {
          Long directoryId = null;
          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 {
                directoryId = paramsList.get(i).directoryId;
                break;
              }
            }
          }
          if (directoryContactsCursor != null) {
            try {
              final Map<String, RecipientEntry> entries =
                  processContactEntries(directoryContactsCursor, directoryId);

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

              callback.matchesFound(entries);
            } finally {
              directoryContactsCursor.close();
            }
          }
        }
      }
    }
  }
  /**
   * 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);
  }